packer-cn/builder/amazon/ebsvolume/builder.go

185 lines
5.7 KiB
Go
Raw Normal View History

builder/amazon: Add `ebs-volume` builder This commit adds a builder that works like EBS builders, except does not create an AMI, and instead is intended to create EBS volumes in an initialized state. For example, the following template can be used to create and export a set of 3 EBS Volumes in a ZFS zpool named `data` for importing by instances running production systems: ``` { "variables": { "aws_access_key_id": "{{ env `AWS_ACCESS_KEY_ID` }}", "aws_secret_access_key": "{{ env `AWS_SECRET_ACCESS_KEY` }}", "region": "{{ env `AWS_REGION` }}", "source_ami": "{{ env `PACKER_SOURCE_AMI` }}", "vpc_id": "{{ env `PACKER_VPC_ID` }}", "subnet_id": "{{ env `PACKER_SUBNET_ID` }}" }, "builders": [{ "type": "amazon-ebs-volume", "access_key": "{{ user `aws_access_key_id` }}", "secret_key": "{{ user `aws_secret_access_key` }}", "region": "{{user `region`}}", "spot_price_auto_product": "Linux/UNIX (Amazon VPC)", "ssh_pty": true, "instance_type": "t2.medium", "vpc_id": "{{user `vpc_id` }}", "subnet_id": "{{user `subnet_id` }}", "associate_public_ip_address": true, "source_ami": "{{user `source_ami` }}", "ssh_username": "ubuntu", "ssh_timeout": "5m", "ebs_volumes": [ { "device_name": "/dev/xvdf", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data1", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdg", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data2", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdh", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data3", "zpool": "data", "Component": "TeamCity" } } ] }], "provisioners": [ { "type": "shell", "start_retry_timeout": "10m", "inline": [ "DEBIAN_FRONTEND=noninteractive sudo apt-get update", "DEBIAN_FRONTEND=noninteractive sudo apt-get install -y zfs", "lsblk", "sudo parted /dev/xvdf --script mklabel GPT", "sudo parted /dev/xvdg --script mklabel GPT", "sudo parted /dev/xvdh --script mklabel GPT", "sudo zpool create -m none data raidz xvdf xvdg xvdh", "sudo zpool status", "sudo zpool export data", "sudo zpool status" ] } ] } ``` StepModifyInstance and StepStopInstance are now shared between EBS and EBS-Volume builders - move them into the AWS common directory and rename them to indicate that they only apply to EBS-backed builders.
2016-10-31 17:44:41 -04:00
// The ebsvolume package contains a packer.Builder implementation that
// builds EBS volumes for Amazon EC2 using an ephemeral instance,
package ebsvolume
import (
"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"
)
2016-11-09 17:42:26 -05:00
const BuilderId = "mitchellh.amazon.ebsvolume"
builder/amazon: Add `ebs-volume` builder This commit adds a builder that works like EBS builders, except does not create an AMI, and instead is intended to create EBS volumes in an initialized state. For example, the following template can be used to create and export a set of 3 EBS Volumes in a ZFS zpool named `data` for importing by instances running production systems: ``` { "variables": { "aws_access_key_id": "{{ env `AWS_ACCESS_KEY_ID` }}", "aws_secret_access_key": "{{ env `AWS_SECRET_ACCESS_KEY` }}", "region": "{{ env `AWS_REGION` }}", "source_ami": "{{ env `PACKER_SOURCE_AMI` }}", "vpc_id": "{{ env `PACKER_VPC_ID` }}", "subnet_id": "{{ env `PACKER_SUBNET_ID` }}" }, "builders": [{ "type": "amazon-ebs-volume", "access_key": "{{ user `aws_access_key_id` }}", "secret_key": "{{ user `aws_secret_access_key` }}", "region": "{{user `region`}}", "spot_price_auto_product": "Linux/UNIX (Amazon VPC)", "ssh_pty": true, "instance_type": "t2.medium", "vpc_id": "{{user `vpc_id` }}", "subnet_id": "{{user `subnet_id` }}", "associate_public_ip_address": true, "source_ami": "{{user `source_ami` }}", "ssh_username": "ubuntu", "ssh_timeout": "5m", "ebs_volumes": [ { "device_name": "/dev/xvdf", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data1", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdg", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data2", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdh", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data3", "zpool": "data", "Component": "TeamCity" } } ] }], "provisioners": [ { "type": "shell", "start_retry_timeout": "10m", "inline": [ "DEBIAN_FRONTEND=noninteractive sudo apt-get update", "DEBIAN_FRONTEND=noninteractive sudo apt-get install -y zfs", "lsblk", "sudo parted /dev/xvdf --script mklabel GPT", "sudo parted /dev/xvdg --script mklabel GPT", "sudo parted /dev/xvdh --script mklabel GPT", "sudo zpool create -m none data raidz xvdf xvdg xvdh", "sudo zpool status", "sudo zpool export data", "sudo zpool status" ] } ] } ``` StepModifyInstance and StepStopInstance are now shared between EBS and EBS-Volume builders - move them into the AWS common directory and rename them to indicate that they only apply to EBS-backed builders.
2016-10-31 17:44:41 -04:00
type Config struct {
common.PackerConfig `mapstructure:",squash"`
awscommon.AccessConfig `mapstructure:",squash"`
awscommon.RunConfig `mapstructure:",squash"`
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"`
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,
}, 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)...)
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) {
config, err := b.config.Config()
if err != nil {
return nil, err
}
session, err := session.NewSession(config)
if err != nil {
return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err)
}
ec2conn := ec2.New(session)
// 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)
launchBlockDevices := commonBlockDevices(b.config.VolumeMappings)
// Build the steps
steps := []multistep.Step{
&awscommon.StepSourceAMIInfo{
SourceAmi: b.config.SourceAmi,
EnhancedNetworking: b.config.AMIEnhancedNetworking,
AmiFilters: b.config.SourceAmiFilter,
},
&awscommon.StepKeyPair{
Debug: b.config.PackerDebug,
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: launchBlockDevices,
Tags: b.config.RunTags,
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
},
&stepTagEBSVolumes{
VolumeMapping: b.config.VolumeMappings,
},
&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,
},
}
// 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)
}
2016-11-09 17:42:26 -05:00
// Build the artifact and return it
artifact := &Artifact{
Volumes: state.Get("ebsvolumes").(EbsVolumes),
BuilderIdValue: BuilderId,
Conn: ec2conn,
}
ui.Say(fmt.Sprintf("Created Volumes: %s", artifact))
return artifact, nil
builder/amazon: Add `ebs-volume` builder This commit adds a builder that works like EBS builders, except does not create an AMI, and instead is intended to create EBS volumes in an initialized state. For example, the following template can be used to create and export a set of 3 EBS Volumes in a ZFS zpool named `data` for importing by instances running production systems: ``` { "variables": { "aws_access_key_id": "{{ env `AWS_ACCESS_KEY_ID` }}", "aws_secret_access_key": "{{ env `AWS_SECRET_ACCESS_KEY` }}", "region": "{{ env `AWS_REGION` }}", "source_ami": "{{ env `PACKER_SOURCE_AMI` }}", "vpc_id": "{{ env `PACKER_VPC_ID` }}", "subnet_id": "{{ env `PACKER_SUBNET_ID` }}" }, "builders": [{ "type": "amazon-ebs-volume", "access_key": "{{ user `aws_access_key_id` }}", "secret_key": "{{ user `aws_secret_access_key` }}", "region": "{{user `region`}}", "spot_price_auto_product": "Linux/UNIX (Amazon VPC)", "ssh_pty": true, "instance_type": "t2.medium", "vpc_id": "{{user `vpc_id` }}", "subnet_id": "{{user `subnet_id` }}", "associate_public_ip_address": true, "source_ami": "{{user `source_ami` }}", "ssh_username": "ubuntu", "ssh_timeout": "5m", "ebs_volumes": [ { "device_name": "/dev/xvdf", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data1", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdg", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data2", "zpool": "data", "Component": "TeamCity" } }, { "device_name": "/dev/xvdh", "delete_on_termination": false, "volume_size": 10, "volume_type": "gp2", "tags": { "Name": "TeamCity-Data3", "zpool": "data", "Component": "TeamCity" } } ] }], "provisioners": [ { "type": "shell", "start_retry_timeout": "10m", "inline": [ "DEBIAN_FRONTEND=noninteractive sudo apt-get update", "DEBIAN_FRONTEND=noninteractive sudo apt-get install -y zfs", "lsblk", "sudo parted /dev/xvdf --script mklabel GPT", "sudo parted /dev/xvdg --script mklabel GPT", "sudo parted /dev/xvdh --script mklabel GPT", "sudo zpool create -m none data raidz xvdf xvdg xvdh", "sudo zpool status", "sudo zpool export data", "sudo zpool status" ] } ] } ``` StepModifyInstance and StepStopInstance are now shared between EBS and EBS-Volume builders - move them into the AWS common directory and rename them to indicate that they only apply to EBS-backed builders.
2016-10-31 17:44:41 -04:00
}
func (b *Builder) Cancel() {
if b.runner != nil {
log.Println("Cancelling the step runner...")
b.runner.Cancel()
}
}