@@ -66,8 +66,9 @@ type DefaultProvider struct {
6666
6767 instanceTypesInfo []* ecsclient.DescribeInstanceTypesResponseBodyInstanceTypesInstanceType
6868
69- muInstanceTypesOfferings sync.RWMutex
70- instanceTypesOfferings map [string ]sets.Set [string ]
69+ muInstanceTypesOfferings sync.RWMutex
70+ instanceTypesOfferings map [string ]sets.Set [string ]
71+ spotInstanceTypesOfferings map [string ]sets.Set [string ]
7172
7273 instanceTypesCache * cache.Cache
7374
@@ -83,17 +84,18 @@ func NewDefaultProvider(region string, kubeClient client.Client, ecsClient *ecsc
8384 instanceTypesCache * cache.Cache , unavailableOfferingsCache * kcache.UnavailableOfferings ,
8485 pricingProvider pricing.Provider , ackProvider ack.Provider ) * DefaultProvider {
8586 return & DefaultProvider {
86- kubeClient : kubeClient ,
87- ecsClient : ecsClient ,
88- region : region ,
89- pricingProvider : pricingProvider ,
90- ackProvider : ackProvider ,
91- instanceTypesInfo : []* ecsclient.DescribeInstanceTypesResponseBodyInstanceTypesInstanceType {},
92- instanceTypesOfferings : map [string ]sets.Set [string ]{},
93- instanceTypesCache : instanceTypesCache ,
94- unavailableOfferings : unavailableOfferingsCache ,
95- cm : pretty .NewChangeMonitor (),
96- instanceTypesSeqNum : 0 ,
87+ kubeClient : kubeClient ,
88+ ecsClient : ecsClient ,
89+ region : region ,
90+ pricingProvider : pricingProvider ,
91+ ackProvider : ackProvider ,
92+ instanceTypesInfo : []* ecsclient.DescribeInstanceTypesResponseBodyInstanceTypesInstanceType {},
93+ instanceTypesOfferings : map [string ]sets.Set [string ]{},
94+ spotInstanceTypesOfferings : map [string ]sets.Set [string ]{},
95+ instanceTypesCache : instanceTypesCache ,
96+ unavailableOfferings : unavailableOfferingsCache ,
97+ cm : pretty .NewChangeMonitor (),
98+ instanceTypesSeqNum : 0 ,
9799 }
98100}
99101
@@ -111,6 +113,9 @@ func (p *DefaultProvider) validateState(nodeClass *v1alpha1.ECSNodeClass) error
111113 if len (p .instanceTypesOfferings ) == 0 {
112114 return errors .New ("no instance types offerings found" )
113115 }
116+ if len (p .spotInstanceTypesOfferings ) == 0 {
117+ return errors .New ("no spot instance types offerings found" )
118+ }
114119 if len (nodeClass .Status .VSwitches ) == 0 {
115120 return errors .New ("no vswitches found" )
116121 }
@@ -182,13 +187,15 @@ func (p *DefaultProvider) List(ctx context.Context, kc *v1alpha1.KubeletConfigur
182187 zoneData := lo .Map (allZones .UnsortedList (), func (zoneID string , _ int ) ZoneData {
183188 if ! p .instanceTypesOfferings [lo .FromPtr (i .InstanceTypeId )].Has (zoneID ) || ! vSwitchsZones .Has (zoneID ) {
184189 return ZoneData {
185- ID : zoneID ,
186- Available : false ,
190+ ID : zoneID ,
191+ Available : false ,
192+ SpotAvailable : false ,
187193 }
188194 }
189195 return ZoneData {
190- ID : zoneID ,
191- Available : true ,
196+ ID : zoneID ,
197+ Available : true ,
198+ SpotAvailable : p .spotInstanceTypesOfferings [lo .FromPtr (i .InstanceTypeId )].Has (zoneID ),
192199 }
193200 })
194201
@@ -271,7 +278,42 @@ func (p *DefaultProvider) UpdateInstanceTypeOfferings(ctx context.Context) error
271278 log .FromContext (ctx ).Error (err , "failed to get instance type offerings" )
272279 return err
273280 }
281+ err = processAvailableResourcesResponse (resp , instanceTypesOfferings )
282+ if err != nil {
283+ log .FromContext (ctx ).Error (err , "failed to process available resource response" )
284+ return err
285+ }
286+
287+ if p .cm .HasChanged ("instance-type-offering" , instanceTypesOfferings ) {
288+ // Only update instanceTypesSeqNun with the instance type offerings have been changed
289+ // This is to not create new keys with duplicate instance type offerings option
290+ atomic .AddUint64 (& p .instanceTypesOfferingsSeqNum , 1 )
291+ log .FromContext (ctx ).WithValues ("instance-type-count" , len (instanceTypesOfferings )).V (1 ).Info ("discovered offerings for instance types" )
292+ }
293+ p .instanceTypesOfferings = instanceTypesOfferings
294+
295+ spotInstanceTypesOfferings := map [string ]sets.Set [string ]{}
296+ describeAvailableResourceRequest = & ecsclient.DescribeAvailableResourceRequest {
297+ RegionId : tea .String (p .region ),
298+ DestinationResource : tea .String ("InstanceType" ),
299+ SpotStrategy : tea .String ("SpotAsPriceGo" ),
300+ }
301+ resp , err = p .ecsClient .DescribeAvailableResourceWithOptions (
302+ describeAvailableResourceRequest , & util.RuntimeOptions {})
303+ if err != nil {
304+ log .FromContext (ctx ).Error (err , "failed to get spot instance type offerings" )
305+ return err
306+ }
307+ err = processAvailableResourcesResponse (resp , spotInstanceTypesOfferings )
308+ if err != nil {
309+ log .FromContext (ctx ).Error (err , "failed to process spot instance type offerings" )
310+ return err
311+ }
312+ p .spotInstanceTypesOfferings = spotInstanceTypesOfferings
313+ return nil
314+ }
274315
316+ func processAvailableResourcesResponse (resp * ecsclient.DescribeAvailableResourceResponse , offerings map [string ]sets.Set [string ]) error {
275317 if resp == nil || resp .Body == nil {
276318 return errors .New ("DescribeAvailableResourceWithOptions failed to return any instance types" )
277319 } else if resp .Body .AvailableZones == nil || len (resp .Body .AvailableZones .AvailableZone ) == 0 {
@@ -280,18 +322,11 @@ func (p *DefaultProvider) UpdateInstanceTypeOfferings(ctx context.Context) error
280322
281323 for _ , az := range resp .Body .AvailableZones .AvailableZone {
282324 // TODO: Later, `ClosedWithStock` will be tested to determine if `ClosedWithStock` should be added.
283- if * az .StatusCategory == "WithStock" { // WithStock, ClosedWithStock, WithoutStock, ClosedWithoutStock
284- processAvailableResources (az , instanceTypesOfferings )
325+ // WithStock, ClosedWithStock, WithoutStock, ClosedWithoutStock
326+ if * az .StatusCategory == "WithStock" {
327+ processAvailableResources (az , offerings )
285328 }
286329 }
287-
288- if p .cm .HasChanged ("instance-type-offering" , instanceTypesOfferings ) {
289- // Only update instanceTypesSeqNun with the instance type offerings have been changed
290- // This is to not create new keys with duplicate instance type offerings option
291- atomic .AddUint64 (& p .instanceTypesOfferingsSeqNum , 1 )
292- log .FromContext (ctx ).WithValues ("instance-type-count" , len (instanceTypesOfferings )).V (1 ).Info ("discovered offerings for instance types" )
293- }
294- p .instanceTypesOfferings = instanceTypesOfferings
295330 return nil
296331}
297332
@@ -365,6 +400,10 @@ func getAllInstanceTypes(client *ecsclient.Client) ([]*ecsclient.DescribeInstanc
365400func (p * DefaultProvider ) createOfferings (_ context.Context , instanceType string , zones []ZoneData ) []cloudprovider.Offering {
366401 var offerings []cloudprovider.Offering
367402 for _ , zone := range zones {
403+ if ! zone .Available {
404+ continue
405+ }
406+
368407 odPrice , odOK := p .pricingProvider .OnDemandPrice (instanceType )
369408 spotPrice , spotOK := p .pricingProvider .SpotPrice (instanceType , zone .ID )
370409
@@ -375,7 +414,7 @@ func (p *DefaultProvider) createOfferings(_ context.Context, instanceType string
375414 offerings = append (offerings , p .createOffering (zone .ID , karpv1 .CapacityTypeOnDemand , odPrice , offeringAvailable ))
376415 }
377416
378- if spotOK {
417+ if spotOK && zone . SpotAvailable {
379418 isUnavailable := p .unavailableOfferings .IsUnavailable (instanceType , zone .ID , karpv1 .CapacityTypeSpot )
380419 offeringAvailable := ! isUnavailable && zone .Available
381420
0 commit comments