builder/amazon: Add SourceAMI and BuildRegion template

Added {{ .SourceAMI }} and {{ .BuildRegion }} template values availible
in `ami_description`, `run_tags`, `run_volume_tags`, `tags`, and
`snapshot_tags`.
This commit is contained in:
Rickard von Essen 2017-01-10 11:41:28 +01:00
parent 198824a25f
commit ba9cae5078
10 changed files with 186 additions and 14 deletions

View File

@ -64,6 +64,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
InterpolateContext: &b.config.ctx, InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{ InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{ Exclude: []string{
"ami_description",
"snapshot_tags",
"tags",
"command_wrapper", "command_wrapper",
"post_mount_commands", "post_mount_commands",
"pre_mount_commands", "pre_mount_commands",
@ -263,10 +266,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags, SnapshotTags: b.config.SnapshotTags,
Ctx: b.config.ctx,
}, },
) )

View File

@ -0,0 +1,6 @@
package common
type BuildInfoTemplate struct {
SourceAMI string
BuildRegion string
}

View File

@ -2,7 +2,6 @@ package common
import ( import (
"fmt" "fmt"
"log"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
@ -11,11 +10,13 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
retry "github.com/mitchellh/packer/common" retry "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
) )
type StepCreateTags struct { type StepCreateTags struct {
Tags map[string]string Tags map[string]string
SnapshotTags map[string]string SnapshotTags map[string]string
Ctx interpolate.Context
} }
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction { func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
@ -23,6 +24,13 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string) amis := state.Get("amis").(map[string]string)
var sourceAMI string
if rawSourceAMI, hasSourceAMI := state.GetOk("source_image"); hasSourceAMI {
sourceAMI = *rawSourceAMI.(*ec2.Image).ImageId
} else {
sourceAMI = ""
}
if len(s.Tags) == 0 && len(s.SnapshotTags) == 0 { if len(s.Tags) == 0 && len(s.SnapshotTags) == 0 {
return multistep.ActionContinue return multistep.ActionContinue
} }
@ -31,6 +39,22 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
for region, ami := range amis { for region, ami := range amis {
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami)) ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
// Convert tags to ec2.Tag format
amiTags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, sourceAMI, s.Ctx, ui)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Snapshot tags:"))
snapshotTags, err := ConvertToEC2Tags(s.SnapshotTags, *ec2conn.Config.Region, sourceAMI, s.Ctx, ui)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Declare list of resources to tag // Declare list of resources to tag
awsConfig := aws.Config{ awsConfig := aws.Config{
Credentials: ec2conn.Config.Credentials, Credentials: ec2conn.Config.Credentials,
@ -79,9 +103,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
// Convert tags to ec2.Tag format // Convert tags to ec2.Tag format
ui.Say("Creating AMI tags") ui.Say("Creating AMI tags")
amiTags := ConvertToEC2Tags(s.Tags) amiTags, err = ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, sourceAMI, s.Ctx, ui)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Creating snapshot tags") ui.Say("Creating snapshot tags")
snapshotTags := ConvertToEC2Tags(s.SnapshotTags) snapshotTags, err = ConvertToEC2Tags(s.SnapshotTags, *ec2conn.Config.Region, sourceAMI, s.Ctx, ui)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Retry creating tags for about 2.5 minutes // Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func() (bool, error) { err = retry.Retry(0.2, 30, 11, func() (bool, error) {
@ -130,14 +165,25 @@ func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup... // No cleanup...
} }
func ConvertToEC2Tags(tags map[string]string) []*ec2.Tag { func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx interpolate.Context, ui packer.Ui) ([]*ec2.Tag, error) {
var ec2tags []*ec2.Tag var amiTags []*ec2.Tag
for key, value := range tags { for key, value := range tags {
log.Printf("[DEBUG] Creating tag %s=%s", key, value)
ec2tags = append(ec2tags, &ec2.Tag{ ctx.Data = &BuildInfoTemplate{
SourceAMI: sourceAmiId,
BuildRegion: region,
}
interpolatedValue, err := interpolate.Render(value, &ctx)
if err != nil {
return amiTags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
}
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, interpolatedValue))
amiTags = append(amiTags, &ec2.Tag{
Key: aws.String(key), Key: aws.String(key),
Value: aws.String(value), Value: aws.String(interpolatedValue),
}) })
} }
return ec2tags
return amiTags, nil
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
) )
type StepModifyAMIAttributes struct { type StepModifyAMIAttributes struct {
@ -17,12 +18,20 @@ type StepModifyAMIAttributes struct {
SnapshotGroups []string SnapshotGroups []string
ProductCodes []string ProductCodes []string
Description string Description string
Ctx interpolate.Context
} }
func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction { func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string) amis := state.Get("amis").(map[string]string)
var sourceAMI string
if rawSourceAMI, hasSourceAMI := state.GetOk("source_image"); hasSourceAMI {
sourceAMI = *rawSourceAMI.(*ec2.Image).ImageId
} else {
sourceAMI = ""
}
snapshots := state.Get("snapshots").(map[string][]string) snapshots := state.Get("snapshots").(map[string][]string)
// Determine if there is any work to do. // Determine if there is any work to do.
@ -38,6 +47,18 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc
return multistep.ActionContinue return multistep.ActionContinue
} }
var err error
s.Ctx.Data = &BuildInfoTemplate{
SourceAMI: sourceAMI,
BuildRegion: *ec2conn.Config.Region,
}
s.Description, err = interpolate.Render(s.Description, &s.Ctx)
if err != nil {
err = fmt.Errorf("Error interpolating AMI description: %s", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Construct the modify image and snapshot attribute requests we're going // Construct the modify image and snapshot attribute requests we're going
// to make. We need to make each separately since the EC2 API only allows // to make. We need to make each separately since the EC2 API only allows
// changing one type at a kind currently. // changing one type at a kind currently.

View File

@ -13,6 +13,7 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
) )
type StepRunSourceInstance struct { type StepRunSourceInstance struct {
@ -32,6 +33,7 @@ type StepRunSourceInstance struct {
Tags map[string]string Tags map[string]string
UserData string UserData string
UserDataFile string UserDataFile string
Ctx interpolate.Context
instanceId string instanceId string
spotRequest *ec2.SpotInstanceRequest spotRequest *ec2.SpotInstanceRequest
@ -275,7 +277,29 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
if _, exists := s.Tags["Name"]; !exists { if _, exists := s.Tags["Name"]; !exists {
s.Tags["Name"] = "Packer Builder" s.Tags["Name"] = "Packer Builder"
} }
ec2Tags := ConvertToEC2Tags(s.Tags)
ec2Tags := make([]*ec2.Tag, 1, len(s.Tags))
for k, v := range s.Tags {
s.Ctx.Data = &BuildInfoTemplate{
SourceAMI: s.SourceAMI,
BuildRegion: *ec2conn.Config.Region,
}
interpolatedValue, err := interpolate.Render(v, &s.Ctx)
if err != nil {
err = fmt.Errorf("Error processing tag: %s:%s - %s", k, v, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ec2Tags = append(ec2Tags, &ec2.Tag{Key: aws.String(k), Value: aws.String(interpolatedValue)})
}
ec2Tags, err = ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx, ui)
if err != nil {
err := fmt.Errorf("Error tagging source instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{ _, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Tags: ec2Tags, Tags: ec2Tags,

View File

@ -44,6 +44,15 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{ err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true, Interpolate: true,
InterpolateContext: &b.config.ctx, InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"ami_description",
"run_tags",
"run_volume_tags",
"snapshot_tags",
"tags",
},
},
}, raws...) }, raws...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -137,10 +146,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AvailabilityZone: b.config.AvailabilityZone, AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices, BlockDevices: b.config.BlockDevices,
Tags: b.config.RunTags, Tags: b.config.RunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}, },
&stepTagEBSVolumes{ &stepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags, VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
}, },
&awscommon.StepGetPassword{ &awscommon.StepGetPassword{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
@ -184,10 +195,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags, SnapshotTags: b.config.SnapshotTags,
Ctx: b.config.ctx,
}, },
} }

