diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index c0453ba6b..fb385dc33 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -42,6 +42,7 @@ type Config struct { PreMountCommands []string `mapstructure:"pre_mount_commands"` RootDeviceName string `mapstructure:"root_device_name"` RootVolumeSize int64 `mapstructure:"root_volume_size"` + RootVolumeType string `mapstructure:"root_volume_type"` SourceAmi string `mapstructure:"source_ami"` SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter"` RootVolumeTags awscommon.TagMap `mapstructure:"root_volume_tags"` @@ -233,6 +234,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &StepFlock{}, &StepPrepareDevice{}, &StepCreateVolume{ + RootVolumeType: b.config.RootVolumeType, RootVolumeSize: b.config.RootVolumeSize, RootVolumeTags: b.config.RootVolumeTags, Ctx: b.config.ctx, diff --git a/builder/amazon/chroot/step_create_volume.go b/builder/amazon/chroot/step_create_volume.go index 4adf3074f..34468ff7e 100644 --- a/builder/amazon/chroot/step_create_volume.go +++ b/builder/amazon/chroot/step_create_volume.go @@ -21,6 +21,7 @@ import ( type StepCreateVolume struct { volumeId string RootVolumeSize int64 + RootVolumeType string RootVolumeTags awscommon.TagMap Ctx interpolate.Context } @@ -70,26 +71,13 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu } } - if rootDevice == nil { - err := fmt.Errorf("Couldn't find root device!") + ui.Say("Creating the root volume...") + createVolume, err = s.buildCreateVolumeInput(*instance.Placement.AvailabilityZone, rootDevice) + if err != nil { 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 len(tagSpecs) > 0 { @@ -137,3 +125,33 @@ func (s *StepCreateVolume) Cleanup(state multistep.StateBag) { ui.Error(fmt.Sprintf("Error deleting EBS volume: %s", err)) } } + +func (s *StepCreateVolume) buildCreateVolumeInput(az string, rootDevice *ec2.BlockDeviceMapping) (*ec2.CreateVolumeInput, error) { + if rootDevice == nil { + return nil, fmt.Errorf("Couldn't find root device!") + } + createVolumeInput := &ec2.CreateVolumeInput{ + AvailabilityZone: aws.String(az), + Size: rootDevice.Ebs.VolumeSize, + SnapshotId: rootDevice.Ebs.SnapshotId, + VolumeType: rootDevice.Ebs.VolumeType, + Iops: rootDevice.Ebs.Iops, + } + if s.RootVolumeSize > *rootDevice.Ebs.VolumeSize { + createVolumeInput.Size = aws.Int64(s.RootVolumeSize) + } + + if s.RootVolumeType == "" || s.RootVolumeType == *rootDevice.Ebs.VolumeType { + return createVolumeInput, nil + } + + if s.RootVolumeType == "io1" { + return nil, fmt.Errorf("Root volume type cannot be io1, because existing root volume type was %s", *rootDevice.Ebs.VolumeType) + } + + createVolumeInput.VolumeType = aws.String(s.RootVolumeType) + // non io1 cannot set iops + createVolumeInput.Iops = nil + + return createVolumeInput, nil +} diff --git a/builder/amazon/chroot/step_create_volume_test.go b/builder/amazon/chroot/step_create_volume_test.go new file mode 100644 index 000000000..03a34bfd5 --- /dev/null +++ b/builder/amazon/chroot/step_create_volume_test.go @@ -0,0 +1,73 @@ +package chroot + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/stretchr/testify/assert" + "testing" +) + +func buildTestRootDevice() *ec2.BlockDeviceMapping { + return &ec2.BlockDeviceMapping{ + Ebs: &ec2.EbsBlockDevice{ + VolumeSize: aws.Int64(10), + SnapshotId: aws.String("snap-1234"), + VolumeType: aws.String("gp2"), + }, + } +} + +func TestCreateVolume_Default(t *testing.T) { + stepCreateVolume := new(StepCreateVolume) + _, err := stepCreateVolume.buildCreateVolumeInput("test-az", buildTestRootDevice()) + assert.NoError(t, err) +} + +func TestCreateVolume_Shrink(t *testing.T) { + stepCreateVolume := StepCreateVolume{RootVolumeSize: 1} + testRootDevice := buildTestRootDevice() + ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice) + assert.NoError(t, err) + // Ensure that the new value is equal to the size of the old root device + assert.Equal(t, *ret.Size, *testRootDevice.Ebs.VolumeSize) +} + +func TestCreateVolume_Expand(t *testing.T) { + stepCreateVolume := StepCreateVolume{RootVolumeSize: 25} + testRootDevice := buildTestRootDevice() + ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice) + assert.NoError(t, err) + // Ensure that the new value is equal to the size of the value passed in + assert.Equal(t, *ret.Size, stepCreateVolume.RootVolumeSize) +} + +func TestCreateVolume_io1_to_io1(t *testing.T) { + stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"} + testRootDevice := buildTestRootDevice() + testRootDevice.Ebs.VolumeType = aws.String("io1") + testRootDevice.Ebs.Iops = aws.Int64(1000) + ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice) + assert.NoError(t, err) + assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType) + assert.Equal(t, *ret.Iops, *testRootDevice.Ebs.Iops) +} + +func TestCreateVolume_io1_to_gp2(t *testing.T) { + stepCreateVolume := StepCreateVolume{RootVolumeType: "gp2"} + testRootDevice := buildTestRootDevice() + testRootDevice.Ebs.VolumeType = aws.String("io1") + testRootDevice.Ebs.Iops = aws.Int64(1000) + + ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice) + assert.NoError(t, err) + assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType) + assert.Nil(t, ret.Iops) +} + +func TestCreateVolume_gp2_to_io1(t *testing.T) { + stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"} + testRootDevice := buildTestRootDevice() + + _, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice) + assert.Error(t, err) +}