Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding capacity API client #202

Merged
merged 2 commits into from
Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions pkg/cloudprovider/aws/capacity_provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package aws

import (
"context"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/awslabs/karpenter/pkg/cloudprovider"
)

type CapacityProvisioner struct {
ec2Iface ec2iface.EC2API
}

// NewCapacityProvisioner lets user provision nodes in AWS
func NewCapacityProvisioner(client ec2iface.EC2API) *CapacityProvisioner {
return &CapacityProvisioner{ec2Iface: client}
}

// Provision accepts desired capacity and contraints for provisioning
func (cp *CapacityProvisioner) Provision(context.Context, *cloudprovider.CapacityConstraints) error {
// Convert contraints to the Node types and select the launch template
// TODO

// Create the desired number of instances based on desired capacity
config := defaultInstanceConfig("", "", cp.ec2Iface)
_ = config

// Set AvailabilityZone, subnet, capacity, on-demand or spot
// and validateAndCreate instances
return nil
}

type instanceConfig struct {
ec2Iface ec2iface.EC2API
templateConfig *ec2.FleetLaunchTemplateConfigRequest
capacitySpec *ec2.TargetCapacitySpecificationRequest
instanceID string
}

func defaultInstanceConfig(templateID, templateVersion string, client ec2iface.EC2API) *instanceConfig {
return &instanceConfig{
ec2Iface: client,
templateConfig: &ec2.FleetLaunchTemplateConfigRequest{
LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{
LaunchTemplateId: aws.String(templateID),
Version: aws.String(templateVersion),
},
Overrides: []*ec2.FleetLaunchTemplateOverridesRequest{
&ec2.FleetLaunchTemplateOverridesRequest{},
},
},
capacitySpec: &ec2.TargetCapacitySpecificationRequest{
DefaultTargetCapacityType: aws.String(ec2.DefaultTargetCapacityTypeOnDemand),
},
}
}

func (cfg *instanceConfig) validateAndCreate(ctx context.Context) error {
input := &ec2.CreateFleetInput{
LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{cfg.templateConfig},
TargetCapacitySpecification: cfg.capacitySpec,
Type: aws.String(ec2.FleetTypeInstant),
}
if err := input.Validate(); err != nil {
return err
}
output, err := cfg.ec2Iface.CreateFleetWithContext(ctx, input)
if err != nil {
return err
}
// TODO Get instanceID from the output
_ = output
return nil
}
8 changes: 8 additions & 0 deletions pkg/cloudprovider/aws/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/autoscaling/autoscalingiface"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/aws/aws-sdk-go/service/eks/eksiface"
"github.com/aws/aws-sdk-go/service/sqs"
Expand All @@ -35,6 +37,7 @@ type Factory struct {
AutoscalingClient autoscalingiface.AutoScalingAPI
SQSClient sqsiface.SQSAPI
EKSClient eksiface.EKSAPI
EC2Client ec2iface.EC2API
Client client.Client
}

Expand All @@ -44,6 +47,7 @@ func NewFactory(options cloudprovider.Options) *Factory {
AutoscalingClient: autoscaling.New(sess),
EKSClient: eks.New(sess),
SQSClient: sqs.New(sess),
EC2Client: ec2.New(sess),
Client: options.Client,
}
}
Expand All @@ -68,6 +72,10 @@ func (f *Factory) QueueFor(spec *v1alpha1.QueueSpec) cloudprovider.Queue {
}
}

func (f *Factory) CapacityClient() cloudprovider.CapacityProvisioner {
return NewCapacityProvisioner(f.EC2Client)
}

func withRegion(sess *session.Session) *session.Session {
region, err := ec2metadata.New(sess).Region()
log.PanicIfError(err, "failed to call the metadata server's region API")
Expand Down
3 changes: 3 additions & 0 deletions pkg/cloudprovider/fake/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ func (f *Factory) NodeGroupFor(sng *v1alpha1.ScalableNodeGroupSpec) cloudprovide
func (f *Factory) QueueFor(spec *v1alpha1.QueueSpec) cloudprovider.Queue {
return &Queue{Id: spec.ID, WantErr: f.WantErr}
}
func (f *Factory) CapacityClient() cloudprovider.CapacityProvisioner {
return &Provisioner{}
}
28 changes: 28 additions & 0 deletions pkg/cloudprovider/fake/provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package fake

import (
"context"

"github.com/awslabs/karpenter/pkg/cloudprovider"
)

type Provisioner struct {
}

func (p *Provisioner) Provision(context.Context, *cloudprovider.CapacityConstraints) error {
return nil
}
35 changes: 35 additions & 0 deletions pkg/cloudprovider/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ limitations under the License.
package cloudprovider

import (
"context"

"github.com/awslabs/karpenter/pkg/apis/autoscaling/v1alpha1"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -25,6 +28,8 @@ type Factory interface {
NodeGroupFor(sng *v1alpha1.ScalableNodeGroupSpec) NodeGroup
// QueueFor returns a queue for the provided spec
QueueFor(queue *v1alpha1.QueueSpec) Queue
// CapacityClient returns a provisioner for the provider to create instances
CapacityClient() CapacityProvisioner
}

// Queue abstracts all provider specific behavior for Queues
Expand All @@ -49,7 +54,37 @@ type NodeGroup interface {
Stabilized() (bool, string, error)
}

// CapacityProvisioner helps provision a desired capacity
// with a set of constraints in the cloud provider,
// number of instances and resource capacity can be controlled by
// setting the capacityConstraints
type CapacityProvisioner interface {
// Provision will send the request to cloud provider to provision the desired capacity.
Provision(context.Context, *CapacityConstraints) error
}

// Options are injected into cloud providers' factories
type Options struct {
Client client.Client
}

// Architecture for the provisioned capacity
type Architecture string

const (
Linux386 Architecture = "linux/386"
LinuxAMD64 Architecture = "linux/amd64"
)

// CapacityConstraints lets the controller define the desired capacity,
// avalability zone, architecture for the desired nodes.
type CapacityConstraints struct {
// Zone constrains where a node can be created within a region
Zone *string
// Resources constrains the minimum capacity to provision (e.g. CPU, Memory)
Resources v1.ResourceList
// NodeOverhead constrains the per node overhead of system resources
NodeOverhead v1.ResourceList
// Architecture constrains the underlying hardware architecture.
Architecture *Architecture
}