provider/aws: Add "no_device" support to ephemeral block devices (#10547)
Fixes #8455, #5390 This add a new `no_device` attribute to `ephemeral_block_device` block, which allows users omit ephemeral devices from AMI's predefined block device mappings, which is useful for EBS-only instance types.
This commit is contained in:
parent
134b438c92
commit
80afc6759b
|
@ -268,7 +268,12 @@ func resourceAwsInstance() *schema.Resource {
|
||||||
|
|
||||||
"virtual_name": &schema.Schema{
|
"virtual_name": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"no_device": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -277,6 +282,9 @@ func resourceAwsInstance() *schema.Resource {
|
||||||
m := v.(map[string]interface{})
|
m := v.(map[string]interface{})
|
||||||
buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
|
buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
|
||||||
buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
|
buf.WriteString(fmt.Sprintf("%s-", m["virtual_name"].(string)))
|
||||||
|
if v, ok := m["no_device"].(bool); ok && v {
|
||||||
|
buf.WriteString(fmt.Sprintf("%t-", v))
|
||||||
|
}
|
||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -936,10 +944,21 @@ func readBlockDeviceMappingsFromConfig(
|
||||||
vL := v.(*schema.Set).List()
|
vL := v.(*schema.Set).List()
|
||||||
for _, v := range vL {
|
for _, v := range vL {
|
||||||
bd := v.(map[string]interface{})
|
bd := v.(map[string]interface{})
|
||||||
blockDevices = append(blockDevices, &ec2.BlockDeviceMapping{
|
bdm := &ec2.BlockDeviceMapping{
|
||||||
DeviceName: aws.String(bd["device_name"].(string)),
|
DeviceName: aws.String(bd["device_name"].(string)),
|
||||||
VirtualName: aws.String(bd["virtual_name"].(string)),
|
VirtualName: aws.String(bd["virtual_name"].(string)),
|
||||||
})
|
}
|
||||||
|
if v, ok := bd["no_device"].(bool); ok && v {
|
||||||
|
bdm.NoDevice = aws.String("")
|
||||||
|
// When NoDevice is true, just ignore VirtualName since it's not needed
|
||||||
|
bdm.VirtualName = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if bdm.NoDevice == nil && aws.StringValue(bdm.VirtualName) == "" {
|
||||||
|
return nil, fmt.Errorf("virtual_name cannot be empty when no_device is false or undefined.")
|
||||||
|
}
|
||||||
|
|
||||||
|
blockDevices = append(blockDevices, bdm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -285,6 +285,99 @@ func TestAccAWSInstance_rootInstanceStore(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcctABSInstance_noAMIEphemeralDevices(t *testing.T) {
|
||||||
|
var v ec2.Instance
|
||||||
|
|
||||||
|
testCheck := func() resource.TestCheckFunc {
|
||||||
|
return func(*terraform.State) error {
|
||||||
|
|
||||||
|
// Map out the block devices by name, which should be unique.
|
||||||
|
blockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping)
|
||||||
|
for _, blockDevice := range v.BlockDeviceMappings {
|
||||||
|
blockDevices[*blockDevice.DeviceName] = blockDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the root block device exists.
|
||||||
|
if _, ok := blockDevices["/dev/sda1"]; !ok {
|
||||||
|
return fmt.Errorf("block device doesn't exist: /dev/sda1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the secondary block not exists.
|
||||||
|
if _, ok := blockDevices["/dev/sdb"]; ok {
|
||||||
|
return fmt.Errorf("block device exist: /dev/sdb")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the third block device not exists.
|
||||||
|
if _, ok := blockDevices["/dev/sdc"]; ok {
|
||||||
|
return fmt.Errorf("block device exist: /dev/sdc")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "aws_instance.foo",
|
||||||
|
IDRefreshIgnore: []string{
|
||||||
|
"ephemeral_block_device", "security_groups", "vpc_security_groups"},
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckInstanceDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: `
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-01f05461" // This AMI (Ubuntu) contains two ephemerals
|
||||||
|
|
||||||
|
instance_type = "c3.large"
|
||||||
|
|
||||||
|
root_block_device {
|
||||||
|
volume_type = "gp2"
|
||||||
|
volume_size = 11
|
||||||
|
}
|
||||||
|
ephemeral_block_device {
|
||||||
|
device_name = "/dev/sdb"
|
||||||
|
no_device = true
|
||||||
|
}
|
||||||
|
ephemeral_block_device {
|
||||||
|
device_name = "/dev/sdc"
|
||||||
|
no_device = true
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckInstanceExists(
|
||||||
|
"aws_instance.foo", &v),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ami", "ami-01f05461"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ebs_optimized", "false"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "instance_type", "c3.large"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "root_block_device.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "root_block_device.0.volume_size", "11"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "root_block_device.0.volume_type", "gp2"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ebs_block_device.#", "0"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ephemeral_block_device.#", "2"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ephemeral_block_device.172787947.device_name", "/dev/sdb"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ephemeral_block_device.172787947.no_device", "true"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ephemeral_block_device.3336996981.device_name", "/dev/sdc"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_instance.foo", "ephemeral_block_device.3336996981.no_device", "true"),
|
||||||
|
testCheck(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
|
func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
|
|
|
@ -126,9 +126,10 @@ Modifying any `ebs_block_device` currently requires resource replacement.
|
||||||
Each `ephemeral_block_device` supports the following:
|
Each `ephemeral_block_device` supports the following:
|
||||||
|
|
||||||
* `device_name` - The name of the block device to mount on the instance.
|
* `device_name` - The name of the block device to mount on the instance.
|
||||||
* `virtual_name` - The [Instance Store Device
|
* `virtual_name` - (Optional) The [Instance Store Device
|
||||||
Name](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#InstanceStoreDeviceNames)
|
Name](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html#InstanceStoreDeviceNames)
|
||||||
(e.g. `"ephemeral0"`)
|
(e.g. `"ephemeral0"`).
|
||||||
|
* `no_device` - (Optional) Suppresses the specified device included in the AMI's block device mapping.
|
||||||
|
|
||||||
Each AWS Instance type has a different set of Instance Store block devices
|
Each AWS Instance type has a different set of Instance Store block devices
|
||||||
available for attachment. AWS [publishes a
|
available for attachment. AWS [publishes a
|
||||||
|
|
Loading…
Reference in New Issue