From d998e883fb5778c9a512058c79ffb13657317171 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 16 Nov 2015 09:45:21 -0600 Subject: [PATCH 1/4] providers/aws: Update Spot Instance request to provide connection information --- .../aws/resource_aws_spot_instance_request.go | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/builtin/providers/aws/resource_aws_spot_instance_request.go b/builtin/providers/aws/resource_aws_spot_instance_request.go index 89384246c..e7f8eef09 100644 --- a/builtin/providers/aws/resource_aws_spot_instance_request.go +++ b/builtin/providers/aws/resource_aws_spot_instance_request.go @@ -97,6 +97,14 @@ func resourceAwsSpotInstanceRequestCreate(d *schema.ResourceData, meta interface }, } + // If the instance is configured with a Network Interface (a subnet, has + // public IP, etc), then the instanceOpts.SecurityGroupIds and SubnetId will + // be nil + if len(instanceOpts.NetworkInterfaces) > 0 { + spotOpts.LaunchSpecification.SecurityGroupIds = instanceOpts.NetworkInterfaces[0].Groups + spotOpts.LaunchSpecification.SubnetId = instanceOpts.NetworkInterfaces[0].SubnetId + } + // Make the spot instance request log.Printf("[DEBUG] Requesting spot bid opts: %s", spotOpts) resp, err := conn.RequestSpotInstances(spotOpts) @@ -172,10 +180,63 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{} // Instance ID is not set if the request is still pending if request.InstanceId != nil { d.Set("spot_instance_id", *request.InstanceId) + if err := readInstance(d, meta); err != nil { + return fmt.Errorf("[ERR] Error reading Spot Instance Data: %s", err) + } } d.Set("spot_request_state", *request.State) d.Set("tags", tagsToMap(request.Tags)) + // return nil + // let's read the instance data... + return readInstance(d, meta) +} + +func readInstance(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{aws.String(d.Get("spot_instance_id").(string))}, + }) + if err != nil { + // If the instance was not found, return nil so that we can show + // that the instance is gone. + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" { + return fmt.Errorf("no instance found") + } + + // Some other error, report it + return err + } + + // If nothing was found, then return no state + if len(resp.Reservations) == 0 { + return fmt.Errorf("no instances found") + } + + instance := resp.Reservations[0].Instances[0] + + // Set these fields for connection information + if instance != nil { + d.Set("public_dns", instance.PublicDnsName) + d.Set("public_ip", instance.PublicIpAddress) + d.Set("private_dns", instance.PrivateDnsName) + d.Set("private_ip", instance.PrivateIpAddress) + } + + // set connection information + if instance.PublicIpAddress != nil { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": *instance.PublicIpAddress, + }) + } else if instance.PrivateIpAddress != nil { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": *instance.PrivateIpAddress, + }) + } + return nil } From f31b30d4a5025b54dd329c744812c0ae520f3d92 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 16 Nov 2015 14:51:14 -0600 Subject: [PATCH 2/4] minor tweaks to connection info setup --- .../aws/resource_aws_spot_instance_request.go | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/builtin/providers/aws/resource_aws_spot_instance_request.go b/builtin/providers/aws/resource_aws_spot_instance_request.go index e7f8eef09..eb9933922 100644 --- a/builtin/providers/aws/resource_aws_spot_instance_request.go +++ b/builtin/providers/aws/resource_aws_spot_instance_request.go @@ -187,8 +187,7 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{} d.Set("spot_request_state", *request.State) d.Set("tags", tagsToMap(request.Tags)) - // return nil - // let's read the instance data... + // Read the instance data, setting up connection information return readInstance(d, meta) } @@ -222,19 +221,19 @@ func readInstance(d *schema.ResourceData, meta interface{}) error { d.Set("public_ip", instance.PublicIpAddress) d.Set("private_dns", instance.PrivateDnsName) d.Set("private_ip", instance.PrivateIpAddress) - } - // set connection information - if instance.PublicIpAddress != nil { - d.SetConnInfo(map[string]string{ - "type": "ssh", - "host": *instance.PublicIpAddress, - }) - } else if instance.PrivateIpAddress != nil { - d.SetConnInfo(map[string]string{ - "type": "ssh", - "host": *instance.PrivateIpAddress, - }) + // set connection information + if instance.PublicIpAddress != nil { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": *instance.PublicIpAddress, + }) + } else if instance.PrivateIpAddress != nil { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": *instance.PrivateIpAddress, + }) + } } return nil From 66ad974193313be14c58df2acbc2dbced218c9a2 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 16 Nov 2015 15:11:44 -0600 Subject: [PATCH 3/4] add acceptance test for spot instanace updates --- ...resource_aws_spot_instance_request_test.go | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/builtin/providers/aws/resource_aws_spot_instance_request_test.go b/builtin/providers/aws/resource_aws_spot_instance_request_test.go index be2ae3956..d329b7d34 100644 --- a/builtin/providers/aws/resource_aws_spot_instance_request_test.go +++ b/builtin/providers/aws/resource_aws_spot_instance_request_test.go @@ -62,6 +62,26 @@ func TestAccAWSSpotInstanceRequest_vpc(t *testing.T) { }) } +func TestAccAWSSpotInstanceRequest_SubnetAndSG(t *testing.T) { + var sir ec2.SpotInstanceRequest + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSpotInstanceRequestDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSSpotInstanceRequestConfig_SubnetAndSG, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSpotInstanceRequestExists( + "aws_spot_instance_request.foo", &sir), + testAccCheckAWSSpotInstanceRequest_InstanceAttributes(&sir), + ), + }, + }, + }) +} + func testCheckKeyPair(keyName string, sir *ec2.SpotInstanceRequest) resource.TestCheckFunc { return func(*terraform.State) error { if sir.LaunchSpecification.KeyName == nil { @@ -178,6 +198,44 @@ func testAccCheckAWSSpotInstanceRequestAttributes( } } +func testAccCheckAWSSpotInstanceRequest_InstanceAttributes( + sir *ec2.SpotInstanceRequest) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesInput{ + InstanceIds: []*string{sir.InstanceId}, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidInstanceID.NotFound" { + return fmt.Errorf("Spot Instance not found") + } + return err + } + + // If nothing was found, then return no state + if len(resp.Reservations) == 0 { + return fmt.Errorf("Spot Instance not found") + } + + instance := resp.Reservations[0].Instances[0] + + var sgMatch bool + for _, s := range instance.SecurityGroups { + // Hardcoded name for the security group that should be added inside the + // VPC + if *s.GroupName == "tf_test_sg_ssh" { + sgMatch = true + } + } + + if !sgMatch { + return fmt.Errorf("Error in matching Spot Instance Security Group, expected 'tf_test_sg_ssh', got %s", instance.SecurityGroups) + } + + return nil + } +} + func testAccCheckAWSSpotInstanceRequestAttributesVPC( sir *ec2.SpotInstanceRequest) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -249,3 +307,44 @@ resource "aws_spot_instance_request" "foo_VPC" { } } ` + +const testAccAWSSpotInstanceRequestConfig_SubnetAndSG = ` +resource "aws_spot_instance_request" "foo" { + ami = "ami-6f6d635f" + spot_price = "0.05" + instance_type = "t1.micro" + wait_for_fulfillment = true + subnet_id = "${aws_subnet.tf_test_subnet.id}" + vpc_security_group_ids = ["${aws_security_group.tf_test_sg_ssh.id}"] + associate_public_ip_address = true +} + +resource "aws_vpc" "default" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + + tags { + Name = "tf_test_vpc" + } +} + +resource "aws_subnet" "tf_test_subnet" { + vpc_id = "${aws_vpc.default.id}" + cidr_block = "10.0.0.0/24" + map_public_ip_on_launch = true + + tags { + Name = "tf_test_subnet" + } +} + +resource "aws_security_group" "tf_test_sg_ssh" { + name = "tf_test_sg_ssh" + description = "tf_test_sg_ssh" + vpc_id = "${aws_vpc.default.id}" + + tags { + Name = "tf_test_sg_ssh" + } +} +` From 70f1c9c1e6298ad551af2892a38bfed44b98a235 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 16 Nov 2015 15:16:41 -0600 Subject: [PATCH 4/4] remove duplicate readInstance call --- builtin/providers/aws/resource_aws_spot_instance_request.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_spot_instance_request.go b/builtin/providers/aws/resource_aws_spot_instance_request.go index eb9933922..26a88a11f 100644 --- a/builtin/providers/aws/resource_aws_spot_instance_request.go +++ b/builtin/providers/aws/resource_aws_spot_instance_request.go @@ -180,6 +180,7 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{} // Instance ID is not set if the request is still pending if request.InstanceId != nil { d.Set("spot_instance_id", *request.InstanceId) + // Read the instance data, setting up connection information if err := readInstance(d, meta); err != nil { return fmt.Errorf("[ERR] Error reading Spot Instance Data: %s", err) } @@ -187,8 +188,7 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{} d.Set("spot_request_state", *request.State) d.Set("tags", tagsToMap(request.Tags)) - // Read the instance data, setting up connection information - return readInstance(d, meta) + return nil } func readInstance(d *schema.ResourceData, meta interface{}) error {