diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 94de49b76..415b82e3b 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -18,6 +18,7 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` osccommon.AccessConfig `mapstructure:",squash"` osccommon.RunConfig `mapstructure:",squash"` + osccommon.BlockDevices `mapstructure:",squash"` ctx interpolate.Context } diff --git a/builder/osc/common/block_device.go b/builder/osc/common/block_device.go new file mode 100644 index 000000000..fa2a16f4e --- /dev/null +++ b/builder/osc/common/block_device.go @@ -0,0 +1,119 @@ +package common + +import ( + "fmt" + "strings" + + "github.com/hashicorp/packer/template/interpolate" + "github.com/outscale/osc-go/oapi" +) + +// BlockDevice +type BlockDevice struct { + DeleteOnVmDeletion bool `mapstructure:"delete_on_vm_deletion"` + DeviceName string `mapstructure:"device_name"` + IOPS int64 `mapstructure:"iops"` + NoDevice bool `mapstructure:"no_device"` + SnapshotId string `mapstructure:"snapshot_id"` + VirtualName string `mapstructure:"virtual_name"` + VolumeType string `mapstructure:"volume_type"` + VolumeSize int64 `mapstructure:"volume_size"` + // Encrypted bool `mapstructure:"encrypted"` + // KmsKeyId string `mapstructure:"kms_key_id"` +} + +type BlockDevices struct { + OMIBlockDevices `mapstructure:",squash"` + LaunchBlockDevices `mapstructure:",squash"` +} + +type OMIBlockDevices struct { + OMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings"` +} + +type LaunchBlockDevices struct { + LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings"` +} + +func buildBlockDevices(b []BlockDevice) []*oapi.BlockDeviceMapping { + var blockDevices []*oapi.BlockDeviceMapping + + for _, blockDevice := range b { + mapping := &oapi.BlockDeviceMapping{ + DeviceName: blockDevice.DeviceName, + } + + if blockDevice.NoDevice { + mapping.NoDevice = "" + } else if blockDevice.VirtualName != "" { + if strings.HasPrefix(blockDevice.VirtualName, "ephemeral") { + mapping.VirtualDeviceName = blockDevice.VirtualName + } + } else { + bsu := oapi.Bsu{ + DeleteOnVmDeletion: blockDevice.DeleteOnVmDeletion, + } + + if blockDevice.VolumeType != "" { + bsu.VolumeType = blockDevice.VolumeType + } + + if blockDevice.VolumeSize > 0 { + bsu.VolumeSize = blockDevice.VolumeSize + } + + // IOPS is only valid for io1 type + if blockDevice.VolumeType == "io1" { + bsu.Iops = blockDevice.IOPS + } + + if blockDevice.SnapshotId != "" { + bsu.SnapshotId = blockDevice.SnapshotId + } + + //missing + //BlockDevice Encrypted + //KmsKeyId + + mapping.Bsu = bsu + } + + 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 == 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.OMIMappings { + if err := d.Prepare(ctx); err != nil { + errs = append(errs, fmt.Errorf("OMIMapping: %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 *OMIBlockDevices) BuildOMIDevices() []*oapi.BlockDeviceMapping { + return buildBlockDevices(b.OMIMappings) +} + +func (b *LaunchBlockDevices) BuildLaunchDevices() []*oapi.BlockDeviceMapping { + return buildBlockDevices(b.LaunchMappings) +} diff --git a/builder/osc/common/block_device_test.go b/builder/osc/common/block_device_test.go new file mode 100644 index 000000000..c6489359a --- /dev/null +++ b/builder/osc/common/block_device_test.go @@ -0,0 +1,163 @@ +package common + +import ( + "reflect" + "testing" + + "github.com/outscale/osc-go/oapi" +) + +func TestBlockDevice(t *testing.T) { + cases := []struct { + Config *BlockDevice + Result *oapi.BlockDeviceMapping + }{ + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + SnapshotId: "snap-1234", + VolumeType: "standard", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + SnapshotId: "snap-1234", + VolumeType: "standard", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VolumeSize: 8, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + VolumeSize: 8, + DeleteOnVmDeletion: false, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "io1", + VolumeSize: 8, + DeleteOnVmDeletion: true, + IOPS: 1000, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + VolumeType: "io1", + VolumeSize: 8, + DeleteOnVmDeletion: true, + Iops: 1000, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "gp2", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + VolumeType: "gp2", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "gp2", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + VolumeType: "gp2", + VolumeSize: 8, + DeleteOnVmDeletion: true, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VolumeType: "standard", + DeleteOnVmDeletion: true, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + Bsu: oapi.Bsu{ + VolumeType: "standard", + DeleteOnVmDeletion: true, + }, + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + VirtualName: "ephemeral0", + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + VirtualDeviceName: "ephemeral0", + }, + }, + { + Config: &BlockDevice{ + DeviceName: "/dev/sdb", + NoDevice: true, + }, + + Result: &oapi.BlockDeviceMapping{ + DeviceName: "/dev/sdb", + NoDevice: "", + }, + }, + } + + for _, tc := range cases { + omiBlockDevices := OMIBlockDevices{ + OMIMappings: []BlockDevice{*tc.Config}, + } + + launchBlockDevices := LaunchBlockDevices{ + LaunchMappings: []BlockDevice{*tc.Config}, + } + + expected := []*oapi.BlockDeviceMapping{tc.Result} + + omiResults := omiBlockDevices.BuildOMIDevices() + if !reflect.DeepEqual(expected, omiResults) { + t.Fatalf("Bad block device, \nexpected: %#v\n\ngot: %#v", + expected, omiResults) + } + + launchResults := launchBlockDevices.BuildLaunchDevices() + if !reflect.DeepEqual(expected, launchResults) { + t.Fatalf("Bad block device, \nexpected: %#v\n\ngot: %#v", + expected, launchResults) + } + } +}