diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index 1d80b924a..dc196817a 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -36,6 +36,7 @@ type StepRunSpotInstance struct { ExpectedRootDevice string InstanceInitiatedShutdownBehavior string InstanceType string + Region string SourceAMI string SpotPrice string SpotTags map[string]string @@ -161,7 +162,6 @@ func (s *StepRunSpotInstance) LoadUserData() (string, error) { func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(ec2iface.EC2API) ui := state.Get("ui").(packer.Ui) - region := state.Get("region").(string) ui.Say("Launching a spot AWS instance...") @@ -199,7 +199,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } // Convert tags from the tag map provided by the user into *ec2.Tag s - ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, region, state) + ec2Tags, err := TagMap(s.Tags).EC2Tags(s.Ctx, s.Region, state) if err != nil { err := fmt.Errorf("Error generating tags for source instance: %s", err) state.Put("error", err) @@ -223,6 +223,14 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) } marketOptions.SetMarketType(ec2.MarketTypeSpot) + spotTags, err := TagMap(s.SpotTags).EC2Tags(s.Ctx, s.Region, state) + if err != nil { + err := fmt.Errorf("Error generating tags for spot request: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + // Create a launch template for the instance ui.Message("Loading User Data File...") @@ -244,12 +252,14 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) LaunchTemplateData: templateData, LaunchTemplateName: aws.String(launchTemplateName), VersionDescription: aws.String("template generated by packer for launching spot instances"), - TagSpecifications: []*ec2.TagSpecification{ + } + if len(spotTags) > 0 { + launchTemplate.TagSpecifications = []*ec2.TagSpecification{ { ResourceType: aws.String("launch-template"), - Tags: ec2Tags, + Tags: spotTags, }, - }, + } } // Tell EC2 to create the template @@ -369,14 +379,6 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) instance := describeOutput.Reservations[0].Instances[0] // Tag the spot instance request (not the eventual spot instance) - spotTags, err := TagMap(s.SpotTags).EC2Tags(s.Ctx, region, state) - if err != nil { - err := fmt.Errorf("Error generating tags for spot request: %s", err) - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - if len(spotTags) > 0 && len(s.SpotTags) > 0 { spotTags.Report(ui) // Use the instance ID to find out the SIR, so that we can tag the spot @@ -436,7 +438,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) if len(volumeIds) > 0 && len(s.VolumeTags) > 0 { ui.Say("Adding tags to source EBS Volumes") - volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, region, state) + volumeTags, err := TagMap(s.VolumeTags).EC2Tags(s.Ctx, s.Region, state) if err != nil { err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err) state.Put("error", err) diff --git a/builder/amazon/common/step_run_spot_instance_test.go b/builder/amazon/common/step_run_spot_instance_test.go index 4a57d0cbd..d00bf74dd 100644 --- a/builder/amazon/common/step_run_spot_instance_test.go +++ b/builder/amazon/common/step_run_spot_instance_test.go @@ -45,6 +45,7 @@ func getBasicStep() *StepRunSpotInstance { ExpectedRootDevice: "ebs", InstanceInitiatedShutdownBehavior: "stop", InstanceType: "t2.micro", + Region: "us-east-1", SourceAMI: "", SpotPrice: "auto", SpotTags: nil, @@ -189,10 +190,7 @@ func (m *runSpotEC2ConnMock) CreateTags(req *ec2.CreateTagsInput) (*ec2.CreateTa } } -func TestRun(t *testing.T) { - instanceId := aws.String("test-instance-id") - spotRequestId := aws.String("spot-id") - volumeId := aws.String("volume-id") +func defaultEc2Mock(instanceId, spotRequestId, volumeId *string) *runSpotEC2ConnMock { instance := &ec2.Instance{ InstanceId: instanceId, SpotInstanceRequestId: spotRequestId, @@ -204,7 +202,7 @@ func TestRun(t *testing.T) { }, }, } - ec2Mock := &runSpotEC2ConnMock{ + return &runSpotEC2ConnMock{ CreateLaunchTemplateFn: func(in *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) { return &ec2.CreateLaunchTemplateOutput{ LaunchTemplate: nil, @@ -233,6 +231,13 @@ func TestRun(t *testing.T) { }, nil }, } +} + +func TestRun(t *testing.T) { + instanceId := aws.String("test-instance-id") + spotRequestId := aws.String("spot-id") + volumeId := aws.String("volume-id") + ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId) uiMock := packer.TestUi(t) @@ -240,7 +245,6 @@ func TestRun(t *testing.T) { state.Put("ec2", ec2Mock) state.Put("ui", uiMock) state.Put("source_image", testImage()) - state.Put("region", "test-region") stepRunSpotInstance := getBasicStep() stepRunSpotInstance.Tags["Name"] = "Packer Builder" @@ -274,17 +278,13 @@ func TestRun(t *testing.T) { if *ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].ResourceType != "launch-template" { t.Fatalf("resource type 'launch-template' expected") } - if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 2 { - t.Fatalf("2 tags expected") + if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 1 { + t.Fatalf("1 launch template tag expected") } nameTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[0] - if *nameTag.Key != "Name" || *nameTag.Value != "Packer Builder" { - t.Fatalf("expected name tag") - } - testTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[1] - if *testTag.Key != "test-tag" || *testTag.Value != "test-value" { - t.Fatalf("expected test tag") + if *nameTag.Key != "spot-tag" || *nameTag.Value != "spot-tag-value" { + t.Fatalf("expected spot-tag: spot-tag-value") } if len(ec2Mock.CreateFleetParams) != 1 { @@ -324,3 +324,39 @@ func TestRun(t *testing.T) { t.Fatalf("should create tags for volume") } } + +func TestRun_NoSpotTags(t *testing.T) { + instanceId := aws.String("test-instance-id") + spotRequestId := aws.String("spot-id") + volumeId := aws.String("volume-id") + ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId) + + uiMock := packer.TestUi(t) + + state := tStateSpot() + state.Put("ec2", ec2Mock) + state.Put("ui", uiMock) + state.Put("source_image", testImage()) + + stepRunSpotInstance := getBasicStep() + stepRunSpotInstance.Tags["Name"] = "Packer Builder" + stepRunSpotInstance.Tags["test-tag"] = "test-value" + stepRunSpotInstance.VolumeTags = map[string]string{ + "volume-tag": "volume-tag-value", + } + + ctx := context.TODO() + action := stepRunSpotInstance.Run(ctx, state) + + if err := state.Get("error"); err != nil { + t.Fatalf("should not error, but: %v", err) + } + + if action != multistep.ActionContinue { + t.Fatalf("shoul continue, but: %v", action) + } + + if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 0 { + t.Fatalf("0 launch template tags expected") + } +} diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 8c4f00fe2..03c8d0c03 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -164,7 +164,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("awsSession", session) state.Put("hook", hook) state.Put("ui", ui) - state.Put("region", *ec2conn.Config.Region) generatedData := &builder.GeneratedData{State: state} var instanceStep multistep.Step @@ -182,6 +181,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack ExpectedRootDevice: "ebs", InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, InstanceType: b.config.InstanceType, + Region: *ec2conn.Config.Region, SourceAMI: b.config.SourceAmi, SpotPrice: b.config.SpotPrice, SpotTags: b.config.SpotTags, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 6600850f6..5688193a8 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -187,7 +187,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("awsSession", session) state.Put("hook", hook) state.Put("ui", ui) - state.Put("region", *ec2conn.Config.Region) generatedData := &builder.GeneratedData{State: state} var instanceStep multistep.Step @@ -205,6 +204,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack ExpectedRootDevice: "ebs", InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, InstanceType: b.config.InstanceType, + Region: *ec2conn.Config.Region, SourceAMI: b.config.SourceAmi, SpotPrice: b.config.SpotPrice, SpotInstanceTypes: b.config.SpotInstanceTypes, diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index f9cb3b819..2c7a859a3 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -168,7 +168,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("iam", iam) state.Put("hook", hook) state.Put("ui", ui) - state.Put("region", *ec2conn.Config.Region) generatedData := &builder.GeneratedData{State: state} var instanceStep multistep.Step @@ -186,6 +185,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack ExpectedRootDevice: "ebs", InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, InstanceType: b.config.InstanceType, + Region: *ec2conn.Config.Region, SourceAMI: b.config.SourceAmi, SpotInstanceTypes: b.config.SpotInstanceTypes, SpotPrice: b.config.SpotPrice, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 214f16283..fe755edb1 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -250,7 +250,6 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack state.Put("awsSession", session) state.Put("hook", hook) state.Put("ui", ui) - state.Put("region", ec2conn.Config.Region) generatedData := &builder.GeneratedData{State: state} var instanceStep multistep.Step @@ -266,6 +265,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Debug: b.config.PackerDebug, EbsOptimized: b.config.EbsOptimized, InstanceType: b.config.InstanceType, + Region: *ec2conn.Config.Region, SourceAMI: b.config.SourceAmi, SpotPrice: b.config.SpotPrice, SpotInstanceTypes: b.config.SpotInstanceTypes,