providers/aws: derive instance root_block_device name

I was working on building a validation to check the user-provided
"device_name" for "root_block_device" on AWS Instances, when I realized
that if I can check it, I might as well just derive it automatically!

So that's what we do here - when you customize the details of the root
block device, device name is just comes from the selected AMI.
This commit is contained in:
Paul Hinze 2015-03-23 11:58:45 -05:00
parent 55d682482a
commit a4e80b6313
3 changed files with 31 additions and 23 deletions

View File

@ -265,13 +265,6 @@ func resourceAwsInstance() *schema.Resource {
ForceNew: true, ForceNew: true,
}, },
"device_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "/dev/sda1",
},
"iops": &schema.Schema{ "iops": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Optional: true, Optional: true,
@ -298,7 +291,6 @@ func resourceAwsInstance() *schema.Resource {
var buf bytes.Buffer var buf bytes.Buffer
m := v.(map[string]interface{}) m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool)))
buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string)))
// See the NOTE in "ebs_block_device" for why we skip iops here. // See the NOTE in "ebs_block_device" for why we skip iops here.
// buf.WriteString(fmt.Sprintf("%d-", m["iops"].(int))) // buf.WriteString(fmt.Sprintf("%d-", m["iops"].(int)))
buf.WriteString(fmt.Sprintf("%d-", m["volume_size"].(int))) buf.WriteString(fmt.Sprintf("%d-", m["volume_size"].(int)))
@ -478,10 +470,14 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
ebs.IOPS = aws.Integer(v) ebs.IOPS = aws.Integer(v)
} }
blockDevices = append(blockDevices, ec2.BlockDeviceMapping{ if dn, err := fetchRootDeviceName(d.Get("ami").(string), ec2conn); err == nil {
DeviceName: aws.String(bd["device_name"].(string)), blockDevices = append(blockDevices, ec2.BlockDeviceMapping{
EBS: ebs, DeviceName: dn,
}) EBS: ebs,
})
} else {
return err
}
} }
} }
@ -778,9 +774,6 @@ func readBlockDevicesFromInstance(instance *ec2.Instance, ec2conn *ec2.EC2) (map
if instanceBd.EBS != nil && instanceBd.EBS.DeleteOnTermination != nil { if instanceBd.EBS != nil && instanceBd.EBS.DeleteOnTermination != nil {
bd["delete_on_termination"] = *instanceBd.EBS.DeleteOnTermination bd["delete_on_termination"] = *instanceBd.EBS.DeleteOnTermination
} }
if instanceBd.DeviceName != nil {
bd["device_name"] = *instanceBd.DeviceName
}
if vol.Size != nil { if vol.Size != nil {
bd["volume_size"] = *vol.Size bd["volume_size"] = *vol.Size
} }
@ -794,6 +787,9 @@ func readBlockDevicesFromInstance(instance *ec2.Instance, ec2conn *ec2.EC2) (map
if blockDeviceIsRoot(instanceBd, instance) { if blockDeviceIsRoot(instanceBd, instance) {
blockDevices["root"] = bd blockDevices["root"] = bd
} else { } else {
if instanceBd.DeviceName != nil {
bd["device_name"] = *instanceBd.DeviceName
}
if vol.Encrypted != nil { if vol.Encrypted != nil {
bd["encrypted"] = *vol.Encrypted bd["encrypted"] = *vol.Encrypted
} }
@ -813,3 +809,21 @@ func blockDeviceIsRoot(bd ec2.InstanceBlockDeviceMapping, instance *ec2.Instance
instance.RootDeviceName != nil && instance.RootDeviceName != nil &&
*bd.DeviceName == *instance.RootDeviceName) *bd.DeviceName == *instance.RootDeviceName)
} }
func fetchRootDeviceName(ami string, conn *ec2.EC2) (aws.StringValue, error) {
if ami == "" {
return nil, fmt.Errorf("Cannot fetch root device name for blank AMI ID.")
}
log.Printf("[DEBUG] Describing AMI %q to get root block device name", ami)
req := &ec2.DescribeImagesRequest{ImageIDs: []string{ami}}
if res, err := conn.DescribeImages(req); err == nil {
if len(res.Images) == 1 {
return res.Images[0].RootDeviceName, nil
} else {
return nil, fmt.Errorf("Expected 1 AMI for ID: %s, got: %#v", ami, res.Images)
}
} else {
return nil, err
}
}

View File

@ -140,11 +140,9 @@ func TestAccAWSInstance_blockDevices(t *testing.T) {
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_instance.foo", "root_block_device.#", "1"), "aws_instance.foo", "root_block_device.#", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_instance.foo", "root_block_device.1246122048.device_name", "/dev/sda1"), "aws_instance.foo", "root_block_device.1023169747.volume_size", "11"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_instance.foo", "root_block_device.1246122048.volume_size", "11"), "aws_instance.foo", "root_block_device.1023169747.volume_type", "gp2"),
resource.TestCheckResourceAttr(
"aws_instance.foo", "root_block_device.1246122048.volume_type", "gp2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_instance.foo", "ebs_block_device.#", "2"), "aws_instance.foo", "ebs_block_device.#", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
@ -467,7 +465,6 @@ resource "aws_instance" "foo" {
instance_type = "m1.small" instance_type = "m1.small"
root_block_device { root_block_device {
device_name = "/dev/sda1"
volume_type = "gp2" volume_type = "gp2"
volume_size = 11 volume_size = 11
} }

View File

@ -66,9 +66,6 @@ to understand the implications of using these attributes.
The `root_block_device` mapping supports the following: The `root_block_device` mapping supports the following:
* `device_name` - The name of the root device on the target instance. Must
match the root device as defined in the AMI. Defaults to `"/dev/sda1"`, which
is the typical root volume for Linux instances.
* `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`, * `volume_type` - (Optional) The type of volume. Can be `"standard"`, `"gp2"`,
or `"io1"`. (Default: `"standard"`). or `"io1"`. (Default: `"standard"`).
* `volume_size` - (Optional) The size of the volume in gigabytes. * `volume_size` - (Optional) The size of the volume in gigabytes.