amazon: use spot tags for launch template tags

- use `spot_tags` instead of `run_tags` for launch template
- move region to `StepRunSpotInstance` from state
This commit is contained in:
Aleksandr Serbin 2020-11-03 22:15:44 +01:00
parent d561b404d6
commit 8dab31b548
6 changed files with 70 additions and 32 deletions

View File

@ -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,13 +252,15 @@ 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
_, err = ec2conn.CreateLaunchTemplate(launchTemplate)
@ -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)

View File

@ -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")
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,