mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-22 07:51:39 -04:00
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.
259 lines
8.1 KiB
Go
259 lines
8.1 KiB
Go
// The ebssurrogate package contains a packer.Builder implementation that
|
|
// builds a new EBS-backed AMI using an ephemeral instance.
|
|
package ebssurrogate
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/ec2"
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/mitchellh/multistep"
|
|
awscommon "github.com/mitchellh/packer/builder/amazon/common"
|
|
"github.com/mitchellh/packer/common"
|
|
"github.com/mitchellh/packer/helper/communicator"
|
|
"github.com/mitchellh/packer/helper/config"
|
|
"github.com/mitchellh/packer/packer"
|
|
"github.com/mitchellh/packer/template/interpolate"
|
|
)
|
|
|
|
const BuilderId = "mitchellh.amazon.ebssurrogate"
|
|
|
|
type Config struct {
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
awscommon.AccessConfig `mapstructure:",squash"`
|
|
awscommon.RunConfig `mapstructure:",squash"`
|
|
awscommon.BlockDevices `mapstructure:",squash"`
|
|
awscommon.AMIConfig `mapstructure:",squash"`
|
|
|
|
RootDevice RootBlockDevice `mapstructure:"ami_root_device"`
|
|
VolumeRunTags map[string]string `mapstructure:"run_volume_tags"`
|
|
|
|
ctx interpolate.Context
|
|
}
|
|
|
|
type Builder struct {
|
|
config Config
|
|
runner multistep.Runner
|
|
}
|
|
|
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
|
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
|
|
}
|
|
|
|
// Accumulate any errors
|
|
var errs *packer.MultiError
|
|
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.RootDevice.Prepare(&b.config.ctx)...)
|
|
|
|
if b.config.AMIVirtType == "" {
|
|
errs = packer.MultiErrorAppend(errs, errors.New("ami_virtualization_type is required."))
|
|
}
|
|
|
|
foundRootVolume := false
|
|
for _, launchDevice := range b.config.BlockDevices.LaunchMappings {
|
|
if launchDevice.DeviceName == b.config.RootDevice.SourceDeviceName {
|
|
foundRootVolume = true
|
|
}
|
|
}
|
|
|
|
if !foundRootVolume {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName))
|
|
}
|
|
|
|
if errs != nil && len(errs.Errors) > 0 {
|
|
return nil, errs
|
|
}
|
|
|
|
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
|
|
return nil, nil
|
|
}
|
|
|
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
|
awsConfig, err := b.config.Config()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
awsSession, err := session.NewSession(awsConfig)
|
|
if err != nil {
|
|
return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err)
|
|
}
|
|
|
|
ec2conn := ec2.New(awsSession)
|
|
|
|
// If the subnet is specified but not the AZ, try to determine the AZ automatically
|
|
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
|
|
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
|
|
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
|
|
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
|
|
}
|
|
|
|
// Setup the state bag and initial state for the steps
|
|
state := new(multistep.BasicStateBag)
|
|
state.Put("config", &b.config)
|
|
state.Put("ec2", ec2conn)
|
|
state.Put("hook", hook)
|
|
state.Put("ui", ui)
|
|
|
|
// 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,
|
|
AmiFilters: b.config.SourceAmiFilter,
|
|
},
|
|
&awscommon.StepKeyPair{
|
|
Debug: b.config.PackerDebug,
|
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
|
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
|
KeyPairName: b.config.SSHKeyPairName,
|
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
|
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
|
},
|
|
&awscommon.StepSecurityGroup{
|
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
|
CommConfig: &b.config.RunConfig.Comm,
|
|
VpcId: b.config.VpcId,
|
|
},
|
|
&awscommon.StepRunSourceInstance{
|
|
Debug: b.config.PackerDebug,
|
|
ExpectedRootDevice: "ebs",
|
|
SpotPrice: b.config.SpotPrice,
|
|
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
|
InstanceType: b.config.InstanceType,
|
|
UserData: b.config.UserData,
|
|
UserDataFile: b.config.UserDataFile,
|
|
SourceAMI: b.config.SourceAmi,
|
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
|
SubnetId: b.config.SubnetId,
|
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
|
EbsOptimized: b.config.EbsOptimized,
|
|
AvailabilityZone: b.config.AvailabilityZone,
|
|
BlockDevices: b.config.BlockDevices,
|
|
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,
|
|
Timeout: b.config.WindowsPasswordTimeout,
|
|
},
|
|
&communicator.StepConnect{
|
|
Config: &b.config.RunConfig.Comm,
|
|
Host: awscommon.SSHHost(
|
|
ec2conn,
|
|
b.config.SSHPrivateIp),
|
|
SSHConfig: awscommon.SSHConfig(
|
|
b.config.RunConfig.Comm.SSHAgentAuth,
|
|
b.config.RunConfig.Comm.SSHUsername,
|
|
b.config.RunConfig.Comm.SSHPassword),
|
|
},
|
|
&common.StepProvision{},
|
|
&awscommon.StepStopEBSBackedInstance{
|
|
SpotPrice: b.config.SpotPrice,
|
|
DisableStopInstance: b.config.DisableStopInstance,
|
|
},
|
|
&awscommon.StepModifyEBSBackedInstance{
|
|
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
|
},
|
|
&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!
|
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
|
b.runner.Run(state)
|
|
|
|
// If there was an error, return that
|
|
if rawErr, ok := state.GetOk("error"); ok {
|
|
return nil, rawErr.(error)
|
|
}
|
|
|
|
if amis, ok := state.GetOk("amis"); ok {
|
|
// Build the artifact and return it
|
|
artifact := &awscommon.Artifact{
|
|
Amis: amis.(map[string]string),
|
|
BuilderIdValue: BuilderId,
|
|
Conn: ec2conn,
|
|
}
|
|
|
|
return artifact, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (b *Builder) Cancel() {
|
|
if b.runner != nil {
|
|
log.Println("Cancelling the step runner...")
|
|
b.runner.Cancel()
|
|
}
|
|
}
|