From f10e84aa16643cec2b3d34bf7e8fa594fe95fcf6 Mon Sep 17 00:00:00 2001 From: Eric Buth Date: Fri, 17 Oct 2014 12:12:45 -0400 Subject: [PATCH 1/2] added block_device attribute --- .../providers/aws/resource_aws_instance.go | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index c1f9261fb..87f31d695 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -1,6 +1,7 @@ package aws import ( + "bytes" "crypto/sha1" "encoding/hex" "fmt" @@ -125,6 +126,52 @@ func resourceAwsInstance() *schema.Resource { Optional: true, }, "tags": tagsSchema(), + + "block_device": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "snapshot_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "volume_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "volume_size": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + + "delete_on_termination": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, + + "encrypted": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + }, + }, + Set: resourceAwsInstanceBlockDevicesHash, + }, }, } } @@ -173,6 +220,22 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { } } + if v := d.Get("block_device"); v != nil { + vs := v.(*schema.Set).List() + if len(vs) > 0 { + runOpts.BlockDevices = make([]ec2.BlockDeviceMapping, len(vs)) + for i, v := range vs { + bd := v.(map[string]interface{}) + runOpts.BlockDevices[i].DeviceName = bd["device_name"].(string) + runOpts.BlockDevices[i].SnapshotId = bd["snapshot_id"].(string) + runOpts.BlockDevices[i].VolumeType = bd["volume_type"].(string) + runOpts.BlockDevices[i].VolumeSize = int64(bd["volume_size"].(int)) + runOpts.BlockDevices[i].DeleteOnTermination = bd["delete_on_termination"].(bool) + runOpts.BlockDevices[i].Encrypted = bd["encrypted"].(bool) + } + } + } + // Create the instance log.Printf("[DEBUG] Run configuration: %#v", runOpts) runResp, err := ec2conn.RunInstances(runOpts) @@ -360,6 +423,30 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { } d.Set("security_groups", sgs) + volIDs := make([]string, len(instance.BlockDevices)) + bdByVolID := make(map[string]ec2.BlockDevice) + for i, bd := range instance.BlockDevices { + volIDs[i] = bd.VolumeId + bdByVolID[bd.VolumeId] = bd + } + + volResp, err := ec2conn.Volumes(volIDs, ec2.NewFilter()) + if err != nil { + return err + } + + bds := make([]map[string]interface{}, len(instance.BlockDevices)) + for i, vol := range volResp.Volumes { + bds[i] = make(map[string]interface{}) + bds[i]["device_name"] = bdByVolID[vol.VolumeId].DeviceName + bds[i]["snapshot_id"] = vol.SnapshotId + bds[i]["volume_type"] = vol.VolumeType + bds[i]["volume_size"] = vol.Size + bds[i]["delete_on_termination"] = bdByVolID[vol.VolumeId].DeleteOnTermination + bds[i]["encrypted"] = vol.Encrypted + } + d.Set("block_device", bds) + return nil } @@ -388,3 +475,15 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRe return i, i.State.Name, nil } } + +func resourceAwsInstanceBlockDevicesHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["device_name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["snapshot_id"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["volume_type"].(string))) + buf.WriteString(fmt.Sprintf("%d-", m["volume_size"].(int))) + buf.WriteString(fmt.Sprintf("%t-", m["delete_on_termination"].(bool))) + buf.WriteString(fmt.Sprintf("%t-", m["encrypted"].(bool))) + return hashcode.String(buf.String()) +} From 9fa25c40b2261311d4eef4dfd43917eabecff43c Mon Sep 17 00:00:00 2001 From: Eric Buth Date: Fri, 31 Oct 2014 16:25:16 -0400 Subject: [PATCH 2/2] tests for aws instance block_device --- .../aws/resource_aws_instance_test.go | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index ff7087732..fc72ff452 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -64,6 +64,44 @@ func TestAccAWSInstance_normal(t *testing.T) { }) } +func TestAccAWSInstance_blockDevicesCheck(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.BlockDevice) + for _, blockDevice := range v.BlockDevices { + blockDevices[blockDevice.DeviceName] = blockDevice + } + + // Check if the secondary block device exists. + if _, ok := blockDevices["/dev/sdb"]; !ok { + fmt.Errorf("block device doesn't exist: /dev/sdb") + } + + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigBlockDevices, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "aws_instance.foo", &v), + testCheck(), + ), + }, + }, + }) +} + func TestAccAWSInstance_sourceDestCheck(t *testing.T) { var v ec2.Instance @@ -233,6 +271,19 @@ resource "aws_instance" "foo" { } ` +const testAccInstanceConfigBlockDevices = ` +resource "aws_instance" "foo" { + # us-west-2 + ami = "ami-55a7ea65" + instance_type = "m1.small" + block_device { + device_name = "/dev/sdb" + volume_type = "gp2" + volume_size = 10 + } +} +` + const testAccInstanceConfigSourceDest = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16"