Merge pull request #3855 from jeremy-asher/chroot-from-scratch
amazon-chroot build AMI from scratch
This commit is contained in:
commit
ed1b664d65
|
@ -25,19 +25,24 @@ const BuilderId = "mitchellh.amazon.chroot"
|
|||
// Config is the configuration that is chained through the steps and
|
||||
// settable from the template.
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
awscommon.AccessConfig `mapstructure:",squash"`
|
||||
awscommon.AMIConfig `mapstructure:",squash"`
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
awscommon.AMIBlockDevices `mapstructure:",squash"`
|
||||
awscommon.AMIConfig `mapstructure:",squash"`
|
||||
awscommon.AccessConfig `mapstructure:",squash"`
|
||||
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
CommandWrapper string `mapstructure:"command_wrapper"`
|
||||
CopyFiles []string `mapstructure:"copy_files"`
|
||||
DevicePath string `mapstructure:"device_path"`
|
||||
MountPath string `mapstructure:"mount_path"`
|
||||
SourceAmi string `mapstructure:"source_ami"`
|
||||
RootVolumeSize int64 `mapstructure:"root_volume_size"`
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition int `mapstructure:"mount_partition"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
CommandWrapper string `mapstructure:"command_wrapper"`
|
||||
CopyFiles []string `mapstructure:"copy_files"`
|
||||
DevicePath string `mapstructure:"device_path"`
|
||||
FromScratch bool `mapstructure:"from_scratch"`
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition int `mapstructure:"mount_partition"`
|
||||
MountPath string `mapstructure:"mount_path"`
|
||||
PostMountCommands []string `mapstructure:"post_mount_commands"`
|
||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||
RootDeviceName string `mapstructure:"root_device_name"`
|
||||
RootVolumeSize int64 `mapstructure:"root_volume_size"`
|
||||
SourceAmi string `mapstructure:"source_ami"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
@ -59,6 +64,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"command_wrapper",
|
||||
"post_mount_commands",
|
||||
"pre_mount_commands",
|
||||
"mount_path",
|
||||
},
|
||||
},
|
||||
|
@ -86,7 +93,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if len(b.config.CopyFiles) == 0 {
|
||||
if len(b.config.CopyFiles) == 0 && !b.config.FromScratch {
|
||||
b.config.CopyFiles = []string{"/etc/resolv.conf"}
|
||||
}
|
||||
|
||||
|
@ -102,8 +109,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
b.config.MountPartition = 1
|
||||
}
|
||||
|
||||
// Accumulate any errors
|
||||
// Accumulate any errors or warnings
|
||||
var errs *packer.MultiError
|
||||
var warns []string
|
||||
|
||||
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
|
@ -115,16 +124,49 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if b.config.SourceAmi == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("source_ami is required."))
|
||||
if b.config.FromScratch {
|
||||
if b.config.SourceAmi != "" {
|
||||
warns = append(warns, "source_ami is unused when from_scratch is true")
|
||||
}
|
||||
if b.config.RootVolumeSize == 0 {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("root_volume_size is required with from_scratch."))
|
||||
}
|
||||
if len(b.config.PreMountCommands) == 0 {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("pre_mount_commands is required with from_scratch."))
|
||||
}
|
||||
if b.config.AMIVirtType == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("ami_virtualization_type is required with from_scratch."))
|
||||
}
|
||||
if b.config.RootDeviceName == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("root_device_name is required with from_scratch."))
|
||||
}
|
||||
if len(b.config.AMIMappings) == 0 {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("ami_block_device_mappings is required with from_scratch."))
|
||||
}
|
||||
} else {
|
||||
if b.config.SourceAmi == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("source_ami is required."))
|
||||
}
|
||||
if len(b.config.AMIMappings) != 0 {
|
||||
warns = append(warns, "ami_block_device_mappings are unused when from_scratch is false")
|
||||
}
|
||||
if b.config.RootDeviceName != "" {
|
||||
warns = append(warns, "root_device_name is unused when from_scratch is false")
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, errs
|
||||
return warns, errs
|
||||
}
|
||||
|
||||
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
|
||||
return nil, nil
|
||||
return warns, nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||
|
@ -161,11 +203,19 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
ForceDeregister: b.config.AMIForceDeregister,
|
||||
},
|
||||
&StepInstanceInfo{},
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
},
|
||||
&StepCheckRootDevice{},
|
||||
}
|
||||
|
||||
if !b.config.FromScratch {
|
||||
steps = append(steps,
|
||||
&awscommon.StepSourceAMIInfo{
|
||||
SourceAmi: b.config.SourceAmi,
|
||||
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||
},
|
||||
&StepCheckRootDevice{},
|
||||
)
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
&StepFlock{},
|
||||
&StepPrepareDevice{},
|
||||
&StepCreateVolume{
|
||||
|
@ -173,10 +223,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
},
|
||||
&StepAttachVolume{},
|
||||
&StepEarlyUnflock{},
|
||||
&StepPreMountCommands{
|
||||
Commands: b.config.PreMountCommands,
|
||||
},
|
||||
&StepMountDevice{
|
||||
MountOptions: b.config.MountOptions,
|
||||
MountPartition: b.config.MountPartition,
|
||||
},
|
||||
&StepPostMountCommands{
|
||||
Commands: b.config.PostMountCommands,
|
||||
},
|
||||
&StepMountExtra{},
|
||||
&StepCopyFiles{},
|
||||
&StepChrootProvision{},
|
||||
|
@ -203,7 +259,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&awscommon.StepCreateTags{
|
||||
Tags: b.config.AMITags,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Run!
|
||||
if b.config.PackerDebug {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package chroot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/post-processor/shell-local"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
)
|
||||
|
||||
func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ctx interpolate.Context, ui packer.Ui) error {
|
||||
for _, rawCmd := range commands {
|
||||
intCmd, err := interpolate.Render(rawCmd, &ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error interpolating: %s", err)
|
||||
}
|
||||
|
||||
command, err := wrappedCommand(intCmd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error wrapping command: %s", err)
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Executing command: %s", command))
|
||||
comm := &shell_local.Communicator{}
|
||||
cmd := &packer.RemoteCmd{Command: command}
|
||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
||||
return fmt.Errorf("Error executing command: %s", err)
|
||||
}
|
||||
if cmd.ExitStatus != 0 {
|
||||
return fmt.Errorf(
|
||||
"Received non-zero exit code %d from command: %s",
|
||||
cmd.ExitStatus,
|
||||
command)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -22,40 +22,52 @@ type StepCreateVolume struct {
|
|||
}
|
||||
|
||||
func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
image := state.Get("source_image").(*ec2.Image)
|
||||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// Determine the root device snapshot
|
||||
log.Printf("Searching for root device of the image (%s)", *image.RootDeviceName)
|
||||
var rootDevice *ec2.BlockDeviceMapping
|
||||
for _, device := range image.BlockDeviceMappings {
|
||||
if *device.DeviceName == *image.RootDeviceName {
|
||||
rootDevice = device
|
||||
break
|
||||
var createVolume *ec2.CreateVolumeInput
|
||||
if config.FromScratch {
|
||||
createVolume = &ec2.CreateVolumeInput{
|
||||
AvailabilityZone: instance.Placement.AvailabilityZone,
|
||||
Size: aws.Int64(s.RootVolumeSize),
|
||||
VolumeType: aws.String(ec2.VolumeTypeGp2),
|
||||
}
|
||||
} else {
|
||||
// Determine the root device snapshot
|
||||
image := state.Get("source_image").(*ec2.Image)
|
||||
log.Printf("Searching for root device of the image (%s)", *image.RootDeviceName)
|
||||
var rootDevice *ec2.BlockDeviceMapping
|
||||
for _, device := range image.BlockDeviceMappings {
|
||||
if *device.DeviceName == *image.RootDeviceName {
|
||||
rootDevice = device
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rootDevice == nil {
|
||||
err := fmt.Errorf("Couldn't find root device!")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Creating the root volume...")
|
||||
vs := *rootDevice.Ebs.VolumeSize
|
||||
if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize {
|
||||
vs = s.RootVolumeSize
|
||||
}
|
||||
|
||||
createVolume = &ec2.CreateVolumeInput{
|
||||
AvailabilityZone: instance.Placement.AvailabilityZone,
|
||||
Size: aws.Int64(vs),
|
||||
SnapshotId: rootDevice.Ebs.SnapshotId,
|
||||
VolumeType: rootDevice.Ebs.VolumeType,
|
||||
Iops: rootDevice.Ebs.Iops,
|
||||
}
|
||||
}
|
||||
|
||||
if rootDevice == nil {
|
||||
err := fmt.Errorf("Couldn't find root device!")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Creating the root volume...")
|
||||
vs := *rootDevice.Ebs.VolumeSize
|
||||
if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize {
|
||||
vs = s.RootVolumeSize
|
||||
}
|
||||
createVolume := &ec2.CreateVolumeInput{
|
||||
AvailabilityZone: instance.Placement.AvailabilityZone,
|
||||
Size: aws.Int64(vs),
|
||||
SnapshotId: rootDevice.Ebs.SnapshotId,
|
||||
VolumeType: rootDevice.Ebs.VolumeType,
|
||||
Iops: rootDevice.Ebs.Iops,
|
||||
}
|
||||
log.Printf("Create args: %+v", createVolume)
|
||||
|
||||
createVolumeResp, err := ec2conn.CreateVolume(createVolume)
|
||||
|
|
|
@ -33,10 +33,18 @@ type StepMountDevice struct {
|
|||
func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
image := state.Get("source_image").(*ec2.Image)
|
||||
device := state.Get("device").(string)
|
||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||
|
||||
var virtualizationType string
|
||||
if config.FromScratch {
|
||||
virtualizationType = config.AMIVirtType
|
||||
} else {
|
||||
image := state.Get("source_image").(*ec2.Image)
|
||||
virtualizationType = *image.VirtualizationType
|
||||
log.Printf("Source image virtualization type is: %s", virtualizationType)
|
||||
}
|
||||
|
||||
ctx := config.ctx
|
||||
ctx.Data = &mountPathData{Device: filepath.Base(device)}
|
||||
mountPath, err := interpolate.Render(config.MountPath, &ctx)
|
||||
|
@ -65,9 +73,8 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Source image virtualization type is: %s", *image.VirtualizationType)
|
||||
deviceMount := device
|
||||
if *image.VirtualizationType == "hvm" {
|
||||
if virtualizationType == "hvm" {
|
||||
deviceMount = fmt.Sprintf("%s%d", device, s.MountPartition)
|
||||
}
|
||||
state.Put("deviceMount", deviceMount)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package chroot
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type postMountCommandsData struct {
|
||||
Device string
|
||||
MountPath string
|
||||
}
|
||||
|
||||
// StepPostMountCommands allows running arbitrary commands after mounting the
|
||||
// device, but prior to the bind mount and copy steps.
|
||||
type StepPostMountCommands struct {
|
||||
Commands []string
|
||||
}
|
||||
|
||||
func (s *StepPostMountCommands) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
device := state.Get("device").(string)
|
||||
mountPath := state.Get("mount_path").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||
|
||||
if len(s.Commands) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ctx := config.ctx
|
||||
ctx.Data = &postMountCommandsData{
|
||||
Device: device,
|
||||
MountPath: mountPath,
|
||||
}
|
||||
|
||||
ui.Say("Running post-mount commands...")
|
||||
if err := RunLocalCommands(s.Commands, wrappedCommand, ctx, ui); err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepPostMountCommands) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,39 @@
|
|||
package chroot
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type preMountCommandsData struct {
|
||||
Device string
|
||||
}
|
||||
|
||||
// StepPreMountCommands sets up the a new block device when building from scratch
|
||||
type StepPreMountCommands struct {
|
||||
Commands []string
|
||||
}
|
||||
|
||||
func (s *StepPreMountCommands) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
device := state.Get("device").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
|
||||
|
||||
if len(s.Commands) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ctx := config.ctx
|
||||
ctx.Data = &preMountCommandsData{Device: device}
|
||||
|
||||
ui.Say("Running device setup commands...")
|
||||
if err := RunLocalCommands(s.Commands, wrappedCommand, ctx, ui); err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepPreMountCommands) Cleanup(state multistep.StateBag) {}
|
|
@ -18,22 +18,36 @@ type StepRegisterAMI struct {
|
|||
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
image := state.Get("source_image").(*ec2.Image)
|
||||
snapshotId := state.Get("snapshot_id").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Registering the AMI...")
|
||||
blockDevices := make([]*ec2.BlockDeviceMapping, len(image.BlockDeviceMappings))
|
||||
for i, device := range image.BlockDeviceMappings {
|
||||
|
||||
var (
|
||||
registerOpts *ec2.RegisterImageInput
|
||||
blockDevices []*ec2.BlockDeviceMapping
|
||||
image *ec2.Image
|
||||
rootDeviceName string
|
||||
)
|
||||
|
||||
if config.FromScratch {
|
||||
blockDevices = config.AMIBlockDevices.BuildAMIDevices()
|
||||
rootDeviceName = config.RootDeviceName
|
||||
} else {
|
||||
image = state.Get("source_image").(*ec2.Image)
|
||||
blockDevices = make([]*ec2.BlockDeviceMapping, len(image.BlockDeviceMappings))
|
||||
rootDeviceName = *image.RootDeviceName
|
||||
}
|
||||
for i, device := range blockDevices {
|
||||
newDevice := device
|
||||
if *newDevice.DeviceName == *image.RootDeviceName {
|
||||
if *newDevice.DeviceName == rootDeviceName {
|
||||
if newDevice.Ebs != nil {
|
||||
newDevice.Ebs.SnapshotId = aws.String(snapshotId)
|
||||
} else {
|
||||
newDevice.Ebs = &ec2.EbsBlockDevice{SnapshotId: aws.String(snapshotId)}
|
||||
}
|
||||
|
||||
if s.RootVolumeSize > *newDevice.Ebs.VolumeSize {
|
||||
if config.FromScratch || s.RootVolumeSize > *newDevice.Ebs.VolumeSize {
|
||||
newDevice.Ebs.VolumeSize = aws.Int64(s.RootVolumeSize)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +61,17 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
|||
blockDevices[i] = newDevice
|
||||
}
|
||||
|
||||
registerOpts := buildRegisterOpts(config, image, blockDevices)
|
||||
if config.FromScratch {
|
||||
registerOpts = &ec2.RegisterImageInput{
|
||||
Name: &config.AMIName,
|
||||
Architecture: aws.String(ec2.ArchitectureValuesX8664),
|
||||
RootDeviceName: aws.String(rootDeviceName),
|
||||
VirtualizationType: aws.String(config.AMIVirtType),
|
||||
BlockDeviceMappings: blockDevices,
|
||||
}
|
||||
} else {
|
||||
registerOpts = buildRegisterOpts(config, image, blockDevices)
|
||||
}
|
||||
|
||||
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||
if config.AMIEnhancedNetworking {
|
||||
|
@ -105,6 +129,5 @@ func buildRegisterOpts(config *Config, image *ec2.Image, blockDevices []*ec2.Blo
|
|||
registerOpts.KernelId = image.KernelId
|
||||
registerOpts.RamdiskId = image.RamdiskId
|
||||
}
|
||||
|
||||
return registerOpts
|
||||
}
|
||||
|
|
|
@ -22,7 +22,15 @@ type BlockDevice struct {
|
|||
}
|
||||
|
||||
type BlockDevices struct {
|
||||
AMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings"`
|
||||
AMIBlockDevices `mapstructure:",squash"`
|
||||
LaunchBlockDevices `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
type AMIBlockDevices struct {
|
||||
AMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings"`
|
||||
}
|
||||
|
||||
type LaunchBlockDevices struct {
|
||||
LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings"`
|
||||
}
|
||||
|
||||
|
@ -77,10 +85,10 @@ func (b *BlockDevices) Prepare(ctx *interpolate.Context) []error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (b *BlockDevices) BuildAMIDevices() []*ec2.BlockDeviceMapping {
|
||||
func (b *AMIBlockDevices) BuildAMIDevices() []*ec2.BlockDeviceMapping {
|
||||
return buildBlockDevices(b.AMIMappings)
|
||||
}
|
||||
|
||||
func (b *BlockDevices) BuildLaunchDevices() []*ec2.BlockDeviceMapping {
|
||||
func (b *LaunchBlockDevices) BuildLaunchDevices() []*ec2.BlockDeviceMapping {
|
||||
return buildBlockDevices(b.LaunchMappings)
|
||||
}
|
||||
|
|
|
@ -124,22 +124,26 @@ func TestBlockDevice(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
blockDevices := BlockDevices{
|
||||
AMIMappings: []BlockDevice{*tc.Config},
|
||||
amiBlockDevices := AMIBlockDevices{
|
||||
AMIMappings: []BlockDevice{*tc.Config},
|
||||
}
|
||||
|
||||
launchBlockDevices := LaunchBlockDevices{
|
||||
LaunchMappings: []BlockDevice{*tc.Config},
|
||||
}
|
||||
|
||||
expected := []*ec2.BlockDeviceMapping{tc.Result}
|
||||
got := blockDevices.BuildAMIDevices()
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
|
||||
amiResults := amiBlockDevices.BuildAMIDevices()
|
||||
if !reflect.DeepEqual(expected, amiResults) {
|
||||
t.Fatalf("Bad block device, \nexpected: %#v\n\ngot: %#v",
|
||||
expected, got)
|
||||
expected, amiResults)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected, blockDevices.BuildLaunchDevices()) {
|
||||
launchResults := launchBlockDevices.BuildLaunchDevices()
|
||||
if !reflect.DeepEqual(expected, launchResults) {
|
||||
t.Fatalf("Bad block device, \nexpected: %#v\n\ngot: %#v",
|
||||
expected,
|
||||
blockDevices.BuildLaunchDevices())
|
||||
expected, launchResults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ description: |
|
|||
in the EC2 documentation.
|
||||
layout: docs
|
||||
page_title: 'Amazon AMI Builder (chroot)'
|
||||
...
|
||||
---
|
||||
|
||||
# AMI Builder (chroot)
|
||||
|
||||
|
@ -69,7 +69,8 @@ each category, the available configuration keys are alphabetized.
|
|||
|
||||
- `source_ami` (string) - The source AMI whose root volume will be copied and
|
||||
provisioned on the currently running instance. This must be an EBS-backed
|
||||
AMI with a root volume snapshot that you have access to.
|
||||
AMI with a root volume snapshot that you have access to. Note: this is not
|
||||
used when `from_scratch` is set to true.
|
||||
|
||||
### Optional:
|
||||
|
||||
|
@ -102,11 +103,11 @@ each category, the available configuration keys are alphabetized.
|
|||
section below. Please read that section for more information on how to
|
||||
use this.
|
||||
|
||||
- `command_wrapper` (string) - How to run shell commands. This defaults
|
||||
to "{{.Command}}". This may be useful to set if you want to set
|
||||
environmental variables or perhaps run it with `sudo` or so on. This is a
|
||||
configuration template where the `.Command` variable is replaced with the
|
||||
command to be run.
|
||||
- `command_wrapper` (string) - How to run shell commands. This defaults to
|
||||
`{{.Command}}`. This may be useful to set if you want to set environmental
|
||||
variables or perhaps run it with `sudo` or so on. This is a configuration
|
||||
template where the `.Command` variable is replaced with the command to
|
||||
be run.
|
||||
|
||||
- `copy_files` (array of strings) - Paths to files on the running EC2 instance
|
||||
that will be copied into the chroot environment prior to provisioning. This
|
||||
|
@ -121,7 +122,20 @@ each category, the available configuration keys are alphabetized.
|
|||
`ec2:ModifyInstanceAttribute` to your AWS IAM policy.
|
||||
|
||||
- `force_deregister` (boolean) - Force Packer to first deregister an existing
|
||||
AMI if one with the same name already exists. Default `false`.
|
||||
AMI if one with the same name already exists. Default false.
|
||||
|
||||
- `from_scratch` (boolean) - Build a new volume instead of starting from an
|
||||
existing AMI root volume snapshot. Default false. If true, `source_ami` is
|
||||
no longer used and the following options become required:
|
||||
`ami_virtualization_type`, `pre_mount_commands` and `root_volume_size`. The
|
||||
below options are also required in this mode only:
|
||||
|
||||
- `ami_block_device_mappings` (array of block device mappings) An entry
|
||||
matching `root_device_name` should be set. See the
|
||||
[amazon-ebs](/docs/builders/amazon-ebs.html) documentation for more
|
||||
details on this parameter.
|
||||
|
||||
- `root_device_name` (string) - The root device name. For example, `xvda`.
|
||||
|
||||
- `mount_path` (string) - The path where the volume will be mounted. This is
|
||||
where the chroot environment will be. This defaults to
|
||||
|
@ -129,8 +143,8 @@ each category, the available configuration keys are alphabetized.
|
|||
where the `.Device` variable is replaced with the name of the device where
|
||||
the volume is attached.
|
||||
|
||||
- `mount_partition` (integer) - The partition number containing the /
|
||||
partition. By default this is the first partition of the volume.
|
||||
- `mount_partition` (integer) - The partition number containing the
|
||||
/ partition. By default this is the first partition of the volume.
|
||||
|
||||
- `mount_options` (array of strings) - Options to supply the `mount` command
|
||||
when mounting devices. Each option will be prefixed with `-o` and supplied
|
||||
|
@ -139,11 +153,22 @@ each category, the available configuration keys are alphabetized.
|
|||
command](http://linuxcommand.org/man_pages/mount8.html) for valid file
|
||||
system specific options
|
||||
|
||||
- `pre_mount_commands` (array of strings) - A series of commands to execute
|
||||
after attaching the root volume and before mounting the chroot. This is not
|
||||
required unless using `from_scratch`. If so, this should include any
|
||||
partitioning and filesystem creation commands. The path to the device is
|
||||
provided by `{{.Device}}`.
|
||||
|
||||
- `post_mount_commands` (array of strings) - As `pre_mount_commands`, but the
|
||||
commands are executed after mounting the root device and before the extra
|
||||
mount and copy steps. The device and mount path are provided by
|
||||
`{{.Device}}` and `{{.MountPath}}`.
|
||||
|
||||
- `root_volume_size` (integer) - The size of the root volume for the chroot
|
||||
environment, and the resulting AMI
|
||||
|
||||
- `skip_region_validation` (boolean) - Set to true if you want to skip
|
||||
validation of the ami_regions configuration option. Defaults to false.
|
||||
- `skip_region_validation` (boolean) - Set to true if you want to skip
|
||||
validation of the `ami_regions` configuration option. Defaults to false.
|
||||
|
||||
- `tags` (object of key/value strings) - Tags applied to the AMI.
|
||||
|
||||
|
@ -236,3 +261,33 @@ services:
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Building From Scratch
|
||||
|
||||
This example demonstrates the essentials of building an image from scratch. A
|
||||
15G gp2 (SSD) device is created (overriding the default of standard/magnetic).
|
||||
The device setup commands partition the device with one partition for use as an
|
||||
HVM image and format it ext4. This builder block should be followed by
|
||||
provisioning commands to install the os and bootloader.
|
||||
|
||||
``` {.javascript}
|
||||
{
|
||||
"type": "amazon-chroot",
|
||||
"ami_name": "packer-from-scratch {{timestamp}}"
|
||||
"from_scratch": true,
|
||||
"ami_virtualization_type": "hvm",
|
||||
"device_setup_commands": [
|
||||
"parted {{.Device}} mklabel msdos mkpart primary 1M 100% set 1 boot on print",
|
||||
"mkfs.ext4 {{.Device}}1"
|
||||
],
|
||||
"root_volume_size": 15,
|
||||
"root_device_name": "xvda",
|
||||
"ami_block_device_mappings": [
|
||||
{
|
||||
"device_name": "xvda",
|
||||
"delete_on_termination": true,
|
||||
"volume_type": "gp2"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue