packer-cn/builder/amazon/common/block_device.go

239 lines
9.3 KiB
Go

//go:generate struct-markdown
package common
import (
"fmt"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/template/interpolate"
)
// BlockDevice
type BlockDevice struct {
// Indicates whether the EBS volume is
// deleted on instance termination. Default false. NOTE: If this
// value is not explicitly set to true and volumes are not cleaned up by
// an alternative method, additional volumes will accumulate after every
// build.
DeleteOnTermination bool `mapstructure:"delete_on_termination" required:"false"`
// The device name exposed to the instance (for
// example, /dev/sdh or xvdh). Required for every device in the block
// device mapping.
DeviceName string `mapstructure:"device_name" required:"false"`
// Indicates whether or not to encrypt the volume.
// By default, Packer will keep the encryption setting to what it was in
// the source image. Setting false will result in an unencrypted device,
// and true will result in an encrypted one.
Encrypted *bool `mapstructure:"encrypted" required:"false"`
// The number of I/O operations per second (IOPS) that
// the volume supports. See the documentation on
// IOPs
// for more information
IOPS int64 `mapstructure:"iops" required:"false"`
// Suppresses the specified device included in the
// block device mapping of the AMI.
NoDevice bool `mapstructure:"no_device" required:"false"`
// The ID of the snapshot.
SnapshotId string `mapstructure:"snapshot_id" required:"false"`
// The virtual device name. See the
// documentation on Block Device
// Mapping
// for more information.
VirtualName string `mapstructure:"virtual_name" required:"false"`
// The volume type. gp2 for General Purpose
// (SSD) volumes, io1 for Provisioned IOPS (SSD) volumes, st1 for
// Throughput Optimized HDD, sc1 for Cold HDD, and standard for
// Magnetic volumes.
VolumeType string `mapstructure:"volume_type" required:"false"`
// The size of the volume, in GiB. Required if
// not specifying a snapshot_id.
VolumeSize int64 `mapstructure:"volume_size" required:"false"`
// ID, alias or ARN 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.
// For valid formats see KmsKeyId in the [AWS API docs -
// CopyImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html)
// This field is validated by Packer, when using an alias, you will have to
// prefix kms_key_id with alias/.
KmsKeyId string `mapstructure:"kms_key_id" required:"false"`
// ebssurrogate only
OmitFromArtifact bool `mapstructure:"omit_from_artifact"`
}
type BlockDevices struct {
AMIBlockDevices `mapstructure:",squash"`
LaunchBlockDevices `mapstructure:",squash"`
}
type AMIBlockDevices struct {
// Add one or more [block device
// mappings](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html)
// to the AMI. These will be attached when booting a new instance from your
// AMI. To add a block device during the Packer build see
// `launch_block_device_mappings` below. Your options here may vary
// depending on the type of VM you use. The block device mappings allow for
// the following configuration:
// - `delete_on_termination` (boolean) - Indicates whether the EBS volume is
// deleted on instance termination. Default `false`. **NOTE**: If this
// value is not explicitly set to `true` and volumes are not cleaned up by
// an alternative method, additional volumes will accumulate after every
// build.
//
// - `device_name` (string) - The device name exposed to the instance (for
// example, `/dev/sdh` or `xvdh`). Required for every device in the block
// device mapping.
//
// - `encrypted` (boolean) - Indicates whether or not to encrypt the volume.
// By default, Packer will keep the encryption setting to what it was in
// the source image. Setting `false` will result in an unencrypted device,
// and `true` will result in an encrypted one.
//
// - `iops` (number) - The number of I/O operations per second (IOPS) that
// the volume supports. See the documentation on
// [IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
// for more information
//
// - `kms_key_id` (string) - The ARN for the KMS encryption key. When
// specifying `kms_key_id`, `encrypted` needs to be set to `true`. For
// valid formats see *KmsKeyId* in the [AWS API docs -
// CopyImage](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CopyImage.html).
//
// - `no_device` (boolean) - Suppresses the specified device included in the
// block device mapping of the AMI.
//
// - `snapshot_id` (string) - The ID of the snapshot.
//
// - `virtual_name` (string) - The virtual device name. See the
// documentation on [Block Device
// Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
// for more information.
//
// - `volume_size` (number) - The size of the volume, in GiB. Required if
// not specifying a `snapshot_id`.
//
// - `volume_type` (string) - The volume type. `gp2` for General Purpose
// (SSD) volumes, `io1` for Provisioned IOPS (SSD) volumes, `st1` for
// Throughput Optimized HDD, `sc1` for Cold HDD, and `standard` for
// Magnetic volumes.
AMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings" required:"false"`
}
type LaunchBlockDevices struct {
// Add one or more block devices before the Packer build starts. If you add
// instance store volumes or EBS volumes in addition to the root device
// volume, the created AMI will contain block device mapping information
// for those volumes. Amazon creates snapshots of the source instance's
// root volume and any other EBS volumes described here. When you launch an
// instance from this new AMI, the instance automatically launches with
// these additional volumes, and will restore them from snapshots taken
// from the source instance.
//
// In addition to the fields available in ami_block_device_mappings, you
// may optionally use the following field:
// - `omit_from_artifact` (boolean) - If true, this block device will not
// be snapshotted and the created AMI will not contain block device mapping
// information for this volume. If false, the block device will be mapped
// into the final created AMI. Set this option to true if you need a block
// device mounted in the surrogate AMI but not in the final created AMI.
LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings" required:"false"`
}
func buildBlockDevices(b []BlockDevice) []*ec2.BlockDeviceMapping {
var blockDevices []*ec2.BlockDeviceMapping
for _, blockDevice := range b {
mapping := &ec2.BlockDeviceMapping{
DeviceName: aws.String(blockDevice.DeviceName),
}
if blockDevice.NoDevice {
mapping.NoDevice = aws.String("")
} else if blockDevice.VirtualName != "" {
if strings.HasPrefix(blockDevice.VirtualName, "ephemeral") {
mapping.VirtualName = aws.String(blockDevice.VirtualName)
}
} else {
ebsBlockDevice := &ec2.EbsBlockDevice{
DeleteOnTermination: aws.Bool(blockDevice.DeleteOnTermination),
}
if blockDevice.VolumeType != "" {
ebsBlockDevice.VolumeType = aws.String(blockDevice.VolumeType)
}
if blockDevice.VolumeSize > 0 {
ebsBlockDevice.VolumeSize = aws.Int64(blockDevice.VolumeSize)
}
// IOPS is only valid for io1 type
if blockDevice.VolumeType == "io1" {
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
}
// You cannot specify Encrypted if you specify a Snapshot ID
if blockDevice.SnapshotId != "" {
ebsBlockDevice.SnapshotId = aws.String(blockDevice.SnapshotId)
}
ebsBlockDevice.Encrypted = blockDevice.Encrypted
if blockDevice.KmsKeyId != "" {
ebsBlockDevice.KmsKeyId = aws.String(blockDevice.KmsKeyId)
}
mapping.Ebs = ebsBlockDevice
}
blockDevices = append(blockDevices, mapping)
}
return blockDevices
}
func (b *BlockDevice) Prepare(ctx *interpolate.Context) error {
if b.DeviceName == "" {
return fmt.Errorf("The `device_name` must be specified " +
"for every device in the block device mapping.")
}
// Warn that encrypted must be true when setting kms_key_id
if b.KmsKeyId != "" && b.Encrypted != nil && *b.Encrypted == false {
return fmt.Errorf("The device %v, must also have `encrypted: "+
"true` when setting a kms_key_id.", b.DeviceName)
}
return nil
}
func (b *BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
for _, d := range b.AMIMappings {
if err := d.Prepare(ctx); err != nil {
errs = append(errs, fmt.Errorf("AMIMapping: %s", err.Error()))
}
}
for _, d := range b.LaunchMappings {
if err := d.Prepare(ctx); err != nil {
errs = append(errs, fmt.Errorf("LaunchMapping: %s", err.Error()))
}
}
return errs
}
func (b *AMIBlockDevices) BuildAMIDevices() []*ec2.BlockDeviceMapping {
return buildBlockDevices(b.AMIMappings)
}
func (b *LaunchBlockDevices) BuildLaunchDevices() []*ec2.BlockDeviceMapping {
return buildBlockDevices(b.LaunchMappings)
}
func (b *LaunchBlockDevices) GetOmissions() map[string]bool {
omitMap := make(map[string]bool)
for _, blockDevice := range b.LaunchMappings {
omitMap[blockDevice.DeviceName] = blockDevice.OmitFromArtifact
}
return omitMap
}