@@ -18,6 +18,7 @@ package cloudprovider
1818
1919import (
2020 "context"
21+ stderrors "errors"
2122 "fmt"
2223 "net/http"
2324 "time"
@@ -36,6 +37,7 @@ import (
3637 karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1"
3738 "sigs.k8s.io/karpenter/pkg/cloudprovider"
3839 "sigs.k8s.io/karpenter/pkg/events"
40+ "sigs.k8s.io/karpenter/pkg/scheduling"
3941 "sigs.k8s.io/karpenter/pkg/utils/resources"
4042
4143 "github.com/cloudpilot-ai/karpenter-provider-alicloud/config"
@@ -65,8 +67,42 @@ func New(kubeClient client.Client, recorder events.Recorder) *CloudProvider {
6567
6668// Create a NodeClaim given the constraints.
6769func (c * CloudProvider ) Create (ctx context.Context , nodeClaim * karpv1.NodeClaim ) (* karpv1.NodeClaim , error ) {
68- // TODO: Implement this
69- return nil , nil
70+ nodeClass , err := c .resolveNodeClassFromNodeClaim (ctx , nodeClaim )
71+ if err != nil {
72+ if errors .IsNotFound (err ) {
73+ c .recorder .Publish (cloudproviderevents .NodeClaimFailedToResolveNodeClass (nodeClaim ))
74+ }
75+ // We treat a failure to resolve the NodeClass as an ICE since this means there is no capacity possibilities for this NodeClaim
76+ return nil , cloudprovider .NewInsufficientCapacityError (fmt .Errorf ("resolving node class, %w" , err ))
77+ }
78+
79+ nodeClassReady := nodeClass .StatusConditions ().Get (status .ConditionReady )
80+ if nodeClassReady .IsFalse () {
81+ return nil , cloudprovider .NewNodeClassNotReadyError (stderrors .New (nodeClassReady .Message ))
82+ }
83+ if nodeClassReady .IsUnknown () {
84+ return nil , fmt .Errorf ("resolving NodeClass readiness, NodeClass is in Ready=Unknown, %s" , nodeClassReady .Message )
85+ }
86+ instanceTypes , err := c .resolveInstanceTypes (ctx , nodeClaim , nodeClass )
87+ if err != nil {
88+ return nil , fmt .Errorf ("resolving instance types, %w" , err )
89+ }
90+ if len (instanceTypes ) == 0 {
91+ return nil , cloudprovider .NewInsufficientCapacityError (fmt .Errorf ("all requested instance types were unavailable during launch" ))
92+ }
93+ instance , err := c .instanceProvider .Create (ctx , nodeClass , nodeClaim , instanceTypes )
94+ if err != nil {
95+ return nil , fmt .Errorf ("creating instance, %w" , err )
96+ }
97+ instanceType , _ := lo .Find (instanceTypes , func (i * cloudprovider.InstanceType ) bool {
98+ return i .Name == instance .Type
99+ })
100+ nc := c .instanceToNodeClaim (instance , instanceType , nodeClass )
101+ nc .Annotations = lo .Assign (nc .Annotations , map [string ]string {
102+ v1alpha1 .AnnotationECSNodeClassHash : nodeClass .Hash (),
103+ v1alpha1 .AnnotationECSNodeClassHashVersion : v1alpha1 .ECSNodeClassHashVersion ,
104+ })
105+ return nc , nil
70106}
71107
72108func (c * CloudProvider ) List (ctx context.Context ) ([]* karpv1.NodeClaim , error ) {
@@ -282,3 +318,30 @@ func (c *CloudProvider) instanceToNodeClaim(i *instance.Instance, instanceType *
282318 nodeClaim .Status .ImageID = i .ImageID
283319 return nodeClaim
284320}
321+
322+ func (c * CloudProvider ) resolveNodeClassFromNodeClaim (ctx context.Context , nodeClaim * karpv1.NodeClaim ) (* v1alpha1.ECSNodeClass , error ) {
323+ nodeClass := & v1alpha1.ECSNodeClass {}
324+ if err := c .kubeClient .Get (ctx , types.NamespacedName {Name : nodeClaim .Spec .NodeClassRef .Name }, nodeClass ); err != nil {
325+ return nil , err
326+ }
327+ // For the purposes of NodeClass CloudProvider resolution, we treat deleting NodeClasses as NotFound
328+ if ! nodeClass .DeletionTimestamp .IsZero () {
329+ // For the purposes of NodeClass CloudProvider resolution, we treat deleting NodeClasses as NotFound,
330+ // but we return a different error message to be clearer to users
331+ return nil , newTerminatingNodeClassError (nodeClass .Name )
332+ }
333+ return nodeClass , nil
334+ }
335+
336+ func (c * CloudProvider ) resolveInstanceTypes (ctx context.Context , nodeClaim * karpv1.NodeClaim , nodeClass * v1alpha1.ECSNodeClass ) ([]* cloudprovider.InstanceType , error ) {
337+ instanceTypes , err := c .instanceTypeProvider .List (ctx , nodeClass .Spec .KubeletConfiguration , nodeClass )
338+ if err != nil {
339+ return nil , fmt .Errorf ("getting instance types, %w" , err )
340+ }
341+ reqs := scheduling .NewNodeSelectorRequirementsWithMinValues (nodeClaim .Spec .Requirements ... )
342+ return lo .Filter (instanceTypes , func (i * cloudprovider.InstanceType , _ int ) bool {
343+ return reqs .Compatible (i .Requirements , scheduling .AllowUndefinedWellKnownLabels ) == nil &&
344+ len (i .Offerings .Compatible (reqs ).Available ()) > 0 &&
345+ resources .Fits (nodeClaim .Spec .Resources .Requests , i .Allocatable ())
346+ }), nil
347+ }
0 commit comments