@@ -17,8 +17,16 @@ import (
17
17
v1 "k8s.io/api/core/v1"
18
18
)
19
19
20
- // ProviderName identifies this module as aws
21
- const ProviderName = "aws"
20
+ const (
21
+ // ProviderName identifies this module as aws
22
+ ProviderName = "aws"
23
+ // LifecycleOnDemand string constant for On-Demand EC2 instances
24
+ LifecycleOnDemand = "on-demand"
25
+ // LifecycleSpot string constant for Spot EC2 instances
26
+ LifecycleSpot = "spot"
27
+ // The AttachInstances API only supports adding 20 instances at a time
28
+ batchSize = 20
29
+ )
22
30
23
31
func instanceToProviderID (instance * autoscaling.Instance ) string {
24
32
return fmt .Sprintf ("aws:///%s/%s" , * instance .AvailabilityZone , * instance .InstanceId )
@@ -236,7 +244,7 @@ func (n *NodeGroup) IncreaseSize(delta int64) error {
236
244
return n .setASGDesiredSizeOneShot (delta )
237
245
}
238
246
239
- log .WithField ("asg" , n .id ).Infof ("Scaling with SetDesiredCapacity trategy " )
247
+ log .WithField ("asg" , n .id ).Infof ("Scaling with SetDesiredCapacity strategy " )
240
248
return n .setASGDesiredSize (n .TargetSize () + delta )
241
249
242
250
}
@@ -343,36 +351,27 @@ func (n *NodeGroup) setASGDesiredSize(newSize int64) error {
343
351
// setASGDesiredSizeOneShot uses the AWS fleet API to acquire all desired
344
352
// capacity in one step and then add it to the existing auto-scaling group.
345
353
func (n * NodeGroup ) setASGDesiredSizeOneShot (addCount int64 ) error {
346
- fleet , err := n .provider .ec2Service .CreateFleet (& ec2.CreateFleetInput {
347
- Type : awsapi .String ("instant" ),
348
- TerminateInstancesWithExpiration : awsapi .Bool (false ),
349
- OnDemandOptions : & ec2.OnDemandOptionsRequest {
350
- MinTargetCapacity : awsapi .Int64 (addCount ),
351
- SingleInstanceType : awsapi .Bool (true ),
352
- },
353
- TargetCapacitySpecification : & ec2.TargetCapacitySpecificationRequest {
354
- OnDemandTargetCapacity : awsapi .Int64 (addCount ),
355
- TotalTargetCapacity : awsapi .Int64 (addCount ),
356
- DefaultTargetCapacityType : awsapi .String ("on-demand" ),
357
- },
358
- LaunchTemplateConfigs : []* ec2.FleetLaunchTemplateConfigRequest {
359
- {
360
- LaunchTemplateSpecification : & ec2.FleetLaunchTemplateSpecificationRequest {
361
- LaunchTemplateId : awsapi .String (n .config .AWSConfig .LaunchTemplateID ),
362
- Version : awsapi .String (n .config .AWSConfig .LaunchTemplateVersion ),
363
- },
364
- },
365
- },
366
- })
354
+ // Parse the Escalator args into the correct format for a CreateFleet request, then make the request.
355
+ fleetInput , err := createFleetInput (* n , addCount )
367
356
if err != nil {
357
+ log .Error ("Failed setup for CreateFleet call." )
368
358
return err
369
359
}
370
360
371
- // This will hold any launch errors for the fleet. In the case of an
372
- // instant fleet with a single instant type this will indicate that the
373
- // entire fleet failed to launch.
374
- for _ , lerr := range fleet .Errors {
375
- return errors .New (* lerr .ErrorMessage )
361
+ fleet , err := n .provider .ec2Service .CreateFleet (fleetInput )
362
+ if err != nil {
363
+ log .Errorf ("Failed CreateFleet call. CreateFleetInput: %v" , fleetInput )
364
+ return err
365
+ }
366
+
367
+ // CreateFleet returns an array of errors with the response. Sometimes errors are present even when instances were
368
+ // successfully provisioned. In this case, the min target capacity is the size of the full request, so if any
369
+ // instances are present this indicates we got them all and can ignore the errors.
370
+ if len (fleet .Instances ) == 0 && len (fleet .Errors ) > 0 {
371
+ for _ , err := range fleet .Errors {
372
+ log .Error (* err .ErrorMessage )
373
+ }
374
+ return errors .New (* fleet .Errors [0 ].ErrorMessage )
376
375
}
377
376
378
377
instances := make ([]* string , 0 )
@@ -402,8 +401,6 @@ InstanceReadyLoop:
402
401
}
403
402
}
404
403
405
- // The AttachInstances API only supports adding 20 instances at a time
406
- batchSize := 20
407
404
var batch []* string
408
405
for batchSize < len (instances ) {
409
406
instances , batch = instances [batchSize :], instances [0 :batchSize :batchSize ]
@@ -413,6 +410,7 @@ InstanceReadyLoop:
413
410
InstanceIds : batch ,
414
411
})
415
412
if err != nil {
413
+ log .Error ("Failed AttachInstances call." )
416
414
return err
417
415
}
418
416
}
@@ -426,7 +424,12 @@ InstanceReadyLoop:
426
424
427
425
log .WithField ("asg" , n .id ).Debugf ("CurrentSize: %v" , n .Size ())
428
426
log .WithField ("asg" , n .id ).Debugf ("CurrentTargetSize: %v" , n .TargetSize ())
429
- return err
427
+ if err != nil {
428
+ log .Error ("Failed AttachInstances call." )
429
+ return err
430
+ }
431
+
432
+ return nil
430
433
}
431
434
432
435
func (n * NodeGroup ) allInstancesReady (ids []* string ) bool {
@@ -453,3 +456,94 @@ func (n *NodeGroup) allInstancesReady(ids []*string) bool {
453
456
454
457
return ready
455
458
}
459
+
460
+ // createFleetInput will parse Escalator input into the format needed for a CreateFleet request.
461
+ func createFleetInput (n NodeGroup , addCount int64 ) (* ec2.CreateFleetInput , error ) {
462
+ lifecycle := n .config .AWSConfig .Lifecycle
463
+ if lifecycle == "" {
464
+ lifecycle = LifecycleOnDemand
465
+ }
466
+
467
+ launchTemplateOverrides , err := createTemplateOverrides (n )
468
+ if err != nil {
469
+ return nil , err
470
+ }
471
+
472
+ fleetInput := & ec2.CreateFleetInput {
473
+ Type : awsapi .String ("instant" ),
474
+ TerminateInstancesWithExpiration : awsapi .Bool (false ),
475
+ TargetCapacitySpecification : & ec2.TargetCapacitySpecificationRequest {
476
+ TotalTargetCapacity : awsapi .Int64 (addCount ),
477
+ DefaultTargetCapacityType : awsapi .String (lifecycle ),
478
+ },
479
+ LaunchTemplateConfigs : []* ec2.FleetLaunchTemplateConfigRequest {
480
+ {
481
+ LaunchTemplateSpecification : & ec2.FleetLaunchTemplateSpecificationRequest {
482
+ LaunchTemplateId : awsapi .String (n .config .AWSConfig .LaunchTemplateID ),
483
+ Version : awsapi .String (n .config .AWSConfig .LaunchTemplateVersion ),
484
+ },
485
+ Overrides : launchTemplateOverrides ,
486
+ },
487
+ },
488
+ }
489
+
490
+ if lifecycle == LifecycleOnDemand {
491
+ fleetInput .OnDemandOptions = & ec2.OnDemandOptionsRequest {
492
+ MinTargetCapacity : awsapi .Int64 (addCount ),
493
+ SingleInstanceType : awsapi .Bool (true ),
494
+ }
495
+ } else {
496
+ fleetInput .SpotOptions = & ec2.SpotOptionsRequest {
497
+ MinTargetCapacity : awsapi .Int64 (addCount ),
498
+ SingleInstanceType : awsapi .Bool (true ),
499
+ }
500
+ }
501
+
502
+ return fleetInput , nil
503
+ }
504
+
505
+ // createTemplateOverrides will parse the overrides into the FleetLaunchTemplateOverridesRequest format
506
+ func createTemplateOverrides (n NodeGroup ) ([]* ec2.FleetLaunchTemplateOverridesRequest , error ) {
507
+ // Get subnetIDs from the ASG
508
+ describeASGOutput , err := n .provider .service .DescribeAutoScalingGroups (& autoscaling.DescribeAutoScalingGroupsInput {
509
+ AutoScalingGroupNames : []* string {
510
+ awsapi .String (n .id ),
511
+ },
512
+ })
513
+ if err != nil {
514
+ log .Errorf ("Failed call to DescribeAutoScalingGroups for ASG %v" , n .id )
515
+ return nil , err
516
+ }
517
+ if len (describeASGOutput .AutoScalingGroups ) == 0 {
518
+ return nil , errors .New ("failed to get an ASG from DescribeAutoscalingGroups response" )
519
+ }
520
+ if * describeASGOutput .AutoScalingGroups [0 ].VPCZoneIdentifier == "" {
521
+ return nil , errors .New ("failed to get any subnetIDs from DescribeAutoscalingGroups response" )
522
+ }
523
+ vpcZoneIdentifier := describeASGOutput .AutoScalingGroups [0 ].VPCZoneIdentifier
524
+ subnetIDs := strings .Split (* vpcZoneIdentifier , "," )
525
+
526
+ instanceTypes := n .config .AWSConfig .InstanceTypeOverrides
527
+
528
+ var launchTemplateOverrides []* ec2.FleetLaunchTemplateOverridesRequest
529
+ if len (instanceTypes ) > 0 {
530
+ for i := range subnetIDs {
531
+ for j := range instanceTypes {
532
+ overridesRequest := ec2.FleetLaunchTemplateOverridesRequest {
533
+ SubnetId : & subnetIDs [i ],
534
+ InstanceType : & instanceTypes [j ],
535
+ }
536
+ launchTemplateOverrides = append (launchTemplateOverrides , & overridesRequest )
537
+ }
538
+ }
539
+ } else {
540
+ for i := range subnetIDs {
541
+ overridesRequest := ec2.FleetLaunchTemplateOverridesRequest {
542
+ SubnetId : & subnetIDs [i ],
543
+ }
544
+ launchTemplateOverrides = append (launchTemplateOverrides , & overridesRequest )
545
+ }
546
+ }
547
+
548
+ return launchTemplateOverrides , nil
549
+ }
0 commit comments