Merge pull request #4399 from rickard-von-essen/aws-interpolate

WIP: builder/amazon: Interpolate {{ .BuildRegion }} and {{ .SourceAMI }}
This commit is contained in:
Matthew Hooker 2017-01-26 11:41:16 -08:00 committed by GitHub
commit 76f897077d
14 changed files with 187 additions and 31 deletions

View File

@ -64,6 +64,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"ami_description",
"snapshot_tags",
"tags",
"command_wrapper",
"post_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,
SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
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

@ -11,11 +11,13 @@ import (
"github.com/mitchellh/multistep"
retry "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepCreateTags struct {
Tags map[string]string
SnapshotTags map[string]string
Ctx interpolate.Context
}
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
@ -23,6 +25,13 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
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 {
return multistep.ActionContinue
}
@ -79,9 +88,20 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
// Convert tags to ec2.Tag format
ui.Say("Creating AMI tags")
amiTags := ConvertToEC2Tags(s.Tags)
amiTags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, sourceAMI, s.Ctx)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Creating snapshot tags")
snapshotTags := ConvertToEC2Tags(s.SnapshotTags)
snapshotTags, err := ConvertToEC2Tags(s.SnapshotTags, *ec2conn.Config.Region, sourceAMI, s.Ctx)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func() (bool, error) {
@ -130,14 +150,25 @@ func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup...
}
func ConvertToEC2Tags(tags map[string]string) []*ec2.Tag {
var ec2tags []*ec2.Tag
func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx interpolate.Context) ([]*ec2.Tag, error) {
var ec2Tags []*ec2.Tag
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 ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
}
log.Printf("Adding tag: \"%s\": \"%s\"", key, interpolatedValue)
ec2Tags = append(ec2Tags, &ec2.Tag{
Key: aws.String(key),
Value: aws.String(value),
Value: aws.String(interpolatedValue),
})
}
return ec2tags
return ec2Tags, nil
}

View File

@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepModifyAMIAttributes struct {
@ -17,12 +18,20 @@ type StepModifyAMIAttributes struct {
SnapshotGroups []string
ProductCodes []string
Description string
Ctx interpolate.Context
}
func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
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)
// Determine if there is any work to do.
@ -38,6 +47,18 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc
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
// to make. We need to make each separately since the EC2 API only allows
// changing one type at a kind currently.

View File

@ -13,6 +13,7 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepRunSourceInstance struct {
@ -32,6 +33,7 @@ type StepRunSourceInstance struct {
Tags map[string]string
UserData string
UserDataFile string
Ctx interpolate.Context
instanceId string
spotRequest *ec2.SpotInstanceRequest
@ -275,7 +277,14 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
if _, exists := s.Tags["Name"]; !exists {
s.Tags["Name"] = "Packer Builder"
}
ec2Tags := ConvertToEC2Tags(s.Tags)
ec2Tags, err := ConvertToEC2Tags(s.Tags, *ec2conn.Config.Region, s.SourceAMI, s.Ctx)
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{
Tags: ec2Tags,

View File

@ -44,6 +44,15 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"ami_description",
"run_tags",
"run_volume_tags",
"snapshot_tags",
"tags",
},
},
}, raws...)
if err != nil {
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,
BlockDevices: b.config.BlockDevices,
Tags: b.config.RunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
},
&stepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
},
&awscommon.StepGetPassword{
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,
SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups,
Ctx: b.config.ctx,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
SnapshotTags: b.config.SnapshotTags,
Ctx: b.config.ctx,
},
}

View File

@ -7,15 +7,18 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type stepTagEBSVolumes struct {
VolumeRunTags map[string]string
Ctx interpolate.Context
}
func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
sourceAMI := state.Get("source_image").(*ec2.Image)
ui := state.Get("ui").(packer.Ui)
if len(s.VolumeRunTags) == 0 {
@ -34,9 +37,15 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
}
ui.Say("Adding tags to source EBS Volumes")
tags := common.ConvertToEC2Tags(s.VolumeRunTags)
tags, err := common.ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx)
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{
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Resources: volumeIds,
Tags: tags,
})

View File