View File

@ -7,15 +7,18 @@ import (
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
) )
type stepTagEBSVolumes struct { type stepTagEBSVolumes struct {
VolumeRunTags map[string]string VolumeRunTags map[string]string
Ctx interpolate.Context
} }
func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance) instance := state.Get("instance").(*ec2.Instance)
sourceAMI := state.Get("source_image").(*ec2.Image)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
if len(s.VolumeRunTags) == 0 { if len(s.VolumeRunTags) == 0 {
@ -33,10 +36,32 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
ui.Say("Adding tags to source EBS Volumes") tags := make([]*ec2.Tag, len(s.VolumeRunTags))
tags := common.ConvertToEC2Tags(s.VolumeRunTags) for key, value := range s.VolumeRunTags {
s.Ctx.Data = &common.BuildInfoTemplate{
SourceAMI: *sourceAMI.ImageId,
BuildRegion: *ec2conn.Config.Region,
}
interpolatedValue, err := interpolate.Render(value, &s.Ctx)
if err != nil {
err = fmt.Errorf("Error processing volume tag: %s:%s - %s", key, value, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
tags = append(tags, &ec2.Tag{Key: &key, Value: &interpolatedValue})
}
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{ ui.Say("Adding tags to source EBS Volumes")
tags, err := common.ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx, ui)
if err != nil {
err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Resources: volumeIds, Resources: volumeIds,
Tags: tags, Tags: tags,
}) })

View File

@ -41,6 +41,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{ err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true, Interpolate: true,
InterpolateContext: &b.config.ctx, InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_tags",
"tags",
},
},
}, raws...) }, raws...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -128,10 +134,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AvailabilityZone: b.config.AvailabilityZone, AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: launchBlockDevices, BlockDevices: launchBlockDevices,
Tags: b.config.RunTags, Tags: b.config.RunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
}, },
&stepTagEBSVolumes{ &stepTagEBSVolumes{
VolumeMapping: b.config.VolumeMappings, VolumeMapping: b.config.VolumeMappings,
Ctx: b.config.ctx,
}, },
&awscommon.StepGetPassword{ &awscommon.StepGetPassword{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,

View File

@ -6,16 +6,20 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
) )
type stepTagEBSVolumes struct { type stepTagEBSVolumes struct {
VolumeMapping []BlockDevice VolumeMapping []BlockDevice
Ctx interpolate.Context
} }
func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance) instance := state.Get("instance").(*ec2.Instance)
sourceAMI := state.Get("source_image").(*ec2.Image)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
volumes := make(EbsVolumes) volumes := make(EbsVolumes)
@ -42,9 +46,21 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
tags := make([]*ec2.Tag, 0, len(mapping.Tags)) tags := make([]*ec2.Tag, 0, len(mapping.Tags))
for key, value := range mapping.Tags { for key, value := range mapping.Tags {
s.Ctx.Data = &awscommon.BuildInfoTemplate{
SourceAMI: *sourceAMI.ImageId,
BuildRegion: *ec2conn.Config.Region,
}
interpolatedValue, err := interpolate.Render(value, &s.Ctx)
if err != nil {
err = fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
tags = append(tags, &ec2.Tag{ tags = append(tags, &ec2.Tag{
Key: aws.String(fmt.Sprintf("%s", key)), Key: aws.String(fmt.Sprintf("%s", key)),
Value: aws.String(fmt.Sprintf("%s", value)), Value: aws.String(fmt.Sprintf("%s", interpolatedValue)),
}) })
} }

View File

@ -63,8 +63,13 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
InterpolateContext: &b.config.ctx, InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{ InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{ Exclude: []string{
"ami_description",
"bundle_upload_command", "bundle_upload_command",
"bundle_vol_command", "bundle_vol_command",
"run_tags",
"run_volume_tags",
"snapshot_tags",
"tags",
}, },
}, },
}, configs...) }, configs...)
@ -223,6 +228,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
AvailabilityZone: b.config.AvailabilityZone, AvailabilityZone: b.config.AvailabilityZone,
BlockDevices: b.config.BlockDevices, BlockDevices: b.config.BlockDevices,
Tags: b.config.RunTags, Tags: b.config.RunTags,
Ctx: b.config.ctx,
}, },
&awscommon.StepGetPassword{ &awscommon.StepGetPassword{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
@ -265,10 +271,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers, SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups, SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags, SnapshotTags: b.config.SnapshotTags,
Ctx: b.config.ctx,
}, },
} }