Merge pull request #8021 from hashicorp/fix_7959

[WIP] implement custom data type "trilean" (tri-state-boolean) to track boo…
This commit is contained in:
Megan Marsh 2019-09-13 14:42:27 -07:00 committed by GitHub
commit fc6a4fd5d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 376 additions and 106 deletions

View File

@ -4,6 +4,7 @@ import (
"reflect" "reflect"
"testing" "testing"
helperconfig "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -126,13 +127,15 @@ func TestBuilderPrepare_Devices(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("should not have error: %s", err) t.Fatalf("should not have error: %s", err)
} }
if !reflect.DeepEqual(b.config.ECSSystemDiskMapping, AlicloudDiskDevice{ expected := AlicloudDiskDevice{
DiskCategory: "cloud", DiskCategory: "cloud",
Description: "system disk", Description: "system disk",
DiskName: "system_disk", DiskName: "system_disk",
DiskSize: 60, DiskSize: 60,
}) { Encrypted: helperconfig.TriUnset,
t.Fatalf("system disk is not set properly, actual: %#v", b.config.ECSSystemDiskMapping) }
if !reflect.DeepEqual(b.config.ECSSystemDiskMapping, expected) {
t.Fatalf("system disk is not set properly, actual: %v; expected: %v", b.config.ECSSystemDiskMapping, expected)
} }
if !reflect.DeepEqual(b.config.ECSImagesDiskMappings, []AlicloudDiskDevice{ if !reflect.DeepEqual(b.config.ECSImagesDiskMappings, []AlicloudDiskDevice{
{ {

View File

@ -5,18 +5,19 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
type AlicloudDiskDevice struct { type AlicloudDiskDevice struct {
DiskName string `mapstructure:"disk_name"` DiskName string `mapstructure:"disk_name"`
DiskCategory string `mapstructure:"disk_category"` DiskCategory string `mapstructure:"disk_category"`
DiskSize int `mapstructure:"disk_size"` DiskSize int `mapstructure:"disk_size"`
SnapshotId string `mapstructure:"disk_snapshot_id"` SnapshotId string `mapstructure:"disk_snapshot_id"`
Description string `mapstructure:"disk_description"` Description string `mapstructure:"disk_description"`
DeleteWithInstance bool `mapstructure:"disk_delete_with_instance"` DeleteWithInstance bool `mapstructure:"disk_delete_with_instance"`
Device string `mapstructure:"disk_device"` Device string `mapstructure:"disk_device"`
Encrypted *bool `mapstructure:"disk_encrypted"` Encrypted config.Trilean `mapstructure:"disk_encrypted"`
} }
type AlicloudDiskDevices struct { type AlicloudDiskDevices struct {
@ -32,7 +33,7 @@ type AlicloudImageConfig struct {
AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"` AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"`
AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"` AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"` AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"`
ImageEncrypted *bool `mapstructure:"image_encrypted"` ImageEncrypted config.Trilean `mapstructure:"image_encrypted"`
AlicloudImageForceDelete bool `mapstructure:"image_force_delete"` AlicloudImageForceDelete bool `mapstructure:"image_force_delete"`
AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"` AlicloudImageForceDeleteSnapshots bool `mapstructure:"image_force_delete_snapshots"`
AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"` AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"`

View File

@ -8,31 +8,32 @@ import (
"github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
type RunConfig struct { type RunConfig struct {
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"` AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
ZoneId string `mapstructure:"zone_id"` ZoneId string `mapstructure:"zone_id"`
IOOptimized *bool `mapstructure:"io_optimized"` IOOptimized config.Trilean `mapstructure:"io_optimized"`
InstanceType string `mapstructure:"instance_type"` InstanceType string `mapstructure:"instance_type"`
Description string `mapstructure:"description"` Description string `mapstructure:"description"`
AlicloudSourceImage string `mapstructure:"source_image"` AlicloudSourceImage string `mapstructure:"source_image"`
ForceStopInstance bool `mapstructure:"force_stop_instance"` ForceStopInstance bool `mapstructure:"force_stop_instance"`
DisableStopInstance bool `mapstructure:"disable_stop_instance"` DisableStopInstance bool `mapstructure:"disable_stop_instance"`
SecurityGroupId string `mapstructure:"security_group_id"` SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupName string `mapstructure:"security_group_name"` SecurityGroupName string `mapstructure:"security_group_name"`
UserData string `mapstructure:"user_data"` UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"` UserDataFile string `mapstructure:"user_data_file"`
VpcId string `mapstructure:"vpc_id"` VpcId string `mapstructure:"vpc_id"`
VpcName string `mapstructure:"vpc_name"` VpcName string `mapstructure:"vpc_name"`
CidrBlock string `mapstructure:"vpc_cidr_block"` CidrBlock string `mapstructure:"vpc_cidr_block"`
VSwitchId string `mapstructure:"vswitch_id"` VSwitchId string `mapstructure:"vswitch_id"`
VSwitchName string `mapstructure:"vswitch_id"` VSwitchName string `mapstructure:"vswitch_id"`
InstanceName string `mapstructure:"instance_name"` InstanceName string `mapstructure:"instance_name"`
InternetChargeType string `mapstructure:"internet_charge_type"` InternetChargeType string `mapstructure:"internet_charge_type"`
InternetMaxBandwidthOut int `mapstructure:"internet_max_bandwidth_out"` InternetMaxBandwidthOut int `mapstructure:"internet_max_bandwidth_out"`
WaitSnapshotReadyTimeout int `mapstructure:"wait_snapshot_ready_timeout"` WaitSnapshotReadyTimeout int `mapstructure:"wait_snapshot_ready_timeout"`
// Communicator settings // Communicator settings
Comm communicator.Config `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"`

View File

@ -30,7 +30,7 @@ func (s *stepCreateAlicloudImage) Run(ctx context.Context, state multistep.State
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
tempImageName := config.AlicloudImageName tempImageName := config.AlicloudImageName
if config.ImageEncrypted != nil && *config.ImageEncrypted { if config.ImageEncrypted.True() {
tempImageName = fmt.Sprintf("packer_%s", random.AlphaNum(7)) tempImageName = fmt.Sprintf("packer_%s", random.AlphaNum(7))
ui.Say(fmt.Sprintf("Creating temporary image for encryption: %s", tempImageName)) ui.Say(fmt.Sprintf("Creating temporary image for encryption: %s", tempImageName))
} else { } else {
@ -85,7 +85,7 @@ func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) {
} }
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
encryptedSet := config.ImageEncrypted != nil && *config.ImageEncrypted encryptedSet := config.ImageEncrypted.True()
_, cancelled := state.GetOk(multistep.StateCancelled) _, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted) _, halted := state.GetOk(multistep.StateHalted)

View File

@ -12,12 +12,13 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
type stepCreateAlicloudInstance struct { type stepCreateAlicloudInstance struct {
IOOptimized *bool IOOptimized confighelper.Trilean
InstanceType string InstanceType string
UserData string UserData string
UserDataFile string UserDataFile string
@ -142,12 +143,10 @@ func (s *stepCreateAlicloudInstance) buildCreateInstanceRequest(state multistep.
request.InternetChargeType = s.InternetChargeType request.InternetChargeType = s.InternetChargeType
request.InternetMaxBandwidthOut = requests.Integer(convertNumber(s.InternetMaxBandwidthOut)) request.InternetMaxBandwidthOut = requests.Integer(convertNumber(s.InternetMaxBandwidthOut))
if s.IOOptimized != nil { if s.IOOptimized.True() {
if *s.IOOptimized { request.IoOptimized = IOOptimizedOptimized
request.IoOptimized = IOOptimizedOptimized } else if s.IOOptimized.False() {
} else { request.IoOptimized = IOOptimizedNone
request.IoOptimized = IOOptimizedNone
}
} }
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
@ -174,8 +173,8 @@ func (s *stepCreateAlicloudInstance) buildCreateInstanceRequest(state multistep.
dataDisk.Description = imageDisk.Description dataDisk.Description = imageDisk.Description
dataDisk.DeleteWithInstance = strconv.FormatBool(imageDisk.DeleteWithInstance) dataDisk.DeleteWithInstance = strconv.FormatBool(imageDisk.DeleteWithInstance)
dataDisk.Device = imageDisk.Device dataDisk.Device = imageDisk.Device
if imageDisk.Encrypted != nil { if imageDisk.Encrypted != confighelper.TriUnset {
dataDisk.Encrypted = strconv.FormatBool(*imageDisk.Encrypted) dataDisk.Encrypted = strconv.FormatBool(imageDisk.Encrypted.True())
} }
dataDisks = append(dataDisks, dataDisk) dataDisks = append(dataDisks, dataDisk)

View File

@ -7,6 +7,7 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -20,7 +21,7 @@ type stepRegionCopyAlicloudImage struct {
func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
if config.ImageEncrypted != nil { if config.ImageEncrypted != confighelper.TriUnset {
s.AlicloudImageDestinationRegions = append(s.AlicloudImageDestinationRegions, s.RegionId) s.AlicloudImageDestinationRegions = append(s.AlicloudImageDestinationRegions, s.RegionId)
s.AlicloudImageDestinationNames = append(s.AlicloudImageDestinationNames, config.AlicloudImageName) s.AlicloudImageDestinationNames = append(s.AlicloudImageDestinationNames, config.AlicloudImageName)
} }
@ -38,7 +39,7 @@ func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.S
ui.Say(fmt.Sprintf("Coping image %s from %s...", srcImageId, s.RegionId)) ui.Say(fmt.Sprintf("Coping image %s from %s...", srcImageId, s.RegionId))
for index, destinationRegion := range s.AlicloudImageDestinationRegions { for index, destinationRegion := range s.AlicloudImageDestinationRegions {
if destinationRegion == s.RegionId && config.ImageEncrypted == nil { if destinationRegion == s.RegionId && config.ImageEncrypted == confighelper.TriUnset {
continue continue
} }
@ -52,8 +53,8 @@ func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.S
copyImageRequest.ImageId = srcImageId copyImageRequest.ImageId = srcImageId
copyImageRequest.DestinationRegionId = destinationRegion copyImageRequest.DestinationRegionId = destinationRegion
copyImageRequest.DestinationImageName = ecsImageName copyImageRequest.DestinationImageName = ecsImageName
if config.ImageEncrypted != nil { if config.ImageEncrypted != confighelper.TriUnset {
copyImageRequest.Encrypted = requests.NewBoolean(*config.ImageEncrypted) copyImageRequest.Encrypted = requests.NewBoolean(config.ImageEncrypted.True())
} }
imageResponse, err := client.CopyImage(copyImageRequest) imageResponse, err := client.CopyImage(copyImageRequest)
@ -65,7 +66,7 @@ func (s *stepRegionCopyAlicloudImage) Run(ctx context.Context, state multistep.S
ui.Message(fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.RegionId, srcImageId, destinationRegion, imageResponse.ImageId)) ui.Message(fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.RegionId, srcImageId, destinationRegion, imageResponse.ImageId))
} }
if config.ImageEncrypted != nil { if config.ImageEncrypted != confighelper.TriUnset {
if _, err := client.WaitForImageStatus(s.RegionId, alicloudImages[s.RegionId], ImageStatusAvailable, time.Duration(ALICLOUD_DEFAULT_LONG_TIMEOUT)*time.Second); err != nil { if _, err := client.WaitForImageStatus(s.RegionId, alicloudImages[s.RegionId], ImageStatusAvailable, time.Duration(ALICLOUD_DEFAULT_LONG_TIMEOUT)*time.Second); err != nil {
return halt(state, err, fmt.Sprintf("Timeout waiting image %s finish copying", alicloudImages[s.RegionId])) return halt(state, err, fmt.Sprintf("Timeout waiting image %s finish copying", alicloudImages[s.RegionId]))
} }

View File

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common" awscommon "github.com/hashicorp/packer/builder/amazon/common"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -14,7 +15,7 @@ import (
// StepRegisterAMI creates the AMI. // StepRegisterAMI creates the AMI.
type StepRegisterAMI struct { type StepRegisterAMI struct {
RootVolumeSize int64 RootVolumeSize int64
EnableAMIENASupport *bool EnableAMIENASupport confighelper.Trilean
EnableAMISriovNetSupport bool EnableAMISriovNetSupport bool
} }
@ -41,7 +42,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge) // As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
registerOpts.SriovNetSupport = aws.String("simple") registerOpts.SriovNetSupport = aws.String("simple")
} }
if s.EnableAMIENASupport != nil && *s.EnableAMIENASupport { if s.EnableAMIENASupport.True() {
// Set EnaSupport to true // Set EnaSupport to true
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge // As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
registerOpts.EnaSupport = aws.Bool(true) registerOpts.EnaSupport = aws.Bool(true)

View File

@ -5,6 +5,7 @@ import (
"log" "log"
"regexp" "regexp"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
@ -19,11 +20,11 @@ type AMIConfig struct {
AMIRegions []string `mapstructure:"ami_regions"` AMIRegions []string `mapstructure:"ami_regions"`
AMISkipRegionValidation bool `mapstructure:"skip_region_validation"` AMISkipRegionValidation bool `mapstructure:"skip_region_validation"`
AMITags TagMap `mapstructure:"tags"` AMITags TagMap `mapstructure:"tags"`
AMIENASupport *bool `mapstructure:"ena_support"` AMIENASupport config.Trilean `mapstructure:"ena_support"`
AMISriovNetSupport bool `mapstructure:"sriov_support"` AMISriovNetSupport bool `mapstructure:"sriov_support"`
AMIForceDeregister bool `mapstructure:"force_deregister"` AMIForceDeregister bool `mapstructure:"force_deregister"`
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"` AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot"` AMIEncryptBootVolume config.Trilean `mapstructure:"encrypt_boot"`
AMIKmsKeyId string `mapstructure:"kms_key_id"` AMIKmsKeyId string `mapstructure:"kms_key_id"`
AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"` AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"`
SnapshotTags TagMap `mapstructure:"snapshot_tags"` SnapshotTags TagMap `mapstructure:"snapshot_tags"`
@ -62,7 +63,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
// Prevent sharing of default KMS key encrypted volumes with other aws users // Prevent sharing of default KMS key encrypted volumes with other aws users
if len(c.AMIUsers) > 0 { if len(c.AMIUsers) > 0 {
if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume != nil && *c.AMIEncryptBootVolume { if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume.True() {
errs = append(errs, fmt.Errorf("Cannot share AMI encrypted with default KMS key")) errs = append(errs, fmt.Errorf("Cannot share AMI encrypted with default KMS key"))
} }
if len(c.AMIRegionKMSKeyIDs) > 0 { if len(c.AMIRegionKMSKeyIDs) > 0 {
@ -92,7 +93,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
} }
if len(c.SnapshotUsers) > 0 { if len(c.SnapshotUsers) > 0 {
if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume != nil && *c.AMIEncryptBootVolume { if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume.True() {
errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key")) errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key"))
} }
if len(c.AMIRegionKMSKeyIDs) > 0 { if len(c.AMIRegionKMSKeyIDs) > 0 {

View File

@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/hashicorp/packer/helper/config"
) )
func testAMIConfig() *AMIConfig { func testAMIConfig() *AMIConfig {
@ -138,7 +139,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
c.SnapshotUsers = []string{"foo", "bar"} c.SnapshotUsers = []string{"foo", "bar"}
c.AMIKmsKeyId = "123-abc-456" c.AMIKmsKeyId = "123-abc-456"
c.AMIEncryptBootVolume = &[]bool{true}[0] c.AMIEncryptBootVolume = config.TriTrue
c.AMIRegions = []string{"us-east-1", "us-west-1"} c.AMIRegions = []string{"us-east-1", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{ c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890", "us-east-1": "123-456-7890",
@ -161,7 +162,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) { func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
c := testAMIConfig() c := testAMIConfig()
c.AMIUsers = []string{"testAccountID"} c.AMIUsers = []string{"testAccountID"}
c.AMIEncryptBootVolume = &[]bool{true}[0] c.AMIEncryptBootVolume = config.TriTrue
accessConf := testAccessConfig() accessConf := testAccessConfig()
@ -177,7 +178,7 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) { func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
c := testAMIConfig() c := testAMIConfig()
c.AMIEncryptBootVolume = aws.Bool(true) c.AMIEncryptBootVolume = config.TriTrue
accessConf := testAccessConfig() accessConf := testAccessConfig()

View File

@ -6,21 +6,22 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
// BlockDevice // BlockDevice
type BlockDevice struct { type BlockDevice struct {
DeleteOnTermination bool `mapstructure:"delete_on_termination"` DeleteOnTermination bool `mapstructure:"delete_on_termination"`
DeviceName string `mapstructure:"device_name"` DeviceName string `mapstructure:"device_name"`
Encrypted *bool `mapstructure:"encrypted"` Encrypted config.Trilean `mapstructure:"encrypted"`
IOPS int64 `mapstructure:"iops"` IOPS int64 `mapstructure:"iops"`
NoDevice bool `mapstructure:"no_device"` NoDevice bool `mapstructure:"no_device"`
SnapshotId string `mapstructure:"snapshot_id"` SnapshotId string `mapstructure:"snapshot_id"`
VirtualName string `mapstructure:"virtual_name"` VirtualName string `mapstructure:"virtual_name"`
VolumeType string `mapstructure:"volume_type"` VolumeType string `mapstructure:"volume_type"`
VolumeSize int64 `mapstructure:"volume_size"` VolumeSize int64 `mapstructure:"volume_size"`
KmsKeyId string `mapstructure:"kms_key_id"` KmsKeyId string `mapstructure:"kms_key_id"`
// ebssurrogate only // ebssurrogate only
OmitFromArtifact bool `mapstructure:"omit_from_artifact"` OmitFromArtifact bool `mapstructure:"omit_from_artifact"`
} }
@ -74,7 +75,8 @@ func buildBlockDevices(b []BlockDevice) []*ec2.BlockDeviceMapping {
if blockDevice.SnapshotId != "" { if blockDevice.SnapshotId != "" {
ebsBlockDevice.SnapshotId = aws.String(blockDevice.SnapshotId) ebsBlockDevice.SnapshotId = aws.String(blockDevice.SnapshotId)
} }
ebsBlockDevice.Encrypted = blockDevice.Encrypted
ebsBlockDevice.Encrypted = blockDevice.Encrypted.ToBoolPointer()
if blockDevice.KmsKeyId != "" { if blockDevice.KmsKeyId != "" {
ebsBlockDevice.KmsKeyId = aws.String(blockDevice.KmsKeyId) ebsBlockDevice.KmsKeyId = aws.String(blockDevice.KmsKeyId)
@ -93,8 +95,9 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error {
return fmt.Errorf("The `device_name` must be specified " + return fmt.Errorf("The `device_name` must be specified " +
"for every device in the block device mapping.") "for every device in the block device mapping.")
} }
// Warn that encrypted must be true or nil when setting kms_key_id // Warn that encrypted must be true or nil when setting kms_key_id
if b.KmsKeyId != "" && b.Encrypted != nil && *b.Encrypted == false { if b.KmsKeyId != "" && b.Encrypted.False() {
return fmt.Errorf("The device %v, must also have `encrypted: "+ return fmt.Errorf("The device %v, must also have `encrypted: "+
"true` when setting a kms_key_id.", b.DeviceName) "true` when setting a kms_key_id.", b.DeviceName)
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/helper/config"
) )
func TestBlockDevice(t *testing.T) { func TestBlockDevice(t *testing.T) {
@ -71,7 +72,7 @@ func TestBlockDevice(t *testing.T) {
VolumeType: "gp2", VolumeType: "gp2",
VolumeSize: 8, VolumeSize: 8,
DeleteOnTermination: true, DeleteOnTermination: true,
Encrypted: aws.Bool(true), Encrypted: config.TriTrue,
}, },
Result: &ec2.BlockDeviceMapping{ Result: &ec2.BlockDeviceMapping{
@ -90,7 +91,7 @@ func TestBlockDevice(t *testing.T) {
VolumeType: "gp2", VolumeType: "gp2",
VolumeSize: 8, VolumeSize: 8,
DeleteOnTermination: true, DeleteOnTermination: true,
Encrypted: aws.Bool(true), Encrypted: config.TriTrue,
KmsKeyId: "2Fa48a521f-3aff-4b34-a159-376ac5d37812", KmsKeyId: "2Fa48a521f-3aff-4b34-a159-376ac5d37812",
}, },

View File

@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -17,7 +18,7 @@ type StepAMIRegionCopy struct {
Regions []string Regions []string
AMIKmsKeyId string AMIKmsKeyId string
RegionKeyIds map[string]string RegionKeyIds map[string]string
EncryptBootVolume *bool // nil means preserve EncryptBootVolume config.Trilean // nil means preserve
Name string Name string
OriginalRegion string OriginalRegion string
@ -74,7 +75,7 @@ func (s *StepAMIRegionCopy) Run(ctx context.Context, state multistep.StateBag) m
s.toDelete = ami s.toDelete = ami
} }
if s.EncryptBootVolume != nil && *s.EncryptBootVolume { if s.EncryptBootVolume.True() {
// encrypt_boot is true, so we have to copy the temporary // encrypt_boot is true, so we have to copy the temporary
// AMI with required encryption setting. // AMI with required encryption setting.
// temp image was created by stepCreateAMI. // temp image was created by stepCreateAMI.
@ -102,7 +103,7 @@ func (s *StepAMIRegionCopy) Run(ctx context.Context, state multistep.StateBag) m
var regKeyID string var regKeyID string
ui.Message(fmt.Sprintf("Copying to: %s", region)) ui.Message(fmt.Sprintf("Copying to: %s", region))
if s.EncryptBootVolume != nil && *s.EncryptBootVolume { if s.EncryptBootVolume.True() {
// Encrypt is true, explicitly // Encrypt is true, explicitly
regKeyID = s.RegionKeyIds[region] regKeyID = s.RegionKeyIds[region]
} else { } else {
@ -112,7 +113,9 @@ func (s *StepAMIRegionCopy) Run(ctx context.Context, state multistep.StateBag) m
go func(region string) { go func(region string) {
defer wg.Done() defer wg.Done()
id, snapshotIds, err := s.amiRegionCopy(ctx, state, s.AccessConfig, s.Name, ami, region, s.OriginalRegion, regKeyID, s.EncryptBootVolume) id, snapshotIds, err := s.amiRegionCopy(ctx, state, s.AccessConfig,
s.Name, ami, region, s.OriginalRegion, regKeyID,
s.EncryptBootVolume.ToBoolPointer())
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
amis[region] = id amis[region] = id

View File

@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -109,7 +110,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
AMIKmsKeyId: "12345", AMIKmsKeyId: "12345",
// Original region key in regionkeyids is different than in amikmskeyid // Original region key in regionkeyids is different than in amikmskeyid
RegionKeyIds: map[string]string{"us-east-1": "12345"}, RegionKeyIds: map[string]string{"us-east-1": "12345"},
EncryptBootVolume: aws.Bool(true), EncryptBootVolume: config.TriTrue,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -153,7 +154,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
stepAMIRegionCopy = StepAMIRegionCopy{ stepAMIRegionCopy = StepAMIRegionCopy{
AccessConfig: testAccessConfig(), AccessConfig: testAccessConfig(),
Regions: []string{"us-east-1"}, Regions: []string{"us-east-1"},
EncryptBootVolume: aws.Bool(false), EncryptBootVolume: config.TriFalse,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -179,7 +180,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
AMIKmsKeyId: "IlikePancakes", AMIKmsKeyId: "IlikePancakes",
// Original region key in regionkeyids is different than in amikmskeyid // Original region key in regionkeyids is different than in amikmskeyid
RegionKeyIds: map[string]string{"us-east-1": "12345", "us-west-2": "abcde", "ap-east-1": "xyz"}, RegionKeyIds: map[string]string{"us-east-1": "12345", "us-west-2": "abcde", "ap-east-1": "xyz"},
EncryptBootVolume: aws.Bool(true), EncryptBootVolume: config.TriTrue,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -226,7 +227,7 @@ func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
Regions: make([]string, 0), Regions: make([]string, 0),
AMIKmsKeyId: "", AMIKmsKeyId: "",
RegionKeyIds: make(map[string]string), RegionKeyIds: make(map[string]string),
EncryptBootVolume: nil, EncryptBootVolume: config.TriUnset,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -252,7 +253,7 @@ func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
Regions: make([]string, 0), Regions: make([]string, 0),
AMIKmsKeyId: "", AMIKmsKeyId: "",
RegionKeyIds: make(map[string]string), RegionKeyIds: make(map[string]string),
EncryptBootVolume: aws.Bool(true), EncryptBootVolume: config.TriTrue,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -278,7 +279,7 @@ func TestStepAmiRegionCopy_nil_intermediary(t *testing.T) {
Regions: make([]string, 0), Regions: make([]string, 0),
AMIKmsKeyId: "", AMIKmsKeyId: "",
RegionKeyIds: make(map[string]string), RegionKeyIds: make(map[string]string),
EncryptBootVolume: aws.Bool(false), EncryptBootVolume: config.TriFalse,
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
} }
@ -360,7 +361,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
AMISkipBuildRegion: false, AMISkipBuildRegion: false,
EncryptBootVolume: aws.Bool(true), EncryptBootVolume: config.TriTrue,
} }
// mock out the region connection code // mock out the region connection code
stepAMIRegionCopy.getRegionConn = getMockConn stepAMIRegionCopy.getRegionConn = getMockConn
@ -386,7 +387,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
Name: "fake-ami-name", Name: "fake-ami-name",
OriginalRegion: "us-east-1", OriginalRegion: "us-east-1",
AMISkipBuildRegion: true, AMISkipBuildRegion: true,
EncryptBootVolume: aws.Bool(true), EncryptBootVolume: config.TriTrue,
} }
// mock out the region connection code // mock out the region connection code
stepAMIRegionCopy.getRegionConn = getMockConn stepAMIRegionCopy.getRegionConn = getMockConn

View File

@ -5,19 +5,20 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
type StepModifyEBSBackedInstance struct { type StepModifyEBSBackedInstance struct {
EnableAMIENASupport *bool EnableAMIENASupport confighelper.Trilean
EnableAMISriovNetSupport bool EnableAMISriovNetSupport bool
} }
func (s *StepModifyEBSBackedInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepModifyEBSBackedInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(ec2iface.EC2API)
instance := state.Get("instance").(*ec2.Instance) instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
@ -40,9 +41,9 @@ func (s *StepModifyEBSBackedInstance) Run(ctx context.Context, state multistep.S
// Handle EnaSupport flag. // Handle EnaSupport flag.
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge // As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
if s.EnableAMIENASupport != nil { if s.EnableAMIENASupport != confighelper.TriUnset {
var prefix string var prefix string
if *s.EnableAMIENASupport { if s.EnableAMIENASupport.True() {
prefix = "En" prefix = "En"
} else { } else {
prefix = "Dis" prefix = "Dis"
@ -50,7 +51,7 @@ func (s *StepModifyEBSBackedInstance) Run(ctx context.Context, state multistep.S
ui.Say(fmt.Sprintf("%sabling Enhanced Networking (ENA)...", prefix)) ui.Say(fmt.Sprintf("%sabling Enhanced Networking (ENA)...", prefix))
_, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{ _, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
InstanceId: instance.InstanceId, InstanceId: instance.InstanceId,
EnaSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(*s.EnableAMIENASupport)}, EnaSupport: &ec2.AttributeBooleanValue{Value: s.EnableAMIENASupport.ToBoolPointer()},
}) })
if err != nil { if err != nil {
err := fmt.Errorf("Error %sabling Enhanced Networking (ENA) on %s: %s", strings.ToLower(prefix), *instance.InstanceId, err) err := fmt.Errorf("Error %sabling Enhanced Networking (ENA) on %s: %s", strings.ToLower(prefix), *instance.InstanceId, err)

View File

@ -0,0 +1,105 @@
package common
import (
"bytes"
"context"
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
helperconfig "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// Define a mock struct to be used in unit tests for common aws steps.
type mockEC2Conn_ModifyEBS struct {
ec2iface.EC2API
Config *aws.Config
// Counters to figure out what code path was taken
shouldError bool
modifyImageAttrCount int
}
func (m *mockEC2Conn_ModifyEBS) ModifyInstanceAttribute(modifyInput *ec2.ModifyInstanceAttributeInput) (*ec2.ModifyInstanceAttributeOutput, error) {
m.modifyImageAttrCount++
// don't need to define output since we always discard it anyway.
output := &ec2.ModifyInstanceAttributeOutput{}
if m.shouldError {
return output, fmt.Errorf("fake ModifyInstanceAttribute error")
}
return output, nil
}
// Create statebag for running test
func fakeModifyEBSBackedInstanceState() multistep.StateBag {
state := new(multistep.BasicStateBag)
state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
state.Put("instance", "i-12345")
return state
}
func StepModifyEBSBackedInstance_EnableAMIENASupport(t *testing.T) {
// Value is unset, so we shouldn't modify
stepModifyEBSBackedInstance := StepModifyEBSBackedInstance{
EnableAMIENASupport: helperconfig.TriUnset,
EnableAMISriovNetSupport: false,
}
// mock out the region connection code
mockConn := &mockEC2Conn_ModifyEBS{
Config: aws.NewConfig(),
}
state := fakeModifyEBSBackedInstanceState()
state.Put("ec2", mockConn)
stepModifyEBSBackedInstance.Run(context.Background(), state)
if mockConn.modifyImageAttrCount > 0 {
t.Fatalf("Should not have modified image since EnableAMIENASupport is unset")
}
// Value is true, so we should modify
stepModifyEBSBackedInstance = StepModifyEBSBackedInstance{
EnableAMIENASupport: helperconfig.TriTrue,
EnableAMISriovNetSupport: false,
}
// mock out the region connection code
mockConn = &mockEC2Conn_ModifyEBS{
Config: aws.NewConfig(),
}
state = fakeModifyEBSBackedInstanceState()
state.Put("ec2", mockConn)
stepModifyEBSBackedInstance.Run(context.Background(), state)
if mockConn.modifyImageAttrCount != 1 {
t.Fatalf("Should have modified image, since EnableAMIENASupport is true")
}
// Value is false, so we should modify
stepModifyEBSBackedInstance = StepModifyEBSBackedInstance{
EnableAMIENASupport: helperconfig.TriFalse,
EnableAMISriovNetSupport: false,
}
// mock out the region connection code
mockConn = &mockEC2Conn_ModifyEBS{
Config: aws.NewConfig(),
}
state = fakeModifyEBSBackedInstanceState()
state.Put("ec2", mockConn)
stepModifyEBSBackedInstance.Run(context.Background(), state)
if mockConn.modifyImageAttrCount != 1 {
t.Fatalf("Should have modified image, since EnableAMIENASupport is true")
}
}

View File

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -20,7 +21,7 @@ import (
type StepSourceAMIInfo struct { type StepSourceAMIInfo struct {
SourceAmi string SourceAmi string
EnableAMISriovNetSupport bool EnableAMISriovNetSupport bool
EnableAMIENASupport *bool EnableAMIENASupport confighelper.Trilean
AMIVirtType string AMIVirtType string
AmiFilters AmiFilterOptions AmiFilters AmiFilterOptions
} }
@ -94,7 +95,7 @@ func (s *StepSourceAMIInfo) Run(ctx context.Context, state multistep.StateBag) m
// Enhanced Networking can only be enabled on HVM AMIs. // Enhanced Networking can only be enabled on HVM AMIs.
// See http://goo.gl/icuXh5 // See http://goo.gl/icuXh5
if (s.EnableAMIENASupport != nil && *s.EnableAMIENASupport) || s.EnableAMISriovNetSupport { if s.EnableAMIENASupport.True() || s.EnableAMISriovNetSupport {
err = s.canEnableEnhancedNetworking(image) err = s.canEnableEnhancedNetworking(image)
if err != nil { if err != nil {
state.Put("error", err) state.Put("error", err)

View File

@ -72,7 +72,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...) errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
if b.config.IsSpotInstance() && ((b.config.AMIENASupport != nil && *b.config.AMIENASupport) || b.config.AMISriovNetSupport) { if b.config.IsSpotInstance() && (b.config.AMIENASupport.True() || b.config.AMISriovNetSupport) {
errs = packer.MultiErrorAppend(errs, errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Spot instances do not support modification, which is required "+ fmt.Errorf("Spot instances do not support modification, which is required "+
"when either `ena_support` or `sriov_support` are set. Please ensure "+ "when either `ena_support` or `sriov_support` are set. Please ensure "+

View File

@ -27,7 +27,7 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
// Create the image // Create the image
amiName := config.AMIName amiName := config.AMIName
state.Put("intermediary_image", false) state.Put("intermediary_image", false)
if config.AMIEncryptBootVolume != nil && *config.AMIEncryptBootVolume != false || s.AMISkipBuildRegion { if config.AMIEncryptBootVolume.True() || s.AMISkipBuildRegion {
state.Put("intermediary_image", true) state.Put("intermediary_image", true)
// From AWS SDK docs: You can encrypt a copy of an unencrypted snapshot, // From AWS SDK docs: You can encrypt a copy of an unencrypted snapshot,

View File

@ -90,7 +90,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName)) errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName))
} }
if b.config.IsSpotInstance() && ((b.config.AMIENASupport != nil && *b.config.AMIENASupport) || b.config.AMISriovNetSupport) { if b.config.IsSpotInstance() && (b.config.AMIENASupport.True() || b.config.AMISriovNetSupport) {
errs = packer.MultiErrorAppend(errs, errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Spot instances do not support modification, which is required "+ fmt.Errorf("Spot instances do not support modification, which is required "+
"when either `ena_support` or `sriov_support` are set. Please ensure "+ "when either `ena_support` or `sriov_support` are set. Please ensure "+

View File

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common" awscommon "github.com/hashicorp/packer/builder/amazon/common"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
@ -16,7 +17,7 @@ type StepRegisterAMI struct {
RootDevice RootBlockDevice RootDevice RootBlockDevice
AMIDevices []*ec2.BlockDeviceMapping AMIDevices []*ec2.BlockDeviceMapping
LaunchDevices []*ec2.BlockDeviceMapping LaunchDevices []*ec2.BlockDeviceMapping
EnableAMIENASupport *bool EnableAMIENASupport confighelper.Trilean
EnableAMISriovNetSupport bool EnableAMISriovNetSupport bool
Architecture string Architecture string
image *ec2.Image image *ec2.Image
@ -46,7 +47,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge) // As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
registerOpts.SriovNetSupport = aws.String("simple") registerOpts.SriovNetSupport = aws.String("simple")
} }
if s.EnableAMIENASupport != nil && *s.EnableAMIENASupport { if s.EnableAMIENASupport.True() {
// Set EnaSupport to true // Set EnaSupport to true
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge // As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
registerOpts.EnaSupport = aws.Bool(true) registerOpts.EnaSupport = aws.Bool(true)

View File

@ -23,7 +23,7 @@ type Config struct {
awscommon.AccessConfig `mapstructure:",squash"` awscommon.AccessConfig `mapstructure:",squash"`
awscommon.RunConfig `mapstructure:",squash"` awscommon.RunConfig `mapstructure:",squash"`
AMIENASupport *bool `mapstructure:"ena_support"` AMIENASupport config.Trilean `mapstructure:"ena_support"`
AMISriovNetSupport bool `mapstructure:"sriov_support"` AMISriovNetSupport bool `mapstructure:"sriov_support"`
VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"` VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"` VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"`
@ -76,7 +76,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, err) errs = packer.MultiErrorAppend(errs, err)
} }
if b.config.IsSpotInstance() && ((b.config.AMIENASupport != nil && *b.config.AMIENASupport) || b.config.AMISriovNetSupport) { if b.config.IsSpotInstance() && ((b.config.AMIENASupport.True()) || b.config.AMISriovNetSupport) {
errs = packer.MultiErrorAppend(errs, errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Spot instances do not support modification, which is required "+ fmt.Errorf("Spot instances do not support modification, which is required "+
"when either `ena_support` or `sriov_support` are set. Please ensure "+ "when either `ena_support` or `sriov_support` are set. Please ensure "+

View File

@ -157,7 +157,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
errs, fmt.Errorf("x509_key_path points to bad file: %s", err)) errs, fmt.Errorf("x509_key_path points to bad file: %s", err))
} }
if b.config.IsSpotInstance() && ((b.config.AMIENASupport != nil && *b.config.AMIENASupport) || b.config.AMISriovNetSupport) { if b.config.IsSpotInstance() && ((b.config.AMIENASupport.True()) || b.config.AMISriovNetSupport) {
errs = packer.MultiErrorAppend(errs, errs = packer.MultiErrorAppend(errs,
fmt.Errorf("Spot instances do not support modification, which is required "+ fmt.Errorf("Spot instances do not support modification, which is required "+
"when either `ena_support` or `sriov_support` are set. Please ensure "+ "when either `ena_support` or `sriov_support` are set. Please ensure "+

View File

@ -7,12 +7,13 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common" awscommon "github.com/hashicorp/packer/builder/amazon/common"
confighelper "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
) )
type StepRegisterAMI struct { type StepRegisterAMI struct {
EnableAMIENASupport *bool EnableAMIENASupport confighelper.Trilean
EnableAMISriovNetSupport bool EnableAMISriovNetSupport bool
} }
@ -38,7 +39,7 @@ func (s *StepRegisterAMI) Run(ctx context.Context, state multistep.StateBag) mul
// As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge) // As of February 2017, this applies to C3, C4, D2, I2, R3, and M4 (excluding m4.16xlarge)
registerOpts.SriovNetSupport = aws.String("simple") registerOpts.SriovNetSupport = aws.String("simple")
} }
if s.EnableAMIENASupport != nil && *s.EnableAMIENASupport { if s.EnableAMIENASupport.True() {
// Set EnaSupport to true // Set EnaSupport to true
// As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge // As of February 2017, this applies to C5, I3, P2, R4, X1, and m4.16xlarge
registerOpts.EnaSupport = aws.Bool(true) registerOpts.EnaSupport = aws.Bool(true)

View File

@ -0,0 +1,72 @@
package config
import (
"strconv"
)
type Trilean uint8
const (
// This will assign unset to 0, which is the default value in interpolation
TriUnset Trilean = iota
TriTrue
TriFalse
)
func (t Trilean) ToString() string {
if t == TriTrue {
return "TriTrue"
} else if t == TriFalse {
return "TriFalse"
}
return "TriUnset"
}
func (t Trilean) ToBoolPointer() *bool {
if t == TriTrue {
return boolPointer(true)
} else if t == TriFalse {
return boolPointer(false)
}
return nil
}
func (t Trilean) True() bool {
if t == TriTrue {
return true
}
return false
}
func (t Trilean) False() bool {
if t == TriFalse {
return true
}
return false
}
func TrileanFromString(s string) (Trilean, error) {
if s == "" {
return TriUnset, nil
}
b, err := strconv.ParseBool(s)
if err != nil {
return TriUnset, err
} else if b == true {
return TriTrue, nil
} else {
return TriFalse, nil
}
}
func TrileanFromBool(b bool) Trilean {
if b {
return TriTrue
}
return TriFalse
}
func boolPointer(b bool) *bool {
return &b
}

View File

@ -0,0 +1,30 @@
package config
import (
"testing"
)
func TestTrilianParsing(t *testing.T) {
type testCase struct {
Input string
Output Trilean
ErrExpected bool
}
testCases := []testCase{
{"true", TriTrue, false}, {"True", TriTrue, false},
{"false", TriFalse, false}, {"False", TriFalse, false},
{"", TriUnset, false}, {"badvalue", TriUnset, true},
{"FAlse", TriUnset, true}, {"TrUe", TriUnset, true},
}
for _, tc := range testCases {
tril, err := TrileanFromString(tc.Input)
if err != nil {
if tc.ErrExpected == false {
t.Fatalf("Didn't expect error: %v", tc)
}
}
if tc.Output != tril {
t.Fatalf("Didn't return proper trilean. %v", tc)
}
}
}

View File

@ -29,6 +29,7 @@ type DecodeOpts struct {
var DefaultDecodeHookFuncs = []mapstructure.DecodeHookFunc{ var DefaultDecodeHookFuncs = []mapstructure.DecodeHookFunc{
uint8ToStringHook, uint8ToStringHook,
stringToTrilean,
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
} }
@ -154,3 +155,30 @@ func uint8ToStringHook(f reflect.Kind, t reflect.Kind, v interface{}) (interface
return v, nil return v, nil
} }
func stringToTrilean(f reflect.Type, t reflect.Type, v interface{}) (interface{}, error) {
// We have a custom data type, config, which we read from a string and
// then cast to a *bool. Why? So that we can appropriately read "unset"
// *bool values in order to intelligently default, even when the values are
// being set by a template variable.
testTril, _ := TrileanFromString("")
if t == reflect.TypeOf(testTril) {
// From value is string
if f == reflect.TypeOf("") {
tril, err := TrileanFromString(v.(string))
if err != nil {
return v, fmt.Errorf("Error parsing bool from given var: %s", err)
}
return tril, nil
} else {
// From value is boolean
if f == reflect.TypeOf(true) {
tril := TrileanFromBool(v.(bool))
return tril, nil
}
}
}
return v, nil
}

View File

@ -13,6 +13,7 @@ func TestDecode(t *testing.T) {
Name string Name string
Address string Address string
Time time.Duration Time time.Duration
Trilean Trilean
} }
cases := map[string]struct { cases := map[string]struct {
@ -23,13 +24,27 @@ func TestDecode(t *testing.T) {
"basic": { "basic": {
[]interface{}{ []interface{}{
map[string]interface{}{ map[string]interface{}{
"name": "bar", "name": "bar",
"time": "5s", "time": "5s",
"trilean": "true",
}, },
}, },
&Target{ &Target{
Name: "bar", Name: "bar",
Time: 5 * time.Second, Time: 5 * time.Second,
Trilean: TriTrue,
},
nil,
},
"empty-string-trilean": {
[]interface{}{
map[string]interface{}{
"trilean": "",
},
},
&Target{
Trilean: TriUnset,
}, },
nil, nil,
}, },