@ -41,6 +41,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_tags",
"ebs_volumes",
},
},
}, raws...)
if err != nil {
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,
BlockDevices: launchBlockDevices,
Tags: b.config.RunTags,
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
},
&stepTagEBSVolumes{
VolumeMapping: b.config.VolumeMappings,
Ctx: b.config.ctx,
},
&awscommon.StepGetPassword{
Debug: b.config.PackerDebug,

View File

@ -3,19 +3,22 @@ package ebsvolume
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type stepTagEBSVolumes struct {
VolumeMapping []BlockDevice
Ctx interpolate.Context
}
func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
sourceAMI := state.Get("source_image").(*ec2.Image)
ui := state.Get("ui").(packer.Ui)
volumes := make(EbsVolumes)
@ -40,12 +43,12 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
continue
}
tags := make([]*ec2.Tag, 0, len(mapping.Tags))
for key, value := range mapping.Tags {
tags = append(tags, &ec2.Tag{
Key: aws.String(fmt.Sprintf("%s", key)),
Value: aws.String(fmt.Sprintf("%s", value)),
})
tags, err := awscommon.ConvertToEC2Tags(mapping.Tags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx)
if err != nil {
err := fmt.Errorf("Error tagging device %s with %s", mapping.DeviceName, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
for _, v := range instance.BlockDeviceMappings {

View File

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

View File

@ -75,7 +75,11 @@ each category, the available configuration keys are alphabetized.
### Optional:
- `ami_description` (string) - The description to set for the
resulting AMI(s). By default this description is empty.
resulting AMI(s). By default this description is empty. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with name of the region where this
is built.
- `ami_groups` (array of strings) - A list of groups that have access to
launch the resulting AMI(s). By default no groups have permission to launch
@ -210,7 +214,11 @@ each category, the available configuration keys are alphabetized.
validation of the `ami_regions` configuration option. Default `false`.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
They will override AMI tags if already applied to snapshot. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with name of the region where this
is built.
- `snapshot_groups` (array of strings) - A list of groups that have access to
create volumes from the snapshot(s). By default no groups have permission to create
@ -250,7 +258,12 @@ each category, the available configuration keys are alphabetized.
- `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build.
- `tags` (object of key/value strings) - Tags applied to the AMI.
- `tags` (object of key/value strings) - Tags applied to the AMI. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with name of the region where this
is built.
## Basic Example

View File

@ -105,7 +105,10 @@ builder.
volumes
- `ami_description` (string) - The description to set for the
resulting AMI(s). By default this description is empty.
resulting AMI(s). By default this description is empty. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `ami_groups` (array of strings) - A list of groups that have access to
launch the resulting AMI(s). By default no groups have permission to launch
@ -182,11 +185,17 @@ builder.
- `run_tags` (object of key/value strings) - Tags to apply to the instance
that is *launched* to create the AMI. These tags are *not* applied to the
resulting AMI unless they're duplicated in `tags`.
resulting AMI unless they're duplicated in `tags`. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `run_volume_tags` (object of key/value strings) - Tags to apply to the volumes
that are *launched* to create the AMI. These tags are *not* applied to the
resulting AMI unless they're duplicated in `tags`.
resulting AMI unless they're duplicated in `tags`. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `security_group_id` (string) - The ID (*not* the name) of the security group
to assign to the instance. By default this is not set and Packer will
@ -214,7 +223,10 @@ builder.
user creating the AMI has permissions to create volumes from the backing snapshot(s).
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
They will override AMI tags if already applied to snapshot. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
Example:
@ -283,7 +295,10 @@ builder.
required if you are using an non-default VPC.
- `tags` (object of key/value strings) - Tags applied to the AMI and
relevant snapshots.
relevant snapshots. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `temporary_key_pair_name` (string) - The name of the temporary keypair
to generate. By default, Packer generates a name with a UUID.

View File

@ -79,7 +79,10 @@ builder.
volumes, `io1` for Provisioned IOPS (SSD) volumes, and `standard` for Magnetic
volumes
- `tags` (map) - Tags to apply to the volume. These are retained after the
builder completes.
builder completes. This is a [configuration template]
(/docs/templates/configuration-templates.html) where the `SourceAMI`
variable is replaced with the source AMI ID and `BuildRegion` variable
is replaced with the value of `region`.
- `associate_public_ip_address` (boolean) - If using a non-default VPC, public
IP addresses are not provided by default. If this is toggled, your new
@ -102,7 +105,10 @@ builder.
- `run_tags` (object of key/value strings) - Tags to apply to the instance
that is *launched* to create the AMI. These tags are *not* applied to the
resulting AMI unless they're duplicated in `tags`.
resulting AMI unless they're duplicated in `tags`. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `security_group_id` (string) - The ID (*not* the name) of the security group
to assign to the instance. By default this is not set and Packer will

View File

@ -127,7 +127,10 @@ builder.
volumes
- `ami_description` (string) - The description to set for the
resulting AMI(s). By default this description is empty.
resulting AMI(s). By default this description is empty. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `ami_groups` (array of strings) - A list of groups that have access to
launch the resulting AMI(s). By default no groups have permission to launch
@ -198,7 +201,10 @@ builder.
- `run_tags` (object of key/value strings) - Tags to apply to the instance
that is *launched* to create the AMI. These tags are *not* applied to the
resulting AMI unless they're duplicated in `tags`.
resulting AMI unless they're duplicated in `tags`. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `security_group_id` (string) - The ID (*not* the name) of the security group
to assign to the instance. By default this is not set and Packer will
@ -290,7 +296,10 @@ builder.
`subnet-12345def`, where Packer will launch the EC2 instance. This field is
required if you are using an non-default VPC.
- `tags` (object of key/value strings) - Tags applied to the AMI.
- `tags` (object of key/value strings) - Tags applied to the AMI. This is a
[configuration template](/docs/templates/configuration-templates.html)
where the `SourceAMI` variable is replaced with the source AMI ID and
`BuildRegion` variable is replaced with the value of `region`.
- `temporary_key_pair_name` (string) - The name of the temporary key pair
to generate. By default, Packer generates a name with an UUID.