build/amazon-ebssurrogate: Add region copy, attributes, tags steps

As pointed out in the initial code review of #4351, some of the steps
from the standard EBS builder were (intetionally) omitted. It turns out
that these actually are useful, and the original rationale for the
omission was wrong. Consequently, this commit adds in the following
steps:

- `StepPrevalidate`
- `StepTagEBSVolumes`
- `StepDeregisterAMI`
- `StepCreateEncryptedAMICopy`
- `StepAMIRegionCopy`
- `StepModifyAMIAttribute`
- `StepCreateTags`

We also fix the interpolation filter and documentation to reflect these
additions, though the majority were already documented and just not
functional.
This commit is contained in:
James Nugent 2017-02-27 07:51:38 -06:00
parent cc6abb0fc7
commit e856339309
5 changed files with 95 additions and 9 deletions

View File

@ -1,21 +1,20 @@
package ebs
package common
import (
"fmt"
"github.com/aws/aws-sdk-go/service/ec2"
"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 {
type StepTagEBSVolumes struct {
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)
instance := state.Get("instance").(*ec2.Instance)
sourceAMI := state.Get("source_image").(*ec2.Image)
@ -37,7 +36,7 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
}
ui.Say("Adding tags to source EBS Volumes")
tags, err := common.ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx)
tags, err := 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)
@ -59,6 +58,6 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepTagEBSVolumes) Cleanup(state multistep.StateBag) {
func (s *StepTagEBSVolumes) Cleanup(state multistep.StateBag) {
// No cleanup...
}

View File

@ -149,7 +149,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Ctx: b.config.ctx,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
},
&stepTagEBSVolumes{
&awscommon.StepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
},

View File

@ -29,6 +29,7 @@ type Config struct {
awscommon.AMIConfig `mapstructure:",squash"`
RootDevice RootBlockDevice `mapstructure:"ami_root_device"`
VolumeRunTags map[string]string `mapstructure:"run_volume_tags"`
ctx interpolate.Context
}
@ -47,6 +48,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
Exclude: []string{
"ami_description",
"run_tags",
"run_volume_tags",
"snapshot_tags",
"tags",
},
},
@ -119,6 +122,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
// Build the steps
steps := []multistep.Step{
&awscommon.StepPreValidate{
DestAmiName: b.config.AMIName,
ForceDeregister: b.config.AMIForceDeregister,
},
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
@ -155,6 +162,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Tags: b.config.RunTags,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
},
&awscommon.StepTagEBSVolumes{
VolumeRunTags: b.config.VolumeRunTags,
Ctx: b.config.ctx,
},
&awscommon.StepGetPassword{
Debug: b.config.PackerDebug,
Comm: &b.config.RunConfig.Comm,
@ -181,10 +192,39 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepSnapshotNewRootVolume{
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
},
&awscommon.StepDeregisterAMI{
ForceDeregister: b.config.AMIForceDeregister,
ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot,
AMIName: b.config.AMIName,
},
&StepRegisterAMI{
RootDevice: b.config.RootDevice,
BlockDevices: b.config.BlockDevices.BuildLaunchDevices(),
},
&awscommon.StepCreateEncryptedAMICopy{
KeyID: b.config.AMIKmsKeyId,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
Name: b.config.AMIName,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
Users: b.config.AMIUsers,
Groups: b.config.AMIGroups,
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,
},
}
// Run!

View File

@ -14,6 +14,7 @@ import (
type StepRegisterAMI struct {
RootDevice RootBlockDevice
BlockDevices []*ec2.BlockDeviceMapping
image *ec2.Image
}
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
@ -74,7 +75,45 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{registerResp.ImageId}})
if err != nil {
err := fmt.Errorf("Error searching for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.image = imagesResp.Images[0]
snapshots := make(map[string][]string)
for _, blockDeviceMapping := range imagesResp.Images[0].BlockDeviceMappings {
if blockDeviceMapping.Ebs != nil && blockDeviceMapping.Ebs.SnapshotId != nil {
snapshots[*ec2conn.Config.Region] = append(snapshots[*ec2conn.Config.Region], *blockDeviceMapping.Ebs.SnapshotId)
}
}
state.Put("snapshots", snapshots)
return multistep.ActionContinue
}
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {}
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {
if s.image == nil {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deregistering the AMI because cancelation or error...")
deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
return
}
}

View File

@ -163,6 +163,14 @@ builder.
- `force_delete_snapshot` (boolean) - Force Packer to delete snapshots associated with
AMIs, which have been deregistered by `force_deregister`. Default `false`.
- `encrypt_boot` (boolean) - Instruct packer to automatically create a copy of the
AMI with an encrypted boot volume (discarding the initial unencrypted AMI in the
process). Default `false`.
- `kms_key_id` (string) - The ID of the KMS key to use for boot volume encryption.
This only applies to the main `region`, other regions where the AMI will be copied
will be encrypted by the default EBS KMS key.
- `iam_instance_profile` (string) - The name of an [IAM instance
profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
to launch the EC2 instance with.