From 7d5780f181bf40b344046bf203a037bb48efbeb4 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:04:20 +0800 Subject: [PATCH 01/11] convert list-clusters and upgrade-cluster to use launch templates but still need to update the cleanup code to use templates --- README.md | 8 ++-- upgrader.go | 120 ++++++++++++++++++++++++++-------------------------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index b18ac61..ba064ff 100755 --- a/README.md +++ b/README.md @@ -44,11 +44,11 @@ available or not. When `--force-replacement` is enabled the process is _not_ ide ## Instance Replacement Process 1. Look up latest AMI based on either the given AMI filter, or the default: `amzn2-ami-ecs-hvm-*-x86_64-ebs` - 2. Identify the ASG for the given ECS cluster to get current launch configuration and instances list - 3. Compare latest AMI with AMI in use by launch configuration + 2. Identify the ASG for the given ECS cluster to get current launch template and instances list + 3. Compare latest AMI with AMI in use by launch template 1. If cluster is not using latest AMI, or `force replacement` is enabled, proceed to #4 2. Else if using latest AMI already, jump to #10 - 4. Create new launch configuration with new AMI + 4. Create new launch template version with new AMI 5. Update ASG to use new launch config 6. Detach existing instances from ASG and replace with new ones 7. Wait for new instances to reach `InService` state with ASG @@ -71,7 +71,7 @@ available or not. When `--force-replacement` is enabled the process is _not_ ide 1. Grab the latest binary for your platform at https://github.com/silinternational/ecs-ami-deploy/releases 2. The CLI makes use of AWS's SDK for Go, which can load authentication credentials from various places similar to the AWS CLI itself -3. Run `ecs-ami-deploy list-cluster` to check if it's working and what clusters you have available. +3. Run `ecs-ami-deploy list-clusters` to check if it's working and what clusters you have available. 4. If you have multiple profiles configured in your `~/.aws/credentials` file, you can use the `-p` or `--profile` flags to specify a different profile. 5. The CLI defaults to region `us-east-1`, you can use the `-r` or `-region` flags to specify else diff --git a/upgrader.go b/upgrader.go index 87e8629..e4cab00 100755 --- a/upgrader.go +++ b/upgrader.go @@ -172,11 +172,12 @@ func (u *Upgrader) ListClusters() ([]ClusterMeta, error) { continue } - lc, err := u.getLaunchConfigurationForASG(asg) + _, ltd, err := u.getLaunchTemplateForASG(asg) if err != nil { - return []ClusterMeta{}, fmt.Errorf("error getting launch configuration for cluster %s: %s", *c.ClusterName, err) + fmt.Printf("Error getting launch template for cluster %s\n%s\n\n", *c.ClusterName, err) + continue } - img, err := u.getImageByID(*lc.ImageId) + img, err := u.getImageByID(*ltd.ImageId) if err != nil { return []ClusterMeta{}, fmt.Errorf("error getting image details for cluster %s: %s", *c.ClusterName, err) } @@ -209,12 +210,13 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Found ASG: %s\n", asgName) - lc, err := u.getLaunchConfigurationForASG(asgName) + lt, ltd, err := u.getLaunchTemplateForASG(asgName) if err != nil { return err } - u.logger.Printf("Current launch configuration: %s\n", *lc.LaunchConfigurationName) - u.logger.Printf("Current image ID: %s\n", *lc.ImageId) + u.logger.Printf("Launch template: %s\n", *lt.LaunchTemplateName) + u.logger.Printf("Latest version: %s\n", *lt.LatestVersionNumber) + u.logger.Printf("Current image ID: %s\n", *ltd.ImageId) latest, err := u.LatestAMI() if err != nil { @@ -222,7 +224,7 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Latest image found: %s\n", *latest.ImageId) - current, err := u.getImageByID(*lc.ImageId) + current, err := u.getImageByID(*ltd.ImageId) if err != nil { return err } @@ -262,16 +264,16 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Existing instances in ASG: %s\n", strings.Join(originalInstanceIDs, ", ")) - newLc, err := u.cloneLaunchConfigurationWithNewImage(lc, latest) + newLtv, err := u.newLaunchTemplateVersionWithNewImage(lt, latest) if err != nil { return err } - u.logger.Printf("New launch configuration created: %s\n", newLc) + u.logger.Printf("New launch template version created: %d\n", *newLtv.VersionNumber) - if err := u.updateAsgLaunchConfiguration(asgName, newLc); err != nil { + if err := u.updateAsgLaunchTemplate(asgName, newLtv); err != nil { return err } - u.logger.Println("ASG updated to use new launch configuration") + u.logger.Println("ASG updated to use new launch template version") // detach and replace instances if err := u.detachAndReplaceAsgInstances(asgName); err != nil { @@ -392,7 +394,7 @@ func (u *Upgrader) getInstanceListForCluster(cluster string) ([]ecsTypes.Contain return descResult.ContainerInstances, nil } -func (u *Upgrader) getLaunchConfigurationForASG(asgName string) (*asgTypes.LaunchConfiguration, error) { +func (u *Upgrader) getLaunchTemplateForASG(asgName string) (*ec2types.LaunchTemplate, *ec2types.ResponseLaunchTemplateData, error) { input := &autoscaling.DescribeAutoScalingGroupsInput{ AutoScalingGroupNames: []string{ asgName, @@ -401,7 +403,7 @@ func (u *Upgrader) getLaunchConfigurationForASG(asgName string) (*asgTypes.Launc result, err := u.asgClient.DescribeAutoScalingGroups(context.Background(), input) if err != nil { - return nil, fmt.Errorf("failed to describe auto-scaling groups: %s", err) + return nil, nil, fmt.Errorf("failed to describe auto-scaling groups: %s", err) } var group asgTypes.AutoScalingGroup @@ -415,38 +417,47 @@ func (u *Upgrader) getLaunchConfigurationForASG(asgName string) (*asgTypes.Launc } // if no matching group was found, return err - if group.LaunchConfigurationName == nil { - return nil, fmt.Errorf("ASG with name %s not found", asgName) + if group.LaunchTemplate == nil { + return nil, nil, fmt.Errorf("ASG %s has no launch template", asgName) } - lcInput := &autoscaling.DescribeLaunchConfigurationsInput{ - LaunchConfigurationNames: []string{ - *group.LaunchConfigurationName, + ltInput := &ec2.DescribeLaunchTemplatesInput{ + LaunchTemplateNames: []string{ + *group.LaunchTemplate.LaunchTemplateName, }, } - lcResult, err := u.asgClient.DescribeLaunchConfigurations(context.Background(), lcInput) + ltResult, err := u.ec2Client.DescribeLaunchTemplates(context.Background(), ltInput) if err != nil { - return nil, fmt.Errorf("failed to describe launch configurations: %s", err) + return nil, nil, fmt.Errorf("failed to describe launch templates: %s", err) } - var lc *asgTypes.LaunchConfiguration + var lt *ec2types.LaunchTemplate - // we should only get one LC back, but just to be safe, loop through results and look for specific match - for _, l := range lcResult.LaunchConfigurations { - if *l.LaunchConfigurationName == *group.LaunchConfigurationName { - lc = &l + // we should only get one LT back, but just to be safe, loop through results and look for specific match + for _, l := range ltResult.LaunchTemplates { + if *l.LaunchTemplateName == *group.LaunchTemplate.LaunchTemplateName { + lt = &l break } } - // if no matching LC was found, return err - if lc == nil { - return nil, fmt.Errorf("unable to find a launch configuration by name %s for ASG %s", - *group.LaunchConfigurationName, asgName) + // if no matching LT was found, return err + if lt == nil { + return nil, nil, fmt.Errorf("unable to find a launch template by name %s for ASG %s", + *group.LaunchTemplate.LaunchTemplateName, asgName) + } + + ltdInput := ec2.DescribeLaunchTemplateVersionsInput{ + LaunchTemplateId: lt.LaunchTemplateId, + Versions: []string{"$Latest"}, + } + ltv, err := u.ec2Client.DescribeLaunchTemplateVersions(context.Background(), <dInput) + if err != nil { + return nil, nil, err } - return lc, nil + return lt, ltv.LaunchTemplateVersions[0].LaunchTemplateData, nil } func (u *Upgrader) getImageByID(imageID string) (ec2types.Image, error) { @@ -470,47 +481,36 @@ func (u *Upgrader) getImageByID(imageID string) (ec2types.Image, error) { return ec2types.Image{}, fmt.Errorf("unable to find image by ID %s", imageID) } -func (u *Upgrader) cloneLaunchConfigurationWithNewImage(lc *asgTypes.LaunchConfiguration, image ec2types.Image) (string, error) { - // autoscaling/types.LaunchConfiguration has the same fields as autoscaling.CreateLaunchConfigurationInput - // using json marshal/unmarshal as easy way to convert from one to the other - var newLc autoscaling.CreateLaunchConfigurationInput - if err := internal.ConvertToOtherType(lc, &newLc); err != nil { - return "", fmt.Errorf("failed to clone launch configuration, error: %s", err) - } - - newLc.LaunchConfigurationName = aws.String(fmt.Sprintf("%s-%s", u.launchConfigNamePrefix, internal.CurrentTimestamp(u.timestampLayout))) - newLc.ImageId = image.ImageId - - // KernelId and RamdiskId must be updated anytime a the ImageId is updated - newLc.KernelId = image.KernelId - newLc.RamdiskId = image.RamdiskId - - // need to nil out snapshot ids of block devices so they don't reference old AMI - for _, b := range newLc.BlockDeviceMappings { - b.Ebs.SnapshotId = nil - } - - // If newLc has an SSH key name and it's empty, change to nil as empty is not valid - if newLc.KeyName != nil && *newLc.KeyName == "" { - newLc.KeyName = nil +func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTemplate, image ec2types.Image) (*ec2types.LaunchTemplateVersion, error) { + newLtv := ec2.CreateLaunchTemplateVersionInput{ + LaunchTemplateId: lt.LaunchTemplateId, + LaunchTemplateData: &ec2types.RequestLaunchTemplateData{ + ImageId: image.ImageId, + }, } - _, err := u.asgClient.CreateLaunchConfiguration(context.Background(), &newLc) + out, err := u.ec2Client.CreateLaunchTemplateVersion(context.Background(), &newLtv) if err != nil { - return "", fmt.Errorf("failed to clone launch configuration, error: %s", err) + return nil, fmt.Errorf("failed to create a new launch template version, error: %s", err) } - return *newLc.LaunchConfigurationName, nil + return out.LaunchTemplateVersion, nil } -func (u *Upgrader) updateAsgLaunchConfiguration(asgName, launchConfigName string) error { +func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTemplateVersion) error { + versionNumber := fmt.Sprintf("%d", *v.VersionNumber) + updateInput := &autoscaling.UpdateAutoScalingGroupInput{ - AutoScalingGroupName: aws.String(asgName), - LaunchConfigurationName: aws.String(launchConfigName), + AutoScalingGroupName: aws.String(asgName), + LaunchTemplate: &asgTypes.LaunchTemplateSpecification{ + LaunchTemplateId: v.LaunchTemplateId, + Version: &versionNumber, + }, } if _, err := u.asgClient.UpdateAutoScalingGroup(context.Background(), updateInput); err != nil { - return fmt.Errorf("unable to update ASG %s to use launch configuration %s, error: %s", asgName, launchConfigName, err) + return fmt.Errorf("unable to update ASG %s to use launch template %s, error: %s", + asgName, *v.VersionDescription, err) } return nil From e29193d6770b0e13a96bb008226c04c12b84099f Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:45:41 +0800 Subject: [PATCH 02/11] finished converting everything to launch templates --- cli/cmd/root.go | 4 +- cli/cmd/upgrade-cluster.go | 44 ++++++----- domain.go | 54 +++++++------- upgrader.go | 148 +++++++++++++++++++++++-------------- upgrader_test.go | 28 ++++--- 5 files changed, 160 insertions(+), 118 deletions(-) diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 737c63a..ca6a640 100755 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -11,7 +11,7 @@ import ( "github.com/spf13/cobra" - homedir "github.com/mitchellh/go-homedir" + "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) @@ -55,7 +55,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ecs-ami-deploy.yaml)") rootCmd.PersistentFlags().StringVarP(&Profile, "profile", "p", "", "AWS shared credentials profile to use") - rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "us-east-1", "AWS shared credentials profile to use") + rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "us-east-1", "AWS region") // Cobra also supports local flags, which will only run // when this action is called directly. diff --git a/cli/cmd/upgrade-cluster.go b/cli/cmd/upgrade-cluster.go index 3f421ee..5cb96e5 100644 --- a/cli/cmd/upgrade-cluster.go +++ b/cli/cmd/upgrade-cluster.go @@ -11,12 +11,12 @@ import ( ) var ( - cluster string - forceReplace bool - launchConfigNamePrefix string - launchConfigLimit int - pollingInterval int - pollingTimeout int + cluster string + forceReplace bool + launchTemplateNamePrefix string + launchTemplateLimit int + pollingInterval int + pollingTimeout int ) // latestAMICmd represents the ec2 latest-ami command @@ -28,13 +28,13 @@ var upgradeClusterCmd = &cobra.Command{ initAwsCfg() upgrader, err := ead.NewUpgrader(AwsCfg, &ead.Config{ - Cluster: cluster, - AMIFilter: AMIFilter, - ForceReplacement: forceReplace, - LaunchConfigNamePrefix: launchConfigNamePrefix, - LaunchConfigLimit: launchConfigLimit, - PollingInterval: time.Duration(pollingInterval) * time.Second, - PollingTimeout: time.Duration(pollingTimeout) * time.Minute, + Cluster: cluster, + AMIFilter: AMIFilter, + ForceReplacement: forceReplace, + LaunchTemplateNamePrefix: launchTemplateNamePrefix, + LaunchTemplateLimit: launchTemplateLimit, + PollingInterval: time.Duration(pollingInterval) * time.Second, + PollingTimeout: time.Duration(pollingTimeout) * time.Minute, }) if err != nil { fmt.Println(err) @@ -65,10 +65,16 @@ func init() { upgradeClusterCmd.PersistentFlags().StringVar(&cluster, "cluster", "", "Cluster name") _ = upgradeClusterCmd.MarkPersistentFlagRequired("cluster") - upgradeClusterCmd.PersistentFlags().StringVar(&launchConfigNamePrefix, "launch-config-name-prefix", "", "Launch Configuration name prefix") - upgradeClusterCmd.PersistentFlags().BoolVar(&forceReplace, "force-replacement", false, "Force replacement if current AMI is already latest") - upgradeClusterCmd.PersistentFlags().StringVar(&AMIFilter, "ami-filter", ead.DefaultAMIFilter, "AMI search filter") - upgradeClusterCmd.PersistentFlags().IntVar(&launchConfigLimit, "launch-config-limit", ead.DefaultLaunchConfigLimit, "Number of previous launch configurations to keep.") - upgradeClusterCmd.PersistentFlags().IntVar(&pollingInterval, "polling-interval-seconds", int(ead.DefaultPollingInterval.Seconds()), "Number of seconds between status checks.") - upgradeClusterCmd.PersistentFlags().IntVar(&pollingTimeout, "polling-timeout-minutes", int(ead.DefaultPollingTimeout.Minutes()), "Number of minutes before a polling operation times out.") + upgradeClusterCmd.PersistentFlags().StringVar(&launchTemplateNamePrefix, "launch-template-name-prefix", + "", "launch template name prefix") + upgradeClusterCmd.PersistentFlags().BoolVar(&forceReplace, "force-replacement", + false, "Force replacement if current AMI is already latest") + upgradeClusterCmd.PersistentFlags().StringVar(&AMIFilter, "ami-filter", + ead.DefaultAMIFilter, "AMI search filter") + upgradeClusterCmd.PersistentFlags().IntVar(&launchTemplateLimit, "launch-template-limit", + ead.DefaultLaunchTemplateLimit, "Number of previous launch template versions to keep.") + upgradeClusterCmd.PersistentFlags().IntVar(&pollingInterval, "polling-interval-seconds", + int(ead.DefaultPollingInterval.Seconds()), "Number of seconds between status checks.") + upgradeClusterCmd.PersistentFlags().IntVar(&pollingTimeout, "polling-timeout-minutes", + int(ead.DefaultPollingTimeout.Minutes()), "Number of minutes before a polling operation times out.") } diff --git a/domain.go b/domain.go index f3cd273..98a76af 100755 --- a/domain.go +++ b/domain.go @@ -9,15 +9,15 @@ import ( ) const ( - DefaultAMIFilter = "amzn2-ami-ecs-hvm-*-x86_64-ebs" - DefaultPollingTimeout = 15 * time.Minute - DefaultPollingInterval = 5 * time.Second - DefaultLaunchConfigLimit = 5 - DefaultTimestampLayout = "20060102T150405" - MinimumIntervalsForStable = 6 - TagNameASG = "ecs-ami-deploy-asg" - TagNameTerminate = "ecs-ami-deploy-terminate" - Version = "0.0.0" + DefaultAMIFilter = "amzn2-ami-ecs-hvm-*-x86_64-ebs" + DefaultPollingTimeout = 15 * time.Minute + DefaultPollingInterval = 5 * time.Second + DefaultLaunchTemplateLimit = 5 + DefaultTimestampLayout = "20060102T150405" + MinimumIntervalsForStable = 6 + TagNameASG = "ecs-ami-deploy-asg" + TagNameTerminate = "ecs-ami-deploy-terminate" + Version = "0.0.0" ) type ClusterMeta struct { @@ -26,25 +26,25 @@ type ClusterMeta struct { } type Config struct { - AMIFilter string - Cluster string - ForceReplacement bool - LaunchConfigLimit int - LaunchConfigNamePrefix string - Logger *log.Logger - PollingInterval time.Duration - PollingTimeout time.Duration - TimestampLayout string + AMIFilter string + Cluster string + ForceReplacement bool + LaunchTemplateLimit int + LaunchTemplateNamePrefix string + Logger *log.Logger + PollingInterval time.Duration + PollingTimeout time.Duration + TimestampLayout string } var DefaultConfig = Config{ - AMIFilter: DefaultAMIFilter, - Cluster: "", - ForceReplacement: false, - LaunchConfigLimit: DefaultLaunchConfigLimit, - LaunchConfigNamePrefix: "", - Logger: nil, - PollingInterval: DefaultPollingInterval, - PollingTimeout: DefaultPollingTimeout, - TimestampLayout: DefaultTimestampLayout, + AMIFilter: DefaultAMIFilter, + Cluster: "", + ForceReplacement: false, + LaunchTemplateLimit: DefaultLaunchTemplateLimit, + LaunchTemplateNamePrefix: "", + Logger: nil, + PollingInterval: DefaultPollingInterval, + PollingTimeout: DefaultPollingTimeout, + TimestampLayout: DefaultTimestampLayout, } diff --git a/upgrader.go b/upgrader.go index e4cab00..3dd8a1a 100755 --- a/upgrader.go +++ b/upgrader.go @@ -22,15 +22,15 @@ import ( ) type Upgrader struct { - amiFilter string - cluster string - forceReplacement bool - launchConfigLimit int - launchConfigNamePrefix string - logger *log.Logger - pollingInterval time.Duration - pollingTimeout time.Duration - timestampLayout string + amiFilter string + cluster string + forceReplacement bool + launchTemplateLimit int + launchTemplateNamePrefix string + logger *log.Logger + pollingInterval time.Duration + pollingTimeout time.Duration + timestampLayout string awsCfg aws.Config asgClient *autoscaling.Client @@ -70,11 +70,11 @@ func (u *Upgrader) loadConfig(config *Config) error { config.Logger = log.Default() config.Logger.SetOutput(os.Stdout) } - if config.LaunchConfigNamePrefix == "" { - config.LaunchConfigNamePrefix = "ecs-" + config.Cluster + if config.LaunchTemplateNamePrefix == "" { + config.LaunchTemplateNamePrefix = "ecs-" + config.Cluster } - if config.LaunchConfigLimit == 0 { - config.LaunchConfigLimit = DefaultConfig.LaunchConfigLimit + if config.LaunchTemplateLimit == 0 { + config.LaunchTemplateLimit = DefaultConfig.LaunchTemplateLimit } if config.PollingInterval == 0 { config.PollingInterval = DefaultConfig.PollingInterval @@ -89,8 +89,8 @@ func (u *Upgrader) loadConfig(config *Config) error { u.amiFilter = config.AMIFilter u.cluster = config.Cluster u.forceReplacement = config.ForceReplacement - u.launchConfigLimit = config.LaunchConfigLimit - u.launchConfigNamePrefix = config.LaunchConfigNamePrefix + u.launchTemplateLimit = config.LaunchTemplateLimit + u.launchTemplateNamePrefix = config.LaunchTemplateNamePrefix u.logger = config.Logger u.pollingInterval = config.PollingInterval u.pollingTimeout = config.PollingTimeout @@ -172,6 +172,11 @@ func (u *Upgrader) ListClusters() ([]ClusterMeta, error) { continue } + if *c.ClusterName != "appsdev-dev" { + fmt.Printf("skipping cluster %s\n\n", *c.ClusterName) + continue + } + _, ltd, err := u.getLaunchTemplateForASG(asg) if err != nil { fmt.Printf("Error getting launch template for cluster %s\n%s\n\n", *c.ClusterName, err) @@ -215,21 +220,21 @@ func (u *Upgrader) UpgradeCluster() error { return err } u.logger.Printf("Launch template: %s\n", *lt.LaunchTemplateName) - u.logger.Printf("Latest version: %s\n", *lt.LatestVersionNumber) + u.logger.Printf("Latest version: %d\n", *lt.LatestVersionNumber) u.logger.Printf("Current image ID: %s\n", *ltd.ImageId) - latest, err := u.LatestAMI() + latestImage, err := u.LatestAMI() if err != nil { return err } - u.logger.Printf("Latest image found: %s\n", *latest.ImageId) + u.logger.Printf("Latest image found: %s\n", *latestImage.ImageId) current, err := u.getImageByID(*ltd.ImageId) if err != nil { return err } - isNewer, err := isNewerImage(current, latest) + isNewer, err := isNewerImage(current, latestImage) if err != nil { return err } @@ -264,12 +269,11 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Existing instances in ASG: %s\n", strings.Join(originalInstanceIDs, ", ")) - newLtv, err := u.newLaunchTemplateVersionWithNewImage(lt, latest) + newLtv, err := u.newLaunchTemplateVersionWithNewImage(lt, ltd, latestImage) if err != nil { return err } u.logger.Printf("New launch template version created: %d\n", *newLtv.VersionNumber) - if err := u.updateAsgLaunchTemplate(asgName, newLtv); err != nil { return err } @@ -313,7 +317,7 @@ func (u *Upgrader) UpgradeCluster() error { return err } - if err := u.cleanupOldLaunchConfigurations(); err != nil { + if err := u.cleanupOldLaunchTemplates(); err != nil { return err } @@ -481,11 +485,19 @@ func (u *Upgrader) getImageByID(imageID string) (ec2types.Image, error) { return ec2types.Image{}, fmt.Errorf("unable to find image by ID %s", imageID) } -func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTemplate, image ec2types.Image) (*ec2types.LaunchTemplateVersion, error) { +func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTemplate, + ltd *ec2types.ResponseLaunchTemplateData, image ec2types.Image) (*ec2types.LaunchTemplateVersion, error) { + newLtv := ec2.CreateLaunchTemplateVersionInput{ LaunchTemplateId: lt.LaunchTemplateId, LaunchTemplateData: &ec2types.RequestLaunchTemplateData{ - ImageId: image.ImageId, + IamInstanceProfile: &ec2types.LaunchTemplateIamInstanceProfileSpecificationRequest{ + Arn: ltd.IamInstanceProfile.Arn, + Name: ltd.IamInstanceProfile.Name, + }, + ImageId: image.ImageId, + InstanceType: ltd.InstanceType, + UserData: ltd.UserData, }, } @@ -498,21 +510,25 @@ func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTempl } func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTemplateVersion) error { - versionNumber := fmt.Sprintf("%d", *v.VersionNumber) - updateInput := &autoscaling.UpdateAutoScalingGroupInput{ AutoScalingGroupName: aws.String(asgName), LaunchTemplate: &asgTypes.LaunchTemplateSpecification{ LaunchTemplateId: v.LaunchTemplateId, - Version: &versionNumber, + Version: aws.String("$Latest"), }, } - if _, err := u.asgClient.UpdateAutoScalingGroup(context.Background(), updateInput); err != nil { - return fmt.Errorf("unable to update ASG %s to use launch template %s, error: %s", - asgName, *v.VersionDescription, err) + return fmt.Errorf("unable to update ASG %s to use launch template %s version %d, error: %s", + asgName, *v.LaunchTemplateName, *v.VersionNumber, err) } + in := &ec2.ModifyLaunchTemplateInput{ + DefaultVersion: aws.String(fmt.Sprintf("%d", v.VersionNumber)), + LaunchTemplateId: v.LaunchTemplateId, + } + if _, err := u.ec2Client.ModifyLaunchTemplate(context.Background(), in); err != nil { + return err + } return nil } @@ -652,6 +668,7 @@ func (u *Upgrader) waitForContainerInstanceCount(cluster string, desired int) er Clusters: []string{cluster}, } + u.logger.Printf("Waiting for cluster %s instances...", cluster) startTime := time.Now() for { if time.Since(startTime) >= u.pollingTimeout { @@ -670,6 +687,7 @@ func (u *Upgrader) waitForContainerInstanceCount(cluster string, desired int) er continue } if c.RegisteredContainerInstancesCount == int32(desired) { + u.logger.Printf("Cluster %s now has %v registered instances.", cluster, c.RegisteredContainerInstancesCount) return nil } u.logger.Printf("Still waiting for cluster %s to have %v registered instances, currently has %v", cluster, desired, c.RegisteredContainerInstancesCount) @@ -951,51 +969,57 @@ func (u *Upgrader) findDetachedButRunningInstances(asgName string) ([]string, er return orphanInstances, nil } -func (u *Upgrader) cleanupOldLaunchConfigurations() error { - input := &autoscaling.DescribeLaunchConfigurationsInput{ - MaxRecords: aws.Int32(100), +func (u *Upgrader) cleanupOldLaunchTemplates() error { + input := &ec2.DescribeLaunchTemplatesInput{ + MaxResults: aws.Int32(100), } - var relevantConfigs []asgTypes.LaunchConfiguration - paginator := autoscaling.NewDescribeLaunchConfigurationsPaginator(u.asgClient, input) + var relevantTemplates []ec2types.LaunchTemplate + paginator := ec2.NewDescribeLaunchTemplatesPaginator(u.ec2Client, input) for paginator.HasMorePages() { page, err := paginator.NextPage(context.Background()) if err != nil { - return fmt.Errorf("error retriving page of launch configs: %s", err) + return fmt.Errorf("error retriving page of launch templates: %s", err) } - for _, c := range page.LaunchConfigurations { - if strings.HasPrefix(*c.LaunchConfigurationName, u.launchConfigNamePrefix) { - relevantConfigs = append(relevantConfigs, c) + for _, lt := range page.LaunchTemplates { + if strings.HasPrefix(*lt.LaunchTemplateName, u.launchTemplateNamePrefix) { + relevantTemplates = append(relevantTemplates, lt) } } } - if len(relevantConfigs) == 0 || len(relevantConfigs) <= u.launchConfigLimit { + if len(relevantTemplates) == 0 || len(relevantTemplates) <= u.launchTemplateLimit { return nil } - u.logger.Printf("Found %v launch configurations with prefix %s. Configured to only keep %v, will delete oldest revisions", - len(relevantConfigs), u.launchConfigNamePrefix, u.launchConfigLimit) + u.logger.Printf("Found %v launch templates with prefix %s. Configured to only keep %v, will delete oldest revisions", + len(relevantTemplates), u.launchTemplateNamePrefix, u.launchTemplateLimit) + + versions, err := u.getTemplateVersions(relevantTemplates) + if err != nil { + return err + } - // sort launch configs newest to oldest - reverseSortLaunchConfigurationsByCreatedTime(relevantConfigs) + // sort launch template versions newest to oldest + reverseSortLaunchTemplateVersions(versions) - for i := u.launchConfigLimit; i < len(relevantConfigs); i++ { - if err := u.deleteLaunchConfiguration(*relevantConfigs[i].LaunchConfigurationName); err != nil { - return fmt.Errorf("error deleting launch configuration %s: %s", *relevantConfigs[i].LaunchConfigurationARN, err) + for i := u.launchTemplateLimit; i < len(versions); i++ { + if err := u.deleteLaunchTemplateVersion(*versions[i].LaunchTemplateName, fmt.Sprintf("%d", *versions[i].VersionNumber)); err != nil { + return fmt.Errorf("error deleting launch template %s version %d: %s", *versions[i].LaunchTemplateName, *versions[i].VersionNumber, err) } } return nil } -func (u *Upgrader) deleteLaunchConfiguration(name string) error { - input := &autoscaling.DeleteLaunchConfigurationInput{ - LaunchConfigurationName: aws.String(name), +func (u *Upgrader) deleteLaunchTemplateVersion(templateName, version string) error { + input := &ec2.DeleteLaunchTemplateVersionsInput{ + LaunchTemplateName: aws.String(templateName), + Versions: []string{version}, } - u.logger.Printf("Deleting launch configuration %s", name) + u.logger.Printf("Deleting launch template %s version %s", templateName, version) - _, err := u.asgClient.DeleteLaunchConfiguration(context.Background(), input) + _, err := u.ec2Client.DeleteLaunchTemplateVersions(context.Background(), input) return err } @@ -1017,8 +1041,22 @@ func isNewerImage(first, second ec2types.Image) (bool, error) { return secondTime.After(firstTime), nil } -func reverseSortLaunchConfigurationsByCreatedTime(lcs []asgTypes.LaunchConfiguration) { - sort.SliceStable(lcs, func(i, j int) bool { - return lcs[i].CreatedTime.UnixNano() > lcs[j].CreatedTime.UnixNano() +func reverseSortLaunchTemplateVersions(ltv []ec2types.LaunchTemplateVersion) { + sort.SliceStable(ltv, func(i, j int) bool { + return ltv[i].CreateTime.UnixNano() > ltv[j].CreateTime.UnixNano() }) } + +func (u *Upgrader) getTemplateVersions(templates []ec2types.LaunchTemplate) (versions []ec2types.LaunchTemplateVersion, err error) { + for _, t := range templates { + in := ec2.DescribeLaunchTemplateVersionsInput{ + LaunchTemplateId: t.LaunchTemplateId, + } + v, err := u.ec2Client.DescribeLaunchTemplateVersions(context.Background(), &in) + if err != nil { + return nil, err + } + versions = append(versions, v.LaunchTemplateVersions...) + } + return +} diff --git a/upgrader_test.go b/upgrader_test.go index 12a7471..3d29c97 100755 --- a/upgrader_test.go +++ b/upgrader_test.go @@ -4,8 +4,6 @@ import ( "testing" "time" - asgTypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" - "github.com/aws/aws-sdk-go-v2/aws" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) @@ -115,41 +113,41 @@ func Test_isNewerImage(t *testing.T) { // } //} -func TestSortLC(t *testing.T) { +func TestSortLT(t *testing.T) { now := time.Now() oneDay := 60 * 24 * time.Minute - lcs := []asgTypes.LaunchConfiguration{ + ltvs := []ec2types.LaunchTemplateVersion{ { - CreatedTime: aws.Time(now.Add(-oneDay)), + CreateTime: aws.Time(now.Add(-oneDay)), }, { - CreatedTime: aws.Time(now.Add(-10 * oneDay)), + CreateTime: aws.Time(now.Add(-10 * oneDay)), }, { - CreatedTime: aws.Time(now.Add(-20 * oneDay)), + CreateTime: aws.Time(now.Add(-20 * oneDay)), }, { - CreatedTime: aws.Time(now.Add(-50 * oneDay)), + CreateTime: aws.Time(now.Add(-50 * oneDay)), }, { - CreatedTime: aws.Time(now.Add(-5 * oneDay)), + CreateTime: aws.Time(now.Add(-5 * oneDay)), }, } - reverseSortLaunchConfigurationsByCreatedTime(lcs) + reverseSortLaunchTemplateVersions(ltvs) - for i := range lcs { + for i := range ltvs { // make sure not to test value out of range - if i+1 >= len(lcs) { + if i+1 >= len(ltvs) { continue } - if lcs[i].CreatedTime.Before(*lcs[i+1].CreatedTime) { + if ltvs[i].CreateTime.Before(*ltvs[i+1].CreateTime) { t.Errorf("time is not sorted as expected. index %v is %v, index %v+1 is %v", - i, lcs[i].CreatedTime.Unix(), i, lcs[i+1].CreatedTime.Unix()) + i, ltvs[i].CreateTime.Unix(), i, ltvs[i+1].CreateTime.Unix()) } } - //for _, i := range lcs { + //for _, i := range ltvs { // log.Printf("%v - %s", i.CreatedTime.Unix(), i.CreatedTime.Format(time.RFC3339)) //} } From 05fd751620482ed221445f49511c3c487efd5e16 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 10:09:49 +0800 Subject: [PATCH 03/11] copy all launch template data to new version --- upgrader.go | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/upgrader.go b/upgrader.go index 3dd8a1a..996d50f 100755 --- a/upgrader.go +++ b/upgrader.go @@ -488,27 +488,34 @@ func (u *Upgrader) getImageByID(imageID string) (ec2types.Image, error) { func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTemplate, ltd *ec2types.ResponseLaunchTemplateData, image ec2types.Image) (*ec2types.LaunchTemplateVersion, error) { + newLtd, err := makeLaunchTemplateDataRequest(ltd) + if err != nil { + return nil, fmt.Errorf("failed to create a new launch template version, %w", err) + } + + newLtd.ImageId = image.ImageId + newLtv := ec2.CreateLaunchTemplateVersionInput{ - LaunchTemplateId: lt.LaunchTemplateId, - LaunchTemplateData: &ec2types.RequestLaunchTemplateData{ - IamInstanceProfile: &ec2types.LaunchTemplateIamInstanceProfileSpecificationRequest{ - Arn: ltd.IamInstanceProfile.Arn, - Name: ltd.IamInstanceProfile.Name, - }, - ImageId: image.ImageId, - InstanceType: ltd.InstanceType, - UserData: ltd.UserData, - }, + LaunchTemplateId: lt.LaunchTemplateId, + LaunchTemplateData: newLtd, } out, err := u.ec2Client.CreateLaunchTemplateVersion(context.Background(), &newLtv) if err != nil { - return nil, fmt.Errorf("failed to create a new launch template version, error: %s", err) + return nil, fmt.Errorf("failed to create a new launch template version, %w", err) } return out.LaunchTemplateVersion, nil } +func makeLaunchTemplateDataRequest(in *ec2types.ResponseLaunchTemplateData) (*ec2types.RequestLaunchTemplateData, error) { + var out ec2types.RequestLaunchTemplateData + if err := internal.ConvertToOtherType(in, &out); err != nil { + return nil, fmt.Errorf("error making launch template data for request, %w", err) + } + return &out, nil +} + func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTemplateVersion) error { updateInput := &autoscaling.UpdateAutoScalingGroupInput{ AutoScalingGroupName: aws.String(asgName), @@ -523,7 +530,7 @@ func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTem } in := &ec2.ModifyLaunchTemplateInput{ - DefaultVersion: aws.String(fmt.Sprintf("%d", v.VersionNumber)), + DefaultVersion: aws.String(fmt.Sprintf("%d", *v.VersionNumber)), LaunchTemplateId: v.LaunchTemplateId, } if _, err := u.ec2Client.ModifyLaunchTemplate(context.Background(), in); err != nil { From d76f1a051e6b452655027cabb6740949cb0330ff Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 10:25:36 +0800 Subject: [PATCH 04/11] remove debugging code --- upgrader.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/upgrader.go b/upgrader.go index 996d50f..ecb5d02 100755 --- a/upgrader.go +++ b/upgrader.go @@ -172,11 +172,6 @@ func (u *Upgrader) ListClusters() ([]ClusterMeta, error) { continue } - if *c.ClusterName != "appsdev-dev" { - fmt.Printf("skipping cluster %s\n\n", *c.ClusterName) - continue - } - _, ltd, err := u.getLaunchTemplateForASG(asg) if err != nil { fmt.Printf("Error getting launch template for cluster %s\n%s\n\n", *c.ClusterName, err) From 5a20e6f51848d9637e255d87f639586134ad6814 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 11:04:11 +0800 Subject: [PATCH 05/11] nil out the snapshot IDs and SSH key name (if necessary) --- upgrader.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/upgrader.go b/upgrader.go index ecb5d02..4cdc54f 100755 --- a/upgrader.go +++ b/upgrader.go @@ -269,6 +269,7 @@ func (u *Upgrader) UpgradeCluster() error { return err } u.logger.Printf("New launch template version created: %d\n", *newLtv.VersionNumber) + if err := u.updateAsgLaunchTemplate(asgName, newLtv); err != nil { return err } @@ -490,6 +491,16 @@ func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTempl newLtd.ImageId = image.ImageId + // need to nil out snapshot ids of block devices so they don't reference old AMI + for _, b := range newLtd.BlockDeviceMappings { + b.Ebs.SnapshotId = nil + } + + // If newLtv has an SSH key name and it's empty, change to nil as empty is not valid + if newLtd.KeyName != nil && *newLtd.KeyName == "" { + newLtd.KeyName = nil + } + newLtv := ec2.CreateLaunchTemplateVersionInput{ LaunchTemplateId: lt.LaunchTemplateId, LaunchTemplateData: newLtd, From 4173dad930cf9e847a5b778171a98e78244a3822 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 11:07:17 +0800 Subject: [PATCH 06/11] update KernelId and RamDiskId also --- upgrader.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/upgrader.go b/upgrader.go index 4cdc54f..fd0e9a9 100755 --- a/upgrader.go +++ b/upgrader.go @@ -491,6 +491,10 @@ func (u *Upgrader) newLaunchTemplateVersionWithNewImage(lt *ec2types.LaunchTempl newLtd.ImageId = image.ImageId + // KernelId and RamdiskId must be updated anytime a the ImageId is updated + newLtd.KernelId = image.KernelId + newLtd.RamDiskId = image.RamdiskId + // need to nil out snapshot ids of block devices so they don't reference old AMI for _, b := range newLtd.BlockDeviceMappings { b.Ebs.SnapshotId = nil From 26b42eb4e07e1d33b13782b90db19a670a17d339 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 11:18:16 +0800 Subject: [PATCH 07/11] edit README description of ASG update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba064ff..57ee6bb 100755 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ available or not. When `--force-replacement` is enabled the process is _not_ ide 1. If cluster is not using latest AMI, or `force replacement` is enabled, proceed to #4 2. Else if using latest AMI already, jump to #10 4. Create new launch template version with new AMI - 5. Update ASG to use new launch config + 5. Update launch template default version and set ASG to use the latest template version (`"$Latest"`) 6. Detach existing instances from ASG and replace with new ones 7. Wait for new instances to reach `InService` state with ASG 8. Watch ECS cluster instances until all new ones are registered and available From 1319fd784616bf42fb58436e7df81d261b4684ec Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 11:19:30 +0800 Subject: [PATCH 08/11] break long lines --- upgrader.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/upgrader.go b/upgrader.go index fd0e9a9..8defee7 100755 --- a/upgrader.go +++ b/upgrader.go @@ -1020,8 +1020,10 @@ func (u *Upgrader) cleanupOldLaunchTemplates() error { reverseSortLaunchTemplateVersions(versions) for i := u.launchTemplateLimit; i < len(versions); i++ { - if err := u.deleteLaunchTemplateVersion(*versions[i].LaunchTemplateName, fmt.Sprintf("%d", *versions[i].VersionNumber)); err != nil { - return fmt.Errorf("error deleting launch template %s version %d: %s", *versions[i].LaunchTemplateName, *versions[i].VersionNumber, err) + versionString := fmt.Sprintf("%d", *versions[i].VersionNumber) + if err := u.deleteLaunchTemplateVersion(*versions[i].LaunchTemplateName, versionString); err != nil { + return fmt.Errorf("error deleting launch template %s version %d: %s", + *versions[i].LaunchTemplateName, *versions[i].VersionNumber, err) } } From c165a634164f90e4d721efb8f3c03c078e3367ff Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Mon, 1 May 2023 20:54:23 +0800 Subject: [PATCH 09/11] minor correction in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57ee6bb..5f9436c 100755 --- a/README.md +++ b/README.md @@ -74,6 +74,6 @@ available or not. When `--force-replacement` is enabled the process is _not_ ide 3. Run `ecs-ami-deploy list-clusters` to check if it's working and what clusters you have available. 4. If you have multiple profiles configured in your `~/.aws/credentials` file, you can use the `-p` or `--profile` flags to specify a different profile. -5. The CLI defaults to region `us-east-1`, you can use the `-r` or `-region` flags to specify else +5. The CLI defaults to region `us-east-1`, you can use the `-r` or `--region` flags to specify else 6. The CLI has help information built in for the various subcommands and their supported flags, use `-h` or `--help` flags with each subcommand for more information. From b6c380fee4a66edbf8055de753331b15f93fafad Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Tue, 2 May 2023 12:02:31 +0800 Subject: [PATCH 10/11] PR review recommendations --- cli/cmd/upgrade-cluster.go | 2 +- upgrader.go | 24 ++++++++++++------------ upgrader_test.go | 4 ---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cli/cmd/upgrade-cluster.go b/cli/cmd/upgrade-cluster.go index 5cb96e5..31600b2 100644 --- a/cli/cmd/upgrade-cluster.go +++ b/cli/cmd/upgrade-cluster.go @@ -66,7 +66,7 @@ func init() { _ = upgradeClusterCmd.MarkPersistentFlagRequired("cluster") upgradeClusterCmd.PersistentFlags().StringVar(&launchTemplateNamePrefix, "launch-template-name-prefix", - "", "launch template name prefix") + "", "Launch template name prefix") upgradeClusterCmd.PersistentFlags().BoolVar(&forceReplace, "force-replacement", false, "Force replacement if current AMI is already latest") upgradeClusterCmd.PersistentFlags().StringVar(&AMIFilter, "ami-filter", diff --git a/upgrader.go b/upgrader.go index 8defee7..434a61e 100755 --- a/upgrader.go +++ b/upgrader.go @@ -172,12 +172,12 @@ func (u *Upgrader) ListClusters() ([]ClusterMeta, error) { continue } - _, ltd, err := u.getLaunchTemplateForASG(asg) + _, ltData, err := u.getLaunchTemplateForASG(asg) if err != nil { fmt.Printf("Error getting launch template for cluster %s\n%s\n\n", *c.ClusterName, err) continue } - img, err := u.getImageByID(*ltd.ImageId) + img, err := u.getImageByID(*ltData.ImageId) if err != nil { return []ClusterMeta{}, fmt.Errorf("error getting image details for cluster %s: %s", *c.ClusterName, err) } @@ -210,13 +210,13 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Found ASG: %s\n", asgName) - lt, ltd, err := u.getLaunchTemplateForASG(asgName) + lt, ltData, err := u.getLaunchTemplateForASG(asgName) if err != nil { return err } u.logger.Printf("Launch template: %s\n", *lt.LaunchTemplateName) u.logger.Printf("Latest version: %d\n", *lt.LatestVersionNumber) - u.logger.Printf("Current image ID: %s\n", *ltd.ImageId) + u.logger.Printf("Current image ID: %s\n", *ltData.ImageId) latestImage, err := u.LatestAMI() if err != nil { @@ -224,7 +224,7 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Latest image found: %s\n", *latestImage.ImageId) - current, err := u.getImageByID(*ltd.ImageId) + current, err := u.getImageByID(*ltData.ImageId) if err != nil { return err } @@ -264,7 +264,7 @@ func (u *Upgrader) UpgradeCluster() error { } u.logger.Printf("Existing instances in ASG: %s\n", strings.Join(originalInstanceIDs, ", ")) - newLtv, err := u.newLaunchTemplateVersionWithNewImage(lt, ltd, latestImage) + newLtv, err := u.newLaunchTemplateVersionWithNewImage(lt, ltData, latestImage) if err != nil { return err } @@ -403,7 +403,7 @@ func (u *Upgrader) getLaunchTemplateForASG(asgName string) (*ec2types.LaunchTemp result, err := u.asgClient.DescribeAutoScalingGroups(context.Background(), input) if err != nil { - return nil, nil, fmt.Errorf("failed to describe auto-scaling groups: %s", err) + return nil, nil, fmt.Errorf("failed to describe auto-scaling groups: %w", err) } var group asgTypes.AutoScalingGroup @@ -429,7 +429,7 @@ func (u *Upgrader) getLaunchTemplateForASG(asgName string) (*ec2types.LaunchTemp ltResult, err := u.ec2Client.DescribeLaunchTemplates(context.Background(), ltInput) if err != nil { - return nil, nil, fmt.Errorf("failed to describe launch templates: %s", err) + return nil, nil, fmt.Errorf("failed to describe launch templates: %w", err) } var lt *ec2types.LaunchTemplate @@ -535,7 +535,7 @@ func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTem }, } if _, err := u.asgClient.UpdateAutoScalingGroup(context.Background(), updateInput); err != nil { - return fmt.Errorf("unable to update ASG %s to use launch template %s version %d, error: %s", + return fmt.Errorf("unable to update ASG %s to use launch template %s version %d, error: %w", asgName, *v.LaunchTemplateName, *v.VersionNumber, err) } @@ -544,7 +544,7 @@ func (u *Upgrader) updateAsgLaunchTemplate(asgName string, v *ec2types.LaunchTem LaunchTemplateId: v.LaunchTemplateId, } if _, err := u.ec2Client.ModifyLaunchTemplate(context.Background(), in); err != nil { - return err + return fmt.Errorf("failed to modify launch template: %w", err) } return nil } @@ -996,7 +996,7 @@ func (u *Upgrader) cleanupOldLaunchTemplates() error { for paginator.HasMorePages() { page, err := paginator.NextPage(context.Background()) if err != nil { - return fmt.Errorf("error retriving page of launch templates: %s", err) + return fmt.Errorf("error retrieving page of launch templates: %s", err) } for _, lt := range page.LaunchTemplates { if strings.HasPrefix(*lt.LaunchTemplateName, u.launchTemplateNamePrefix) { @@ -1022,7 +1022,7 @@ func (u *Upgrader) cleanupOldLaunchTemplates() error { for i := u.launchTemplateLimit; i < len(versions); i++ { versionString := fmt.Sprintf("%d", *versions[i].VersionNumber) if err := u.deleteLaunchTemplateVersion(*versions[i].LaunchTemplateName, versionString); err != nil { - return fmt.Errorf("error deleting launch template %s version %d: %s", + return fmt.Errorf("error deleting launch template %s version %d: %w", *versions[i].LaunchTemplateName, *versions[i].VersionNumber, err) } } diff --git a/upgrader_test.go b/upgrader_test.go index 3d29c97..85e40a9 100755 --- a/upgrader_test.go +++ b/upgrader_test.go @@ -146,8 +146,4 @@ func TestSortLT(t *testing.T) { i, ltvs[i].CreateTime.Unix(), i, ltvs[i+1].CreateTime.Unix()) } } - - //for _, i := range ltvs { - // log.Printf("%v - %s", i.CreatedTime.Unix(), i.CreatedTime.Format(time.RFC3339)) - //} } From 8ea12a5e8dd71b57c66a47898d1f62cfb3c63584 Mon Sep 17 00:00:00 2001 From: Schparky <3172830+Schparky@users.noreply.github.com> Date: Wed, 3 May 2023 21:18:27 +0800 Subject: [PATCH 11/11] PR feedback: use better grammar Co-authored-by: Jason Jackson <35783387+jason-jackson@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f9436c..7038451 100755 --- a/README.md +++ b/README.md @@ -74,6 +74,6 @@ available or not. When `--force-replacement` is enabled the process is _not_ ide 3. Run `ecs-ami-deploy list-clusters` to check if it's working and what clusters you have available. 4. If you have multiple profiles configured in your `~/.aws/credentials` file, you can use the `-p` or `--profile` flags to specify a different profile. -5. The CLI defaults to region `us-east-1`, you can use the `-r` or `--region` flags to specify else +5. The CLI defaults to region `us-east-1`, you can use the `-r` or `--region` flags to specify a different region 6. The CLI has help information built in for the various subcommands and their supported flags, use `-h` or `--help` flags with each subcommand for more information.