Merge pull request #6669 from sargun/root_volume_type

Enable setting the volume type
This commit is contained in:
Adrien Delorme 2018-09-07 14:48:31 +02:00 committed by GitHub
commit 89e43e7747
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 17 deletions

View File

@ -42,6 +42,7 @@ type Config struct {
PreMountCommands []string `mapstructure:"pre_mount_commands"` PreMountCommands []string `mapstructure:"pre_mount_commands"`
RootDeviceName string `mapstructure:"root_device_name"` RootDeviceName string `mapstructure:"root_device_name"`
RootVolumeSize int64 `mapstructure:"root_volume_size"` RootVolumeSize int64 `mapstructure:"root_volume_size"`
RootVolumeType string `mapstructure:"root_volume_type"`
SourceAmi string `mapstructure:"source_ami"` SourceAmi string `mapstructure:"source_ami"`
SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter"` SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter"`
RootVolumeTags awscommon.TagMap `mapstructure:"root_volume_tags"` 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{}, &StepFlock{},
&StepPrepareDevice{}, &StepPrepareDevice{},
&StepCreateVolume{ &StepCreateVolume{
RootVolumeType: b.config.RootVolumeType,
RootVolumeSize: b.config.RootVolumeSize, RootVolumeSize: b.config.RootVolumeSize,
RootVolumeTags: b.config.RootVolumeTags, RootVolumeTags: b.config.RootVolumeTags,
Ctx: b.config.ctx, Ctx: b.config.ctx,

View File

@ -2,6 +2,7 @@ package chroot
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
@ -21,6 +22,7 @@ import (
type StepCreateVolume struct { type StepCreateVolume struct {
volumeId string volumeId string
RootVolumeSize int64 RootVolumeSize int64
RootVolumeType string
RootVolumeTags awscommon.TagMap RootVolumeTags awscommon.TagMap
Ctx interpolate.Context Ctx interpolate.Context
} }
@ -53,11 +55,21 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu
var createVolume *ec2.CreateVolumeInput var createVolume *ec2.CreateVolumeInput
if config.FromScratch { if config.FromScratch {
rootVolumeType := ec2.VolumeTypeGp2
if s.RootVolumeType == "io1" {
err := errors.New("Cannot use io1 volume when building from scratch")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else if s.RootVolumeType != "" {
rootVolumeType = s.RootVolumeType
}
createVolume = &ec2.CreateVolumeInput{ createVolume = &ec2.CreateVolumeInput{
AvailabilityZone: instance.Placement.AvailabilityZone, AvailabilityZone: instance.Placement.AvailabilityZone,
Size: aws.Int64(s.RootVolumeSize), Size: aws.Int64(s.RootVolumeSize),
VolumeType: aws.String(ec2.VolumeTypeGp2), VolumeType: aws.String(rootVolumeType),
} }
} else { } else {
// Determine the root device snapshot // Determine the root device snapshot
image := state.Get("source_image").(*ec2.Image) image := state.Get("source_image").(*ec2.Image)
@ -70,26 +82,13 @@ func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) mu
} }
} }
if rootDevice == nil { ui.Say("Creating the root volume...")
err := fmt.Errorf("Couldn't find root device!") createVolume, err = s.buildCreateVolumeInput(*instance.Placement.AvailabilityZone, rootDevice)
if err != nil {
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt 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 { if len(tagSpecs) > 0 {
@ -137,3 +136,33 @@ func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
ui.Error(fmt.Sprintf("Error deleting EBS volume: %s", err)) 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
}

View File

@ -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)
}

View File

@ -254,6 +254,11 @@ each category, the available configuration keys are alphabetized.
of the `source_ami` unless `from_scratch` is `true`, in which case of the `source_ami` unless `from_scratch` is `true`, in which case
this field must be defined. this field must be defined.
- `root_volume_type` (string) - The type of EBS volume for the chroot environment
and resulting AMI. The default value is the type of the `source_ami`, unless
`from_scratch` is true, in which case the default value is `gp2`. You can only
specify `io1` if building based on top of a `source_ami` which is also `io1`.
- `root_volume_tags` (object of key/value strings) - Tags to apply to the volumes - `root_volume_tags` (object of key/value strings) - Tags to apply to the volumes
that are *launched*. This is a that are *launched*. This is a
[template engine](/docs/templates/engine.html), [template engine](/docs/templates/engine.html),