From 8a9753032ce6b933444202a61f57746d6ddaa771 Mon Sep 17 00:00:00 2001 From: Daniel Rodgers-Pryor Date: Sun, 30 Jun 2019 16:00:52 +1000 Subject: [PATCH] Allow EC2 fleet packer instances to run in parallel Without conflicting over the name of the launch template that they create/delete. Previously, the launch template name was just hard coded to `packer-fleet-launch-template`, but since AWS enforces unique template names within an account, this caused simultaneously running packer instances to hit template-already-exists errors when creating their templates and race-conditions around deleting the template. Now, the template name is randomly generated on each run, so there should be no conflicts. --- builder/amazon/common/step_run_spot_instance.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index 2ec57114c..40c2e4e57 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/hashicorp/packer/common/random" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -259,6 +260,14 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) // Create a launch template for the instance ui.Message("Loading User Data File...") + + // Generate a random name to avoid conflicting with other + // instances of packer running in this AWS account + launchTemplateName := fmt.Sprintf( + "packer-fleet-launch-template-%s", + random.AlphaNum(7)) + state.Put("launchTemplateName", launchTemplateName) // For the cleanup step + userData, err := s.LoadUserData() if err != nil { state.Put("error", err) @@ -268,7 +277,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) templateData := s.CreateTemplateData(&userData, az, state, marketOptions) launchTemplate := &ec2.CreateLaunchTemplateInput{ LaunchTemplateData: templateData, - LaunchTemplateName: aws.String("packer-fleet-launch-template"), + LaunchTemplateName: aws.String(launchTemplateName), VersionDescription: aws.String("template generated by packer for launching spot instances"), } @@ -294,7 +303,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) LaunchTemplateConfigs: []*ec2.FleetLaunchTemplateConfigRequest{ { LaunchTemplateSpecification: &ec2.FleetLaunchTemplateSpecificationRequest{ - LaunchTemplateName: aws.String("packer-fleet-launch-template"), + LaunchTemplateName: aws.String(launchTemplateName), Version: aws.String("1"), }, Overrides: overrides, @@ -460,6 +469,7 @@ func (s *StepRunSpotInstance) Cleanup(state multistep.StateBag) { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) + launchTemplateName := state.Get("launchTemplateName").(string) // Cancel the spot request if it exists if s.spotRequest != nil { @@ -494,7 +504,7 @@ func (s *StepRunSpotInstance) Cleanup(state multistep.StateBag) { // Delete the launch template used to create the spot fleet deleteInput := &ec2.DeleteLaunchTemplateInput{ - LaunchTemplateName: aws.String("packer-fleet-launch-template"), + LaunchTemplateName: aws.String(launchTemplateName), } if _, err := ec2conn.DeleteLaunchTemplate(deleteInput); err != nil { ui.Error(err.Error())