From cf53778de59f2dc7a4dc5706f76c74b0b53e3b75 Mon Sep 17 00:00:00 2001 From: Jon Benson Date: Tue, 8 Sep 2015 16:23:25 -0500 Subject: [PATCH 01/73] Change amazon.ami to amazon.image --- website/source/docs/providers/atlas/r/artifact.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/atlas/r/artifact.html.markdown b/website/source/docs/providers/atlas/r/artifact.html.markdown index 7c8be2985..0a7b7f523 100644 --- a/website/source/docs/providers/atlas/r/artifact.html.markdown +++ b/website/source/docs/providers/atlas/r/artifact.html.markdown @@ -24,7 +24,7 @@ to this artifact will trigger a change to that instance. # Read the AMI resource "atlas_artifact" "web" { name = "hashicorp/web" - type = "amazon.ami" + type = "amazon.image" build = "latest" metadata { arch = "386" From c60a963908dcd5445783fd126da1ad454479a418 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 12 Nov 2015 15:15:47 -0600 Subject: [PATCH 02/73] providers/aws: Retry deleting IAM Server Cert on dependency violation This will retry deleting a server cert if it throws an error about being in use with an ELB (that we've likely just deleted) Includes test for ELB+IAM SSL cert bug dependency violation --- builtin/providers/aws/resource_aws_elb.go | 20 ++- .../providers/aws/resource_aws_elb_test.go | 121 ++++++++++++++++++ .../resource_aws_iam_server_certificate.go | 22 +++- builtin/providers/aws/structure.go | 34 ++--- builtin/providers/aws/structure_test.go | 54 ++++---- 5 files changed, 200 insertions(+), 51 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 5ff3b3b28..63794fd2e 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -6,6 +6,7 @@ import ( "log" "regexp" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -256,8 +257,23 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts) - if _, err := elbconn.CreateLoadBalancer(elbOpts); err != nil { - return fmt.Errorf("Error creating ELB: %s", err) + err = resource.Retry(1*time.Minute, func() error { + _, err := elbconn.CreateLoadBalancer(elbOpts) + + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + // Check for IAM SSL Cert error, eventual consistancy issue + if awsErr.Code() == "CertificateNotFound" { + return fmt.Errorf("[WARN] Error creating ELB Listener with SSL Cert, retrying: %s", err) + } + } + return resource.RetryError{Err: err} + } + return nil + }) + + if err != nil { + return err } // Assign the elb's unique identifier for use later diff --git a/builtin/providers/aws/resource_aws_elb_test.go b/builtin/providers/aws/resource_aws_elb_test.go index 6dad03e56..6ccc5cd66 100644 --- a/builtin/providers/aws/resource_aws_elb_test.go +++ b/builtin/providers/aws/resource_aws_elb_test.go @@ -179,6 +179,33 @@ func TestAccAWSELB_tags(t *testing.T) { }) } +func TestAccAWSELB_iam_server_cert(t *testing.T) { + var conf elb.LoadBalancerDescription + // var td elb.TagDescription + testCheck := func(*terraform.State) error { + if len(conf.ListenerDescriptions) != 1 { + return fmt.Errorf( + "TestAccAWSELB_iam_server_cert expected 1 listener, got %d", + len(conf.ListenerDescriptions)) + } + return nil + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSELBDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccELBIAMServerCertConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSELBExists("aws_elb.bar", &conf), + testCheck, + ), + }, + }, + }) +} + func testAccLoadTags(conf *elb.LoadBalancerDescription, td *elb.TagDescription) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).elbconn @@ -1001,3 +1028,97 @@ resource "aws_security_group" "bar" { } } ` + +// This IAM Server config is lifted from +// builtin/providers/aws/resource_aws_iam_server_certificate_test.go +var testAccELBIAMServerCertConfig = ` +resource "aws_iam_server_certificate" "test_cert" { + name = "terraform-test-cert" + certificate_body = < Date: Thu, 12 Nov 2015 16:20:54 -0600 Subject: [PATCH 03/73] add debugging statements to add/remove listeners --- builtin/providers/aws/resource_aws_elb.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 63794fd2e..faf0b8add 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -410,6 +410,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { LoadBalancerPorts: ports, } + log.Printf("[DEBUG] ELB Delete Listeners opts: %s", deleteListenersOpts) _, err := elbconn.DeleteLoadBalancerListeners(deleteListenersOpts) if err != nil { return fmt.Errorf("Failure removing outdated ELB listeners: %s", err) @@ -422,6 +423,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { Listeners: add, } + log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts) _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts) if err != nil { return fmt.Errorf("Failure adding new or updated ELB listeners: %s", err) From b81f9a9c52996fa737a21d173a6c18621f951bb7 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 13 Nov 2015 10:34:15 -0600 Subject: [PATCH 04/73] provider/aws: Fix issue with LB Cookie Stickiness and empty expiration period --- .../aws/resource_aws_lb_cookie_stickiness_policy.go | 12 +++++++++--- .../resource_aws_lb_cookie_stickiness_policy_test.go | 1 - 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go index bed01aadd..a5189fdc2 100644 --- a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go +++ b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "log" "strings" "github.com/aws/aws-sdk-go/aws" @@ -51,11 +52,15 @@ func resourceAwsLBCookieStickinessPolicyCreate(d *schema.ResourceData, meta inte // Provision the LBStickinessPolicy lbspOpts := &elb.CreateLBCookieStickinessPolicyInput{ - CookieExpirationPeriod: aws.Int64(int64(d.Get("cookie_expiration_period").(int))), - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - PolicyName: aws.String(d.Get("name").(string)), + LoadBalancerName: aws.String(d.Get("load_balancer").(string)), + PolicyName: aws.String(d.Get("name").(string)), } + if v := d.Get("cookie_expiration_period").(int); v > 0 { + lbspOpts.CookieExpirationPeriod = aws.Int64(int64(v)) + } + + log.Printf("[DEBUG] LB Cookie Stickiness Policy opts: %#v", lbspOpts) if _, err := elbconn.CreateLBCookieStickinessPolicy(lbspOpts); err != nil { return fmt.Errorf("Error creating LBCookieStickinessPolicy: %s", err) } @@ -66,6 +71,7 @@ func resourceAwsLBCookieStickinessPolicyCreate(d *schema.ResourceData, meta inte PolicyNames: []*string{aws.String(d.Get("name").(string))}, } + log.Printf("[DEBUG] LB Cookie Stickiness create configuration: %#v", setLoadBalancerOpts) if _, err := elbconn.SetLoadBalancerPoliciesOfListener(setLoadBalancerOpts); err != nil { return fmt.Errorf("Error setting LBCookieStickinessPolicy: %s", err) } diff --git a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy_test.go b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy_test.go index e1073e5e8..765d2ffcd 100644 --- a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy_test.go +++ b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy_test.go @@ -94,7 +94,6 @@ resource "aws_lb_cookie_stickiness_policy" "foo" { name = "foo-policy" load_balancer = "${aws_elb.lb.id}" lb_port = 80 - cookie_expiration_period = 600 } ` From e94fcdb9df41abafc434ef287cdddb421ec8278c Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 13 Nov 2015 10:46:27 -0600 Subject: [PATCH 05/73] add validation for cookie stickiness --- .../aws/resource_aws_lb_cookie_stickiness_policy.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go index a5189fdc2..ea2215b6a 100644 --- a/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go +++ b/builtin/providers/aws/resource_aws_lb_cookie_stickiness_policy.go @@ -42,6 +42,14 @@ func resourceAwsLBCookieStickinessPolicy() *schema.Resource { Type: schema.TypeInt, Optional: true, ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(int) + if value <= 0 { + es = append(es, fmt.Errorf( + "LB Cookie Expiration Period must be greater than zero if specified")) + } + return + }, }, }, } From d998e883fb5778c9a512058c79ffb13657317171 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 16 Nov 2015 09:45:21 -0600 Subject: [PATCH 06/73] 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 07/73] 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 08/73] 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 09/73] 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 { From 203a5651614ee9af4d8900ee67d530ab1bf025e2 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 18 Nov 2015 17:16:03 -0600 Subject: [PATCH 10/73] scripts: check for the correct env vars in dist.sh we need AWS keys now, not bintray keys --- scripts/dist.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/dist.sh b/scripts/dist.sh index 1488d1b71..a9b57cd87 100755 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -9,8 +9,8 @@ if [ -z $VERSION ]; then fi # Make sure we have a bintray API key -if [ -z $BINTRAY_API_KEY ]; then - echo "Please set your bintray API key in the BINTRAY_API_KEY env var." +if [[ -z $AWS_ACCESS_KEY_ID || -z $AWS_SECRET_ACCESS_KEY ]]; then + echo "Please set AWS access keys as env vars before running this script." exit 1 fi From 6ae3218f8a5034796a33c16f018800d3ea180ad6 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Thu, 19 Nov 2015 16:06:30 +0200 Subject: [PATCH 11/73] Add failing tests for JSON configuration parsing Reproduces the issue reported by @svanharmelen in #3964. --- config/loader_test.go | 96 +++++++++++++++++++++++++ config/test-fixtures/attributes.tf | 15 ++++ config/test-fixtures/attributes.tf.json | 27 +++++++ 3 files changed, 138 insertions(+) create mode 100644 config/test-fixtures/attributes.tf create mode 100644 config/test-fixtures/attributes.tf.json diff --git a/config/loader_test.go b/config/loader_test.go index 18b26f9c5..bc1deb890 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -557,6 +557,102 @@ func TestLoad_temporary_files(t *testing.T) { } } +func TestLoad_hclAttributes(t *testing.T) { + c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf")) + if err != nil { + t.Fatalf("Bad: %s", err) + } + + if c == nil { + t.Fatal("config should not be nil") + } + + actual := resourcesStr(c.Resources) + print(actual) + if actual != strings.TrimSpace(jsonAttributeStr) { + t.Fatalf("bad:\n%s", actual) + } + + r := c.Resources[0] + if r.Name != "test" && r.Type != "cloudstack_firewall" { + t.Fatalf("Bad: %#v", r) + } + + raw := r.RawConfig + if raw.Raw["ipaddress"] != "192.168.0.1" { + t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) + } + + rule := raw.Raw["rule"].([]map[string]interface{})[0] + if rule["protocol"] != "tcp" { + t.Fatalf("Bad: %s", rule["protocol"]) + } + + if rule["source_cidr"] != "10.0.0.0/8" { + t.Fatalf("Bad: %s", rule["source_cidr"]) + } + + ports := rule["ports"].([]interface{}) + + if ports[0] != "80" { + t.Fatalf("Bad ports: %s", ports[0]) + } + if ports[1] != "1000-2000" { + t.Fatalf("Bad ports: %s", ports[1]) + } +} + +func TestLoad_jsonAttributes(t *testing.T) { + c, err := LoadFile(filepath.Join(fixtureDir, "attributes.tf.json")) + if err != nil { + t.Fatalf("Bad: %s", err) + } + + if c == nil { + t.Fatal("config should not be nil") + } + + actual := resourcesStr(c.Resources) + print(actual) + if actual != strings.TrimSpace(jsonAttributeStr) { + t.Fatalf("bad:\n%s", actual) + } + + r := c.Resources[0] + if r.Name != "test" && r.Type != "cloudstack_firewall" { + t.Fatalf("Bad: %#v", r) + } + + raw := r.RawConfig + if raw.Raw["ipaddress"] != "192.168.0.1" { + t.Fatalf("Bad: %s", raw.Raw["ipAddress"]) + } + + rule := raw.Raw["rule"].([]map[string]interface{})[0] + if rule["protocol"] != "tcp" { + t.Fatalf("Bad: %s", rule["protocol"]) + } + + if rule["source_cidr"] != "10.0.0.0/8" { + t.Fatalf("Bad: %s", rule["source_cidr"]) + } + + ports := rule["ports"].([]interface{}) + + if ports[0] != "80" { + t.Fatalf("Bad ports: %s", ports[0]) + } + if ports[1] != "1000-2000" { + t.Fatalf("Bad ports: %s", ports[1]) + } +} + +const jsonAttributeStr = ` +cloudstack_firewall[test] (x1) + ipaddress + rule +` + const heredocProvidersStr = ` aws access_key diff --git a/config/test-fixtures/attributes.tf b/config/test-fixtures/attributes.tf new file mode 100644 index 000000000..2fe0291e0 --- /dev/null +++ b/config/test-fixtures/attributes.tf @@ -0,0 +1,15 @@ +provider "cloudstack" { + api_url = "bla" + api_key = "bla" + secret_key = "bla" +} + +resource "cloudstack_firewall" "test" { + ipaddress = "192.168.0.1" + + rule { + source_cidr = "10.0.0.0/8" + protocol = "tcp" + ports = ["80", "1000-2000"] + } +} diff --git a/config/test-fixtures/attributes.tf.json b/config/test-fixtures/attributes.tf.json new file mode 100644 index 000000000..773274d48 --- /dev/null +++ b/config/test-fixtures/attributes.tf.json @@ -0,0 +1,27 @@ +{ + "provider": { + "cloudstack": { + "api_url": "bla", + "api_key": "bla", + "secret_key": "bla" + } + }, + "resource": { + "cloudstack_firewall": { + "test": { + "ipaddress": "192.168.0.1", + "rule": [ + { + "source_cidr": "10.0.0.0/8", + "protocol": "tcp", + "ports": [ + "80", + "1000-2000" + ] + } + ] + } + } + } +} + From 15e79270098062666f4305ed267bfda8e6a2e7e4 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 19 Nov 2015 09:31:33 -0600 Subject: [PATCH 12/73] config: test covering escaped quotes syntax error This was never intended to be valid syntax, but it worked in the old HCL parser, and we've found a decent number of examples of it in the wild. Fixed in https://github.com/hashicorp/hcl/pull/62 and we'll keep this test in Terraform to cover the behavior. --- config/loader_test.go | 27 +++++++++++++++++++++++++++ config/test-fixtures/escapedquotes.tf | 7 +++++++ 2 files changed, 34 insertions(+) create mode 100644 config/test-fixtures/escapedquotes.tf diff --git a/config/loader_test.go b/config/loader_test.go index 18b26f9c5..5be023cb1 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -70,6 +70,26 @@ func TestLoadFileHeredoc(t *testing.T) { } } +func TestLoadFileEscapedQuotes(t *testing.T) { + c, err := LoadFile(filepath.Join(fixtureDir, "escapedquotes.tf")) + if err != nil { + t.Fatalf("err: %s", err) + } + + if c == nil { + t.Fatal("config should not be nil") + } + + if c.Dir != "" { + t.Fatalf("bad: %#v", c.Dir) + } + + actual := resourcesStr(c.Resources) + if actual != strings.TrimSpace(escapedquotesResourcesStr) { + t.Fatalf("bad:\n%s", actual) + } +} + func TestLoadFileBasic(t *testing.T) { c, err := LoadFile(filepath.Join(fixtureDir, "basic.tf")) if err != nil { @@ -571,6 +591,13 @@ aws_iam_policy[policy] (x1) policy ` +const escapedquotesResourcesStr = ` +aws_instance[quotes] (x1) + ami + vars + user: var.ami +` + const basicOutputsStr = ` web_ip vars diff --git a/config/test-fixtures/escapedquotes.tf b/config/test-fixtures/escapedquotes.tf new file mode 100644 index 000000000..4fe9a020b --- /dev/null +++ b/config/test-fixtures/escapedquotes.tf @@ -0,0 +1,7 @@ +variable "ami" { + default = [ "ami", "abc123" ] +} + +resource "aws_instance" "quotes" { + ami = "${join(\",\", var.ami)}" +} From 08743474783e61423ae73128fe65f952700f5f0f Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 15:37:05 -0600 Subject: [PATCH 13/73] update ami id for test --- builtin/providers/aws/resource_aws_ami_copy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_ami_copy_test.go b/builtin/providers/aws/resource_aws_ami_copy_test.go index e844853ab..0a469a8e0 100644 --- a/builtin/providers/aws/resource_aws_ami_copy_test.go +++ b/builtin/providers/aws/resource_aws_ami_copy_test.go @@ -171,7 +171,7 @@ resource "aws_instance" "test" { // one snapshot in our created AMI. // This is an Amazon Linux HVM AMI. A public HVM AMI is required // because paravirtual images cannot be copied between accounts. - ami = "ami-8fff43e4" + ami = "ami-5449393e" instance_type = "t2.micro" tags { Name = "terraform-acc-ami-copy-victim" From 01b9af40d1034bfe2c4a9da6884fa28d437e897d Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 15:44:40 -0600 Subject: [PATCH 14/73] fix resource name in test --- .../aws/resource_aws_autoscaling_notification_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_autoscaling_notification_test.go b/builtin/providers/aws/resource_aws_autoscaling_notification_test.go index 8002dd885..81fccfea3 100644 --- a/builtin/providers/aws/resource_aws_autoscaling_notification_test.go +++ b/builtin/providers/aws/resource_aws_autoscaling_notification_test.go @@ -240,7 +240,7 @@ resource "aws_autoscaling_notification" "example" { ` const testAccASGNotificationConfig_update = ` -resource "aws_sns_topic" "user_updates" { +resource "aws_sns_topic" "topic_example" { name = "user-updates-topic" } @@ -286,7 +286,7 @@ resource "aws_autoscaling_notification" "example" { "autoscaling:EC2_INSTANCE_TERMINATE", "autoscaling:EC2_INSTANCE_LAUNCH_ERROR" ] - topic_arn = "${aws_sns_topic.user_updates.arn}" + topic_arn = "${aws_sns_topic.topic_example.arn}" }` const testAccASGNotificationConfig_pagination = ` From 75d056c878b651931a85e3ad7e41a5d8ddc4583f Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 19 Nov 2015 16:11:42 -0600 Subject: [PATCH 15/73] provider/digitalocean: comment out test for relative DNS records Until we hear back from DigitalOcean on whether this behavior is supposed to be supported or not. --- .../digitalocean/resource_digitalocean_record_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/builtin/providers/digitalocean/resource_digitalocean_record_test.go b/builtin/providers/digitalocean/resource_digitalocean_record_test.go index 7811ee9c8..94a061656 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_record_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_record_test.go @@ -104,6 +104,15 @@ func TestAccDigitalOceanRecord_HostnameValue(t *testing.T) { }) } +// This test fails with: +// +// POST https://api.digitalocean.com/v2/domains/foobar-test-terraform.com/records: +// 422 Data needs to end with a dot (.) +// +// Which seems like a behavior change on the DO API side. Opened support ticket +// #826791 to ask DigitalOcean about this, and we'll comment out the test for +// now. --phinze +/* func TestAccDigitalOceanRecord_RelativeHostnameValue(t *testing.T) { var record godo.DomainRecord @@ -130,6 +139,7 @@ func TestAccDigitalOceanRecord_RelativeHostnameValue(t *testing.T) { }, }) } +*/ func TestAccDigitalOceanRecord_ExternalHostnameValue(t *testing.T) { var record godo.DomainRecord From 0aedb7eae6841c3883b3aa667da897a8d3646854 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:19:49 -0600 Subject: [PATCH 16/73] mark snapshots as computed for ElastiCache clusters --- builtin/providers/aws/resource_aws_elasticache_cluster.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index 18a4ddf41..d03ae89e8 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -141,6 +141,7 @@ func resourceAwsElasticacheCluster() *schema.Resource { "snapshot_window": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "snapshot_retention_limit": &schema.Schema{ From e91381c4e27d43940cfbf26b47070815363a62e3 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:24:17 -0600 Subject: [PATCH 17/73] error test when env var is not supplied --- builtin/providers/aws/resource_aws_flow_log_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/providers/aws/resource_aws_flow_log_test.go b/builtin/providers/aws/resource_aws_flow_log_test.go index 9f9ca0570..1ffedc3f0 100644 --- a/builtin/providers/aws/resource_aws_flow_log_test.go +++ b/builtin/providers/aws/resource_aws_flow_log_test.go @@ -34,6 +34,9 @@ func TestAccAWSFlowLog_basic(t *testing.T) { func TestAccAWSFlowLog_subnet(t *testing.T) { var flowLog ec2.FlowLog lgn := os.Getenv("LOG_GROUP_NAME") + if lgn == "" { + t.Fatalf("LOG_GROUP_NAME env var needed for TestAccAWSFlowLog_subnet") + } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 325fd751eb003260bc7fb0402053f930b79db50b Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:27:56 -0600 Subject: [PATCH 18/73] update TestAccAWSFlowLog_subnet to use new cloudwatch resource, not needing env var anymore --- builtin/providers/aws/resource_aws_flow_log_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_flow_log_test.go b/builtin/providers/aws/resource_aws_flow_log_test.go index 1ffedc3f0..02fb3cfd8 100644 --- a/builtin/providers/aws/resource_aws_flow_log_test.go +++ b/builtin/providers/aws/resource_aws_flow_log_test.go @@ -33,10 +33,6 @@ func TestAccAWSFlowLog_basic(t *testing.T) { func TestAccAWSFlowLog_subnet(t *testing.T) { var flowLog ec2.FlowLog - lgn := os.Getenv("LOG_GROUP_NAME") - if lgn == "" { - t.Fatalf("LOG_GROUP_NAME env var needed for TestAccAWSFlowLog_subnet") - } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,7 +40,7 @@ func TestAccAWSFlowLog_subnet(t *testing.T) { CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: fmt.Sprintf(testAccFlowLogConfig_subnet, lgn), + Config: testAccFlowLogConfig_subnet, Check: resource.ComposeTestCheckFunc( testAccCheckFlowLogExists("aws_flow_log.test_flow_log_subnet", &flowLog), testAccCheckAWSFlowLogAttributes(&flowLog), @@ -203,11 +199,14 @@ resource "aws_iam_role" "test_role" { } EOF } +resource "aws_cloudwatch_log_group" "foobar" { + name = "foo-bar" +} resource "aws_flow_log" "test_flow_log_subnet" { # log_group_name needs to exist before hand # until we have a CloudWatch Log Group Resource - log_group_name = "%s" + log_group_name = "${aws_cloudwatch_log_group.foobar.name}" iam_role_arn = "${aws_iam_role.test_role.arn}" subnet_id = "${aws_subnet.test_subnet.id}" traffic_type = "ALL" From 3d089143c62bb6f37a3c1407008a6494d62a389a Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 19 Nov 2015 16:28:24 -0600 Subject: [PATCH 19/73] provider/azure: fix hosted service acctest Just some basic bitrot stuff. --- builtin/providers/azure/resource_azure_instance_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/azure/resource_azure_instance_test.go b/builtin/providers/azure/resource_azure_instance_test.go index 7e63486c3..1ed9fffb8 100644 --- a/builtin/providers/azure/resource_azure_instance_test.go +++ b/builtin/providers/azure/resource_azure_instance_test.go @@ -58,7 +58,7 @@ func TestAccAzureInstance_separateHostedService(t *testing.T) { "azure_instance.foo", testAccHostedServiceName, &dpmt), testAccCheckAzureInstanceBasicAttributes(&dpmt), resource.TestCheckResourceAttr( - "azure_instance.foo", "name", "terraform-test"), + "azure_instance.foo", "name", instanceName), resource.TestCheckResourceAttr( "azure_instance.foo", "hosted_service_name", "terraform-testing-service"), resource.TestCheckResourceAttr( @@ -392,8 +392,8 @@ resource "azure_hosted_service" "foo" { } resource "azure_instance" "foo" { - name = "terraform-test" - hosted_service_name = "${azure_hosted_service.foo.name}" + name = "%s" + hosted_service_name = "${azure_hosted_service.foo.name}" image = "Ubuntu Server 14.04 LTS" size = "Basic_A1" storage_service_name = "%s" @@ -407,7 +407,7 @@ resource "azure_instance" "foo" { public_port = 22 private_port = 22 } -}`, testAccHostedServiceName, testAccStorageServiceName) +}`, testAccHostedServiceName, instanceName, testAccStorageServiceName) var testAccAzureInstance_advanced = fmt.Sprintf(` resource "azure_virtual_network" "foo" { From 887839ce2369a5fc06d81f5aa50e8d2cfaa9e93f Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 19 Nov 2015 18:36:00 -0600 Subject: [PATCH 20/73] provider/digitalocean: remove relative CNAME test Heard back from DO support: > we require it to be a FQDN for a CNAME record in our DNS system. /cc @paystee, the original author here --- .../resource_digitalocean_record_test.go | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/builtin/providers/digitalocean/resource_digitalocean_record_test.go b/builtin/providers/digitalocean/resource_digitalocean_record_test.go index 94a061656..7a4123bd6 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_record_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_record_test.go @@ -104,43 +104,6 @@ func TestAccDigitalOceanRecord_HostnameValue(t *testing.T) { }) } -// This test fails with: -// -// POST https://api.digitalocean.com/v2/domains/foobar-test-terraform.com/records: -// 422 Data needs to end with a dot (.) -// -// Which seems like a behavior change on the DO API side. Opened support ticket -// #826791 to ask DigitalOcean about this, and we'll comment out the test for -// now. --phinze -/* -func TestAccDigitalOceanRecord_RelativeHostnameValue(t *testing.T) { - var record godo.DomainRecord - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckDigitalOceanRecordDestroy, - Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccCheckDigitalOceanRecordConfig_relative_cname, - Check: resource.ComposeTestCheckFunc( - testAccCheckDigitalOceanRecordExists("digitalocean_record.foobar", &record), - testAccCheckDigitalOceanRecordAttributesHostname("a.b", &record), - resource.TestCheckResourceAttr( - "digitalocean_record.foobar", "name", "terraform"), - resource.TestCheckResourceAttr( - "digitalocean_record.foobar", "domain", "foobar-test-terraform.com"), - resource.TestCheckResourceAttr( - "digitalocean_record.foobar", "value", "a.b"), - resource.TestCheckResourceAttr( - "digitalocean_record.foobar", "type", "CNAME"), - ), - }, - }, - }) -} -*/ - func TestAccDigitalOceanRecord_ExternalHostnameValue(t *testing.T) { var record godo.DomainRecord From cb84b98ce433d14196e9b9cba59034a3fdccd92f Mon Sep 17 00:00:00 2001 From: Takaaki Furukawa Date: Fri, 20 Nov 2015 21:01:02 +0900 Subject: [PATCH 21/73] provider/vsphere: Rename functions --- .../resource_vsphere_virtual_machine.go | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go index ac15cd97f..98a523488 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_machine.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_machine.go @@ -563,8 +563,8 @@ func addHardDisk(vm *object.VirtualMachine, size, iops int64, diskType string) e } } -// createNetworkDevice creates VirtualDeviceConfigSpec for Network Device. -func createNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) { +// buildNetworkDevice builds VirtualDeviceConfigSpec for Network Device. +func buildNetworkDevice(f *find.Finder, label, adapterType string) (*types.VirtualDeviceConfigSpec, error) { network, err := f.Network(context.TODO(), "*"+label) if err != nil { return nil, err @@ -608,8 +608,8 @@ func createNetworkDevice(f *find.Finder, label, adapterType string) (*types.Virt } } -// createVMRelocateSpec creates VirtualMachineRelocateSpec to set a place for a new VirtualMachine. -func createVMRelocateSpec(rp *object.ResourcePool, ds *object.Datastore, vm *object.VirtualMachine) (types.VirtualMachineRelocateSpec, error) { +// buildVMRelocateSpec builds VirtualMachineRelocateSpec to set a place for a new VirtualMachine. +func buildVMRelocateSpec(rp *object.ResourcePool, ds *object.Datastore, vm *object.VirtualMachine) (types.VirtualMachineRelocateSpec, error) { var key int devices, err := vm.Device(context.TODO()) @@ -655,8 +655,8 @@ func getDatastoreObject(client *govmomi.Client, f *object.DatacenterFolders, nam return ref.Reference(), nil } -// createStoragePlacementSpecCreate creates StoragePlacementSpec for create action. -func createStoragePlacementSpecCreate(f *object.DatacenterFolders, rp *object.ResourcePool, storagePod object.StoragePod, configSpec types.VirtualMachineConfigSpec) types.StoragePlacementSpec { +// buildStoragePlacementSpecCreate builds StoragePlacementSpec for create action. +func buildStoragePlacementSpecCreate(f *object.DatacenterFolders, rp *object.ResourcePool, storagePod object.StoragePod, configSpec types.VirtualMachineConfigSpec) types.StoragePlacementSpec { vmfr := f.VmFolder.Reference() rpr := rp.Reference() spr := storagePod.Reference() @@ -674,8 +674,8 @@ func createStoragePlacementSpecCreate(f *object.DatacenterFolders, rp *object.Re return sps } -// createStoragePlacementSpecClone creates StoragePlacementSpec for clone action. -func createStoragePlacementSpecClone(c *govmomi.Client, f *object.DatacenterFolders, vm *object.VirtualMachine, rp *object.ResourcePool, storagePod object.StoragePod) types.StoragePlacementSpec { +// buildStoragePlacementSpecClone builds StoragePlacementSpec for clone action. +func buildStoragePlacementSpecClone(c *govmomi.Client, f *object.DatacenterFolders, vm *object.VirtualMachine, rp *object.ResourcePool, storagePod object.StoragePod) types.StoragePlacementSpec { vmr := vm.Reference() vmfr := f.VmFolder.Reference() rpr := rp.Reference() @@ -784,7 +784,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error { networkDevices := []types.BaseVirtualDeviceConfigSpec{} for _, network := range vm.networkInterfaces { // network device - nd, err := createNetworkDevice(finder, network.label, "e1000") + nd, err := buildNetworkDevice(finder, network.label, "e1000") if err != nil { return err } @@ -821,7 +821,7 @@ func (vm *virtualMachine) createVirtualMachine(c *govmomi.Client) error { sp := object.StoragePod{ object.NewFolder(c.Client, d), } - sps := createStoragePlacementSpecCreate(dcFolders, resourcePool, sp, configSpec) + sps := buildStoragePlacementSpecCreate(dcFolders, resourcePool, sp, configSpec) datastore, err = findDatastore(c, sps) if err != nil { return err @@ -938,7 +938,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { sp := object.StoragePod{ object.NewFolder(c.Client, d), } - sps := createStoragePlacementSpecClone(c, dcFolders, template, resourcePool, sp) + sps := buildStoragePlacementSpecClone(c, dcFolders, template, resourcePool, sp) datastore, err = findDatastore(c, sps) if err != nil { return err @@ -950,7 +950,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { } log.Printf("[DEBUG] datastore: %#v", datastore) - relocateSpec, err := createVMRelocateSpec(resourcePool, datastore, template) + relocateSpec, err := buildVMRelocateSpec(resourcePool, datastore, template) if err != nil { return err } @@ -961,7 +961,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { networkConfigs := []types.CustomizationAdapterMapping{} for _, network := range vm.networkInterfaces { // network device - nd, err := createNetworkDevice(finder, network.label, "vmxnet3") + nd, err := buildNetworkDevice(finder, network.label, "vmxnet3") if err != nil { return err } @@ -1003,7 +1003,7 @@ func (vm *virtualMachine) deployVirtualMachine(c *govmomi.Client) error { } log.Printf("[DEBUG] virtual machine config spec: %v", configSpec) - // create CustomizationSpec + // build CustomizationSpec customSpec := types.CustomizationSpec{ Identity: &types.CustomizationLinuxPrep{ HostName: &types.CustomizationFixedName{ From 4d0699b9ddecab2e16eadda4ceb81b50e8d69193 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:19:49 -0600 Subject: [PATCH 22/73] mark snapshots as computed for ElastiCache clusters --- builtin/providers/aws/resource_aws_elasticache_cluster.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index 18a4ddf41..a0350c401 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -141,6 +141,7 @@ func resourceAwsElasticacheCluster() *schema.Resource { "snapshot_window": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "snapshot_retention_limit": &schema.Schema{ From fe204bb29137f29941c771d38e46fa1e4ff767f1 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:24:17 -0600 Subject: [PATCH 23/73] error test when env var is not supplied --- builtin/providers/aws/resource_aws_flow_log_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/builtin/providers/aws/resource_aws_flow_log_test.go b/builtin/providers/aws/resource_aws_flow_log_test.go index 9f9ca0570..530559389 100644 --- a/builtin/providers/aws/resource_aws_flow_log_test.go +++ b/builtin/providers/aws/resource_aws_flow_log_test.go @@ -34,6 +34,9 @@ func TestAccAWSFlowLog_basic(t *testing.T) { func TestAccAWSFlowLog_subnet(t *testing.T) { var flowLog ec2.FlowLog lgn := os.Getenv("LOG_GROUP_NAME") + if lgn == "" { + t.Fatalf("LOG_GROUP_NAME env var needed for TestAccAWSFlowLog_subnet") + } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 73475edceb835d9463f1958f9e23ea67046c2fe1 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Thu, 19 Nov 2015 16:27:56 -0600 Subject: [PATCH 24/73] update TestAccAWSFlowLog_subnet to use new cloudwatch resource, not needing env var anymore --- .../providers/aws/resource_aws_elasticache_cluster.go | 2 +- builtin/providers/aws/resource_aws_flow_log_test.go | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index a0350c401..d03ae89e8 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -141,7 +141,7 @@ func resourceAwsElasticacheCluster() *schema.Resource { "snapshot_window": &schema.Schema{ Type: schema.TypeString, Optional: true, - Computed: true, + Computed: true, }, "snapshot_retention_limit": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_flow_log_test.go b/builtin/providers/aws/resource_aws_flow_log_test.go index 530559389..02fb3cfd8 100644 --- a/builtin/providers/aws/resource_aws_flow_log_test.go +++ b/builtin/providers/aws/resource_aws_flow_log_test.go @@ -33,10 +33,6 @@ func TestAccAWSFlowLog_basic(t *testing.T) { func TestAccAWSFlowLog_subnet(t *testing.T) { var flowLog ec2.FlowLog - lgn := os.Getenv("LOG_GROUP_NAME") - if lgn == "" { - t.Fatalf("LOG_GROUP_NAME env var needed for TestAccAWSFlowLog_subnet") - } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,7 +40,7 @@ func TestAccAWSFlowLog_subnet(t *testing.T) { CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: fmt.Sprintf(testAccFlowLogConfig_subnet, lgn), + Config: testAccFlowLogConfig_subnet, Check: resource.ComposeTestCheckFunc( testAccCheckFlowLogExists("aws_flow_log.test_flow_log_subnet", &flowLog), testAccCheckAWSFlowLogAttributes(&flowLog), @@ -203,11 +199,14 @@ resource "aws_iam_role" "test_role" { } EOF } +resource "aws_cloudwatch_log_group" "foobar" { + name = "foo-bar" +} resource "aws_flow_log" "test_flow_log_subnet" { # log_group_name needs to exist before hand # until we have a CloudWatch Log Group Resource - log_group_name = "%s" + log_group_name = "${aws_cloudwatch_log_group.foobar.name}" iam_role_arn = "${aws_iam_role.test_role.arn}" subnet_id = "${aws_subnet.test_subnet.id}" traffic_type = "ALL" From e9a18a8f9f10665de52874a61e37e54eeaebaefa Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 20 Nov 2015 09:52:23 -0600 Subject: [PATCH 25/73] provider/google: fix sql database test Was missing a required parameter /cc @lwander @sparkprime --- builtin/providers/google/resource_sql_database_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/google/resource_sql_database_test.go b/builtin/providers/google/resource_sql_database_test.go index fa2e580e1..70d7e5f05 100644 --- a/builtin/providers/google/resource_sql_database_test.go +++ b/builtin/providers/google/resource_sql_database_test.go @@ -101,6 +101,7 @@ func testAccGoogleSqlDatabaseDestroy(s *terraform.State) error { var testGoogleSqlDatabase_basic = fmt.Sprintf(` resource "google_sql_database_instance" "instance" { name = "tf-lw-%d" + region = "us-central" settings { tier = "D0" } From e67551a641b3cc1a202d089185305dbf01d65009 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 20 Nov 2015 09:58:03 -0600 Subject: [PATCH 26/73] provider/docker: fix image test there's a new latest in town --- builtin/providers/docker/resource_docker_image_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/docker/resource_docker_image_test.go b/builtin/providers/docker/resource_docker_image_test.go index 0f0f0707a..b902749d7 100644 --- a/builtin/providers/docker/resource_docker_image_test.go +++ b/builtin/providers/docker/resource_docker_image_test.go @@ -17,7 +17,7 @@ func TestAccDockerImage_basic(t *testing.T) { resource.TestCheckResourceAttr( "docker_image.foo", "latest", - "b7cf8f0d9e82c9d96bd7afd22c600bfdb86b8d66c50d29164e5ad2fb02f7187b"), + "d52aff8195301dba95e8e3d14f0c3738a874237afd54233d250a2fc4489bfa83"), ), }, }, From 93ff7edb13250e931e76432df089eb83301140ac Mon Sep 17 00:00:00 2001 From: Lars Wander Date: Fri, 20 Nov 2015 08:49:55 -0500 Subject: [PATCH 27/73] provider/google: self-signed ssl certs for testing --- .../resource_compute_ssl_certificate_test.go | 4 +-- ...esource_compute_target_https_proxy_test.go | 16 +++++------ .../google/test-fixtures/ssl_cert/test.crt | 21 +++++++++++++++ .../google/test-fixtures/ssl_cert/test.csr | 17 ++++++++++++ .../google/test-fixtures/ssl_cert/test.key | 27 +++++++++++++++++++ 5 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 builtin/providers/google/test-fixtures/ssl_cert/test.crt create mode 100644 builtin/providers/google/test-fixtures/ssl_cert/test.csr create mode 100644 builtin/providers/google/test-fixtures/ssl_cert/test.key diff --git a/builtin/providers/google/resource_compute_ssl_certificate_test.go b/builtin/providers/google/resource_compute_ssl_certificate_test.go index 5d84527d9..a237bea16 100644 --- a/builtin/providers/google/resource_compute_ssl_certificate_test.go +++ b/builtin/providers/google/resource_compute_ssl_certificate_test.go @@ -74,7 +74,7 @@ const testAccComputeSslCertificate_basic = ` resource "google_compute_ssl_certificate" "foobar" { name = "terraform-test" description = "very descriptive" - private_key = "${file("~/cert/example.key")}" - certificate = "${file("~/cert/example.crt")}" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" } ` diff --git a/builtin/providers/google/resource_compute_target_https_proxy_test.go b/builtin/providers/google/resource_compute_target_https_proxy_test.go index 14ae8b30b..af3704d3e 100644 --- a/builtin/providers/google/resource_compute_target_https_proxy_test.go +++ b/builtin/providers/google/resource_compute_target_https_proxy_test.go @@ -142,15 +142,15 @@ resource "google_compute_url_map" "foobar" { resource "google_compute_ssl_certificate" "foobar1" { name = "terraform-test1" description = "very descriptive" - private_key = "${file("~/cert/example.key")}" - certificate = "${file("~/cert/example.crt")}" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" } resource "google_compute_ssl_certificate" "foobar2" { name = "terraform-test2" description = "very descriptive" - private_key = "${file("~/cert/example.key")}" - certificate = "${file("~/cert/example.crt")}" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" } ` @@ -199,14 +199,14 @@ resource "google_compute_url_map" "foobar" { resource "google_compute_ssl_certificate" "foobar1" { name = "terraform-test1" description = "very descriptive" - private_key = "${file("~/cert/example.key")}" - certificate = "${file("~/cert/example.crt")}" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" } resource "google_compute_ssl_certificate" "foobar2" { name = "terraform-test2" description = "very descriptive" - private_key = "${file("~/cert/example.key")}" - certificate = "${file("~/cert/example.crt")}" + private_key = "${file("test-fixtures/ssl_cert/test.key")}" + certificate = "${file("test-fixtures/ssl_cert/test.crt")}" } ` diff --git a/builtin/providers/google/test-fixtures/ssl_cert/test.crt b/builtin/providers/google/test-fixtures/ssl_cert/test.crt new file mode 100644 index 000000000..122d22d85 --- /dev/null +++ b/builtin/providers/google/test-fixtures/ssl_cert/test.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgjCCAmoCCQCPrrFCwXharzANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMC +VVMxETAPBgNVBAgMCE5ldy1Zb3JrMQwwCgYDVQQHDANOWUMxFTATBgNVBAoMDE9y +Z2FuaXphdGlvbjEQMA4GA1UECwwHU2VjdGlvbjEQMA4GA1UEAwwHTXkgTmFtZTEX +MBUGCSqGSIb3DQEJARYIbWVAbWUubWUwHhcNMTUxMTIwMTM0MTIwWhcNMTYxMTE5 +MTM0MTIwWjCBgjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldy1Zb3JrMQwwCgYD +VQQHDANOWUMxFTATBgNVBAoMDE9yZ2FuaXphdGlvbjEQMA4GA1UECwwHU2VjdGlv +bjEQMA4GA1UEAwwHTXkgTmFtZTEXMBUGCSqGSIb3DQEJARYIbWVAbWUubWUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbTuIV7EySLAijNAnsXG7HO/m4 +pu1Yy2sWWcqIifaSq0pL3JUGmWRKFRTb4msFIuKrkvsMLxWy6zIOnx0okRb7sTKb +XLBiN7zjSLCD6k31zlllO0GHkPu923VeGZ52xlIWxo22R2yoRuddD0YkQPctV7q9 +H7sKJq2141Ut9reMT2LKVRPlzf8wTcv+F+cAc3/i9Tib90GqclGrwk6XE59RBgzT +m9V7b/V+uusDtj6T3/ne5MHnq4g6lUz4mE7FneDVealjx7fHXtWSmR7dfbJilJj1 +foR/wPBeopdR5wAZS26bHjFIBMqAc7AgxbXdMorEDIY4i2OFjPTu22YYtmFZAgMB +AAEwDQYJKoZIhvcNAQELBQADggEBAHmgedgYDSIPiyaZnCWG56jFqYtHYS5xMOFS +T4FBEPsqgjbSYgjiugeQ37+nsbg/NQf4Z/Ca9CS20f7et8pjZWYqbqdGbifHSUAP +MsR3MK/8EsNVskioufvgExNrqHbcJD8aKrBHAyA6NbjaTnnBPrwdfcXxnWdpPNOh +yG6xSdi807t2e7dX59Nr6Fg6DHd9XPEM7VL/k5RBQyBf1ZgrO9cwA2jl8UtWKpaa +fO24S7Acwggi9TjJnyHOhWh21DEUEQG+czXAd5/LSjynTcI7xmuyfEgqJPIrskPv +OqM8II/iNr9Zglvp6hlmzIWnhgwLZiEljYGuMRNhr21jlHsCCYY= +-----END CERTIFICATE----- diff --git a/builtin/providers/google/test-fixtures/ssl_cert/test.csr b/builtin/providers/google/test-fixtures/ssl_cert/test.csr new file mode 100644 index 000000000..dee9945ed --- /dev/null +++ b/builtin/providers/google/test-fixtures/ssl_cert/test.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICyDCCAbACAQAwgYIxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXctWW9yazEM +MAoGA1UEBwwDTllDMRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEDAOBgNVBAsMB1Nl +Y3Rpb24xEDAOBgNVBAMMB015IE5hbWUxFzAVBgkqhkiG9w0BCQEWCG1lQG1lLm1l +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA207iFexMkiwIozQJ7Fxu +xzv5uKbtWMtrFlnKiIn2kqtKS9yVBplkShUU2+JrBSLiq5L7DC8VsusyDp8dKJEW ++7Eym1ywYje840iwg+pN9c5ZZTtBh5D7vdt1XhmedsZSFsaNtkdsqEbnXQ9GJED3 +LVe6vR+7CiatteNVLfa3jE9iylUT5c3/ME3L/hfnAHN/4vU4m/dBqnJRq8JOlxOf +UQYM05vVe2/1frrrA7Y+k9/53uTB56uIOpVM+JhOxZ3g1XmpY8e3x17Vkpke3X2y +YpSY9X6Ef8DwXqKXUecAGUtumx4xSATKgHOwIMW13TKKxAyGOItjhYz07ttmGLZh +WQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAGtNMtOtE7gUP5DbkZNxPsoGazkM +c3//gjH3MsTFzQ39r1uNq3fnbBBoYeQnsI05Bf7kSEVeT6fzdl5aBhOWxFF6uyTI +TZzcH9kvZ2IwFDbsa6vqrIJ6jIkpCIfPR8wN5LlBca9oZwJnt4ejF3RB5YBfnmeo +t5JXTbxGRvPBVRZCfJgcxcn731m1Rc8c9wud2IaNWiLob2J/92BJhSt/aiYps/TJ +ww5dRi6zhpxhR+RjlstG3C6oeYeQlSgzeBjhRcxtPHQWfcVfRLCtubqvuUQPcpw2 +YqMujh4vyKo+JEtqI8gqp4Bu0HVI1vr1vhblntFrQb0kueqV94HarE0uH+c= +-----END CERTIFICATE REQUEST----- diff --git a/builtin/providers/google/test-fixtures/ssl_cert/test.key b/builtin/providers/google/test-fixtures/ssl_cert/test.key new file mode 100644 index 000000000..92dd45137 --- /dev/null +++ b/builtin/providers/google/test-fixtures/ssl_cert/test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA207iFexMkiwIozQJ7Fxuxzv5uKbtWMtrFlnKiIn2kqtKS9yV +BplkShUU2+JrBSLiq5L7DC8VsusyDp8dKJEW+7Eym1ywYje840iwg+pN9c5ZZTtB +h5D7vdt1XhmedsZSFsaNtkdsqEbnXQ9GJED3LVe6vR+7CiatteNVLfa3jE9iylUT +5c3/ME3L/hfnAHN/4vU4m/dBqnJRq8JOlxOfUQYM05vVe2/1frrrA7Y+k9/53uTB +56uIOpVM+JhOxZ3g1XmpY8e3x17Vkpke3X2yYpSY9X6Ef8DwXqKXUecAGUtumx4x +SATKgHOwIMW13TKKxAyGOItjhYz07ttmGLZhWQIDAQABAoIBABEjzyOrfiiGbH5k +2MmyR64mj9PQqAgijdIHXn7hWXYJERtwt+z2HBJ2J1UwEvEp0tFaAWjoXSfInfbq +lJrRDBzLsorV6asjdA3HZpRIwaMOZ4oz4WE5AZPLDRc3pVzfDxdcmUK/vkxAjmCF +ixPWR/sxOhUB39phP35RsByRhbLfdGQkSspmD41imASqdqG96wsuc9Rk1Qjx9szr +kUxZkQGKUkRz4yQCwTR4+w2I21/cT5kxwM/KZG5f62tqB9urtFuTONrm7Z7xJv1T +BkHxQJxtsGhG8Dp8RB3t5PLou39xaBrjS5lpzJYtzrja25XGNEuONiQlWEDmk7li +acJWPQECgYEA98hjLlSO2sudUI36kJWc9CBqFznnUD2hIWRBM/Xc7mBhFGWxoxGm +f2xri91XbfH3oICIIBs52AdCyfjYbpF0clq8pSL+gHzRQTLcLUKVz3BxnxJAxyIG +QYPxmtMLVSzB5eZh+bPvcCyzd2ALDE1vFClQI/BcK/2dsJcXP2gSqdECgYEA4pTA +3okbdWOutnOwakyfVAbXjMx81D9ii2ZGHbuPY4PSD/tAe8onkEzHJgvinjddbi9p +oGwFhPqgfdWX7YNz5qsj9HP6Ehy7dw/EwvmX49yHsere85LiPMn/T9KkK0Pbn+HY ++0Q+ov/2wV3J7zPo8fffyQYizUKexGUN3XspGQkCgYEArFsMeobBE/q8g/MuzvHz +SnFduqhBebRU59hH7q/gLUSHYtvWM7ssWMh/Crw9e7HrcQ7XIZYup1FtqPZa/pZZ +LM5nGGt+IrwwBq0tMKJ3eOMbde4Jdzr4pQv1vJ9+65GFkritgDckn5/IeoopRTZ7 +xMd0AnvIcaUp0lNXDXkEOnECgYAk2C2YwlDdwOzrLFrWnkkWX9pzQdlWpkv/AQ2L +zjEd7JSfFqtAtfnDBEkqDaq3MaeWwEz70jT/j8XDUJVZARQ6wT+ig615foSZcs37 +Kp0hZ34FV30TvKHfYrWKpGUfx/QRxqcDDPDmjprwjLDGnflWR4lzZfUIzbmFlC0y +A9IGCQKBgH3ieP6nYCJexppvdxoycFkp3bSPr26MOCvACNsa+wJxBo59Zxs0YAmJ +9f6OOdUExueRY5iZCy0KPSgjYj96RuR0gV3cKc/WdOot4Ypgc/TK+r/UPDM2VAHk +yJuxkyXdOrstesxZIxpourS3kONtQUqMFmdqQeBngZl4v7yBtiRW +-----END RSA PRIVATE KEY----- From 24d15820c16061057d962b5a6976c42fd5b588e3 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 20 Nov 2015 10:51:06 -0600 Subject: [PATCH 28/73] provider/heroku: fix acc test depends_on requires a list of strings. the old parser would silently ignore this field, but the new one returned a syntax error. --- builtin/providers/heroku/resource_heroku_cert_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/heroku/resource_heroku_cert_test.go b/builtin/providers/heroku/resource_heroku_cert_test.go index 4182ab575..1a2c7879b 100644 --- a/builtin/providers/heroku/resource_heroku_cert_test.go +++ b/builtin/providers/heroku/resource_heroku_cert_test.go @@ -30,7 +30,7 @@ func TestAccHerokuCert_Basic(t *testing.T) { resource "heroku_cert" "ssl_certificate" { app = "${heroku_app.foobar.name}" - depends_on = "heroku_addon.ssl" + depends_on = ["heroku_addon.ssl"] certificate_chain="${file("` + certificateChainFile + `")}" private_key="${file("` + wd + `/test-fixtures/terraform.key")}" } From 9cf1c2943c0738af087b506801cbc11e338a422d Mon Sep 17 00:00:00 2001 From: stack72 Date: Wed, 4 Nov 2015 10:38:26 +0000 Subject: [PATCH 29/73] Adding the first pass of the work to get a floatingip assigned to a region --- builtin/providers/digitalocean/provider.go | 9 +- .../resource_digitalocean_floating_ip.go | 78 +++++++++++++++++ .../resource_digitalocean_floating_ip_test.go | 85 +++++++++++++++++++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 builtin/providers/digitalocean/resource_digitalocean_floating_ip.go create mode 100644 builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go diff --git a/builtin/providers/digitalocean/provider.go b/builtin/providers/digitalocean/provider.go index 080716e2e..be197a32f 100644 --- a/builtin/providers/digitalocean/provider.go +++ b/builtin/providers/digitalocean/provider.go @@ -18,10 +18,11 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "digitalocean_domain": resourceDigitalOceanDomain(), - "digitalocean_droplet": resourceDigitalOceanDroplet(), - "digitalocean_record": resourceDigitalOceanRecord(), - "digitalocean_ssh_key": resourceDigitalOceanSSHKey(), + "digitalocean_domain": resourceDigitalOceanDomain(), + "digitalocean_droplet": resourceDigitalOceanDroplet(), + "digitalocean_floating_ip": resourceDigitalOceanFloatingIp(), + "digitalocean_record": resourceDigitalOceanRecord(), + "digitalocean_ssh_key": resourceDigitalOceanSSHKey(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go new file mode 100644 index 000000000..562e7e2b2 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go @@ -0,0 +1,78 @@ +package digitalocean + +import ( + "fmt" + "log" + + "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceDigitalOceanFloatingIp() *schema.Resource { + return &schema.Resource{ + Create: resourceDigitalOceanFloatingIpCreate, + Read: resourceDigitalOceanFloatingIpRead, + Delete: resourceDigitalOceanFloatingIpDelete, + + Schema: map[string]*schema.Schema{ + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "ip_address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceDigitalOceanFloatingIpCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + // Build up our creation options + + opts := &godo.FloatingIPCreateRequest{ + Region: d.Get("region").(string), + } + + log.Printf("[DEBUG] FloatingIP Create: %#v", opts) + floatingIp, _, err := client.FloatingIPs.Create(opts) + if err != nil { + return fmt.Errorf("Error creating FloatingIP: %s", err) + } + + d.SetId(floatingIp.IP) + log.Printf("[INFO] Floating IP: %s", floatingIp.IP) + + return resourceDigitalOceanFloatingIpRead(d, meta) +} + +func resourceDigitalOceanFloatingIpRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + floatingIp, _, err := client.FloatingIPs.Get(d.Id()) + if err != nil { + return fmt.Errorf("Error retrieving FloatingIP: %s", err) + } + + d.Set("region", floatingIp.Region) + + return nil +} + +func resourceDigitalOceanFloatingIpDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*godo.Client) + + log.Printf("[INFO] Deleting FloatingIP: %s", d.Id()) + _, err := client.FloatingIPs.Delete(d.Id()) + if err != nil { + return fmt.Errorf("Error deleting FloatingIP: %s", err) + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go b/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go new file mode 100644 index 000000000..f489aa412 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go @@ -0,0 +1,85 @@ +package digitalocean + +import ( + "fmt" + "testing" + + "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccDigitalOceanFloatingIP_Basic(t *testing.T) { + var floatingIP godo.FloatingIP + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanFloatingIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDigitalOceanFloatingIPConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanFloatingIPExists("digitalocean_floating_ip.foobar", &floatingIP), + resource.TestCheckResourceAttr( + "digitalocean_floating_ip.foobar", "region", "nyc3"), + ), + }, + }, + }) +} + +func testAccCheckDigitalOceanFloatingIPDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*godo.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "digitalocean_floating_ip" { + continue + } + + // Try to find the key + _, _, err := client.FloatingIPs.Get(rs.Primary.ID) + + if err == nil { + fmt.Errorf("Floating IP still exists") + } + } + + return nil +} + +func testAccCheckDigitalOceanFloatingIPExists(n string, floatingIP *godo.FloatingIP) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Record ID is set") + } + + client := testAccProvider.Meta().(*godo.Client) + + // Try to find the FloatingIP + foundFloatingIP, _, err := client.FloatingIPs.Get(rs.Primary.ID) + + if err != nil { + return err + } + + if foundFloatingIP.IP != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + + *floatingIP = *foundFloatingIP + + return nil + } +} + +var testAccCheckDigitalOceanFloatingIPConfig_basic = ` +resource "digitalocean_floating_ip" "foobar" { + region = "nyc3" +}` From 7bda85559066fb27bbc2a7be77b97f61890fc2aa Mon Sep 17 00:00:00 2001 From: stack72 Date: Wed, 4 Nov 2015 11:37:33 +0000 Subject: [PATCH 30/73] Adding the work to assign a Floating IP to a Droplet --- .../resource_digitalocean_floating_ip.go | 29 +++++++++---- .../resource_digitalocean_floating_ip_test.go | 41 ++++++++++++++++-- .../providers/do/r/floating_ip.html.markdown | 43 +++++++++++++++++++ website/source/layouts/digitalocean.erb | 4 ++ 4 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 website/source/docs/providers/do/r/floating_ip.html.markdown diff --git a/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go index 562e7e2b2..9a6f680ae 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go +++ b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go @@ -15,17 +15,23 @@ func resourceDigitalOceanFloatingIp() *schema.Resource { Delete: resourceDigitalOceanFloatingIpDelete, Schema: map[string]*schema.Schema{ - "region": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "ip_address": &schema.Schema{ Type: schema.TypeString, Optional: true, Computed: true, }, + + "region": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "droplet_id": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, }, } } @@ -34,9 +40,15 @@ func resourceDigitalOceanFloatingIpCreate(d *schema.ResourceData, meta interface client := meta.(*godo.Client) // Build up our creation options + opts := &godo.FloatingIPCreateRequest{} - opts := &godo.FloatingIPCreateRequest{ - Region: d.Get("region").(string), + if v, ok := d.GetOk("droplet_id"); ok { + log.Printf("[INFO] Found a droplet_id to try and attach to the FloatingIP") + opts.DropletID = v.(int) + } else if d.Get("region").(string) != "" { + opts.Region = d.Get("region").(string) + } else { + return fmt.Errorf("You must specify either a Droplet ID or a Region for a FloatingIP") } log.Printf("[DEBUG] FloatingIP Create: %#v", opts) @@ -60,6 +72,7 @@ func resourceDigitalOceanFloatingIpRead(d *schema.ResourceData, meta interface{} } d.Set("region", floatingIp.Region) + d.Set("ip_address", floatingIp.IP) return nil } diff --git a/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go b/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go index f489aa412..2c4a438cf 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_floating_ip_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform/terraform" ) -func TestAccDigitalOceanFloatingIP_Basic(t *testing.T) { +func TestAccDigitalOceanFloatingIP_Region(t *testing.T) { var floatingIP godo.FloatingIP resource.Test(t, resource.TestCase{ @@ -18,7 +18,7 @@ func TestAccDigitalOceanFloatingIP_Basic(t *testing.T) { CheckDestroy: testAccCheckDigitalOceanFloatingIPDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccCheckDigitalOceanFloatingIPConfig_basic, + Config: testAccCheckDigitalOceanFloatingIPConfig_region, Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanFloatingIPExists("digitalocean_floating_ip.foobar", &floatingIP), resource.TestCheckResourceAttr( @@ -29,6 +29,26 @@ func TestAccDigitalOceanFloatingIP_Basic(t *testing.T) { }) } +func TestAccDigitalOceanFloatingIP_Droplet(t *testing.T) { + var floatingIP godo.FloatingIP + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanFloatingIPDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDigitalOceanFloatingIPConfig_droplet, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanFloatingIPExists("digitalocean_floating_ip.foobar", &floatingIP), + resource.TestCheckResourceAttr( + "digitalocean_floating_ip.foobar", "region", "sgp1"), + ), + }, + }, + }) +} + func testAccCheckDigitalOceanFloatingIPDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*godo.Client) @@ -79,7 +99,22 @@ func testAccCheckDigitalOceanFloatingIPExists(n string, floatingIP *godo.Floatin } } -var testAccCheckDigitalOceanFloatingIPConfig_basic = ` +var testAccCheckDigitalOceanFloatingIPConfig_region = ` resource "digitalocean_floating_ip" "foobar" { region = "nyc3" }` + +var testAccCheckDigitalOceanFloatingIPConfig_droplet = ` + +resource "digitalocean_droplet" "foobar" { + name = "baz" + size = "1gb" + image = "centos-5-8-x32" + region = "sgp1" + ipv6 = true + private_networking = true +} + +resource "digitalocean_floating_ip" "foobar" { + droplet_id = "${digitalocean_droplet.foobar.id}" +}` diff --git a/website/source/docs/providers/do/r/floating_ip.html.markdown b/website/source/docs/providers/do/r/floating_ip.html.markdown new file mode 100644 index 000000000..df29a3cff --- /dev/null +++ b/website/source/docs/providers/do/r/floating_ip.html.markdown @@ -0,0 +1,43 @@ +--- +layout: "digitalocean" +page_title: "DigitalOcean: digitalocean_floating_ip" +sidebar_current: "docs-do-resource-floating-ip" +description: |- + Provides a DigitalOcean Floating IP resource. +--- + +# digitalocean\_floating_ip + +Provides a DigitalOcean Floating IP to represent a publicly-accessible static IP addresses that can be mapped to one of your Droplets. + +## Example Usage + +``` +resource "digitalocean_droplet" "foobar" { + name = "baz" + size = "1gb" + image = "centos-5-8-x32" + region = "sgp1" + ipv6 = true + private_networking = true +} + +resource "digitalocean_floating_ip" "foobar" { + droplet_id = "${digitalocean_droplet.foobar.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional) The region that the Floating IP is reserved to. +* `droplet_id` - (Optional) The ID of Droplet that the Floating IP will be assigned to. + +~> **NOTE:** A Floating IP can be assigned to a region OR a droplet_id. If both region AND droplet_id are specified, then the Floating IP will be assigned to the droplet and use that region + +## Attributes Reference + +The following attributes are exported: + +* `ip_address` - The IP Address of the resource diff --git a/website/source/layouts/digitalocean.erb b/website/source/layouts/digitalocean.erb index 42ede7db3..f3f4306df 100644 --- a/website/source/layouts/digitalocean.erb +++ b/website/source/layouts/digitalocean.erb @@ -21,6 +21,10 @@ digitalocean_droplet + > + digitalocean_floating_ip + + > digitalocean_record From 74c93d3a460ed7bd2dc8cfb3eb10b33961215140 Mon Sep 17 00:00:00 2001 From: stack72 Date: Wed, 4 Nov 2015 15:32:26 +0000 Subject: [PATCH 31/73] Reording the code for the creation of a Floating IP for a droplet. The call to the DO api takes a few seconds to propagate so I had to sacriface some kittens and added a short 10 second sleep --- .../resource_digitalocean_floating_ip.go | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go index 9a6f680ae..abdb53ac1 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go +++ b/builtin/providers/digitalocean/resource_digitalocean_floating_ip.go @@ -3,8 +3,10 @@ package digitalocean import ( "fmt" "log" + "time" "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" ) @@ -24,6 +26,7 @@ func resourceDigitalOceanFloatingIp() *schema.Resource { "region": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, }, @@ -42,13 +45,14 @@ func resourceDigitalOceanFloatingIpCreate(d *schema.ResourceData, meta interface // Build up our creation options opts := &godo.FloatingIPCreateRequest{} + if v, ok := d.GetOk("region"); ok { + log.Printf("[INFO] Create a FloatingIP for a region") + opts.Region = v.(string) + } + if v, ok := d.GetOk("droplet_id"); ok { log.Printf("[INFO] Found a droplet_id to try and attach to the FloatingIP") opts.DropletID = v.(int) - } else if d.Get("region").(string) != "" { - opts.Region = d.Get("region").(string) - } else { - return fmt.Errorf("You must specify either a Droplet ID or a Region for a FloatingIP") } log.Printf("[DEBUG] FloatingIP Create: %#v", opts) @@ -56,9 +60,7 @@ func resourceDigitalOceanFloatingIpCreate(d *schema.ResourceData, meta interface if err != nil { return fmt.Errorf("Error creating FloatingIP: %s", err) } - d.SetId(floatingIp.IP) - log.Printf("[INFO] Floating IP: %s", floatingIp.IP) return resourceDigitalOceanFloatingIpRead(d, meta) } @@ -66,12 +68,20 @@ func resourceDigitalOceanFloatingIpCreate(d *schema.ResourceData, meta interface func resourceDigitalOceanFloatingIpRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*godo.Client) + time.Sleep(7 * time.Second) + log.Printf("[INFO] Reading the details of the FloatingIP %s", d.Id()) floatingIp, _, err := client.FloatingIPs.Get(d.Id()) if err != nil { return fmt.Errorf("Error retrieving FloatingIP: %s", err) } - d.Set("region", floatingIp.Region) + if _, ok := d.GetOk("droplet_id"); ok { + log.Printf("[INFO] The region of the Droplet is %s", floatingIp.Droplet.Region) + d.Set("region", floatingIp.Droplet.Region.Slug) + } else { + d.Set("region", floatingIp.Region.Slug) + } + d.Set("ip_address", floatingIp.IP) return nil @@ -80,6 +90,21 @@ func resourceDigitalOceanFloatingIpRead(d *schema.ResourceData, meta interface{} func resourceDigitalOceanFloatingIpDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*godo.Client) + if _, ok := d.GetOk("droplet_id"); ok { + log.Printf("[INFO] Unassigning the Floating IP from the Droplet") + action, _, err := client.FloatingIPActions.Unassign(d.Id()) + if err != nil { + return fmt.Errorf( + "Error Unassigning FloatingIP (%s) from the droplet: %s", d.Id(), err) + } + + _, unassignedErr := waitForFloatingIPReady(d, "completed", []string{"new"}, "status", meta, action.ID) + if unassignedErr != nil { + return fmt.Errorf( + "Error waiting for FloatingIP (%s) to be unassigned: %s", d.Id(), unassignedErr) + } + } + log.Printf("[INFO] Deleting FloatingIP: %s", d.Id()) _, err := client.FloatingIPs.Delete(d.Id()) if err != nil { @@ -89,3 +114,37 @@ func resourceDigitalOceanFloatingIpDelete(d *schema.ResourceData, meta interface d.SetId("") return nil } + +func waitForFloatingIPReady( + d *schema.ResourceData, target string, pending []string, attribute string, meta interface{}, action int) (interface{}, error) { + log.Printf( + "[INFO] Waiting for FloatingIP (%s) to have %s of %s", + d.Id(), attribute, target) + + stateConf := &resource.StateChangeConf{ + Pending: pending, + Target: target, + Refresh: newFloatingIPStateRefreshFunc(d, attribute, meta, action), + Timeout: 60 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + + NotFoundChecks: 60, + } + + return stateConf.WaitForState() +} + +func newFloatingIPStateRefreshFunc( + d *schema.ResourceData, attribute string, meta interface{}, action int) resource.StateRefreshFunc { + client := meta.(*godo.Client) + return func() (interface{}, string, error) { + floatingIP, _, err := client.FloatingIPActions.Get(d.Id(), action) + if err != nil { + return nil, "", fmt.Errorf("Error retrieving FloatingIP Action: %s", err) + } + + log.Printf("[INFO] The FloatingIP Assigned Status is %s", floatingIP.Status) + return &floatingIP, floatingIP.Status, nil + } +} From cf5b32617b89995a06865335c3b6ced03ecfb08b Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 20 Nov 2015 11:44:29 -0600 Subject: [PATCH 32/73] fix vpn gateway refresh/reattach issue --- .../providers/aws/resource_aws_vpn_gateway.go | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/builtin/providers/aws/resource_aws_vpn_gateway.go b/builtin/providers/aws/resource_aws_vpn_gateway.go index debd0ec91..4d7860dec 100644 --- a/builtin/providers/aws/resource_aws_vpn_gateway.go +++ b/builtin/providers/aws/resource_aws_vpn_gateway.go @@ -168,24 +168,34 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error d.Id(), d.Get("vpc_id").(string)) - _, err := conn.AttachVpnGateway(&ec2.AttachVpnGatewayInput{ + req := &ec2.AttachVpnGatewayInput{ VpnGatewayId: aws.String(d.Id()), VpcId: aws.String(d.Get("vpc_id").(string)), + } + + err := resource.Retry(30*time.Second, func() error { + _, err := conn.AttachVpnGateway(req) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok { + if "InvalidVpnGatewayID.NotFound" == ec2err.Code() { + //retry + return fmt.Errorf("Gateway not found, retry for eventual consistancy") + } + } + return resource.RetryError{Err: err} + } + return nil }) + if err != nil { return err } - // A note on the states below: the AWS docs (as of July, 2014) say - // that the states would be: attached, attaching, detached, detaching, - // but when running, I noticed that the state is usually "available" when - // it is attached. - // Wait for it to be fully attached before continuing log.Printf("[DEBUG] Waiting for VPN gateway (%s) to attach", d.Id()) stateConf := &resource.StateChangeConf{ Pending: []string{"detached", "attaching"}, - Target: "available", + Target: "attached", Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id(), "available"), Timeout: 1 * time.Minute, } @@ -271,6 +281,7 @@ func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ VpnGatewayIds: []*string{aws.String(id)}, }) + if err != nil { if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" { resp = nil @@ -288,10 +299,6 @@ func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) vpnGateway := resp.VpnGateways[0] - if time.Now().Sub(start) > 10*time.Second { - return vpnGateway, expected, nil - } - if len(vpnGateway.VpcAttachments) == 0 { // No attachments, we're detached return vpnGateway, "detached", nil From 9eb46c28b27c250730818960adaa794e5d49b10a Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 20 Nov 2015 13:15:20 -0600 Subject: [PATCH 33/73] use a log group resourcE --- builtin/providers/aws/resource_aws_flow_log_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/providers/aws/resource_aws_flow_log_test.go b/builtin/providers/aws/resource_aws_flow_log_test.go index 02fb3cfd8..061643e94 100644 --- a/builtin/providers/aws/resource_aws_flow_log_test.go +++ b/builtin/providers/aws/resource_aws_flow_log_test.go @@ -2,7 +2,6 @@ package aws import ( "fmt" - "os" "testing" "github.com/aws/aws-sdk-go/aws" @@ -13,7 +12,6 @@ import ( func TestAccAWSFlowLog_basic(t *testing.T) { var flowLog ec2.FlowLog - lgn := os.Getenv("LOG_GROUP_NAME") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,7 +19,7 @@ func TestAccAWSFlowLog_basic(t *testing.T) { CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: fmt.Sprintf(testAccFlowLogConfig_basic, lgn), + Config: testAccFlowLogConfig_basic, Check: resource.ComposeTestCheckFunc( testAccCheckFlowLogExists("aws_flow_log.test_flow_log", &flowLog), testAccCheckAWSFlowLogAttributes(&flowLog), @@ -142,6 +140,9 @@ resource "aws_iam_role" "test_role" { EOF } +resource "aws_cloudwatch_log_group" "foobar" { + name = "foo-bar" +} resource "aws_flow_log" "test_flow_log" { # log_group_name needs to exist before hand # until we have a CloudWatch Log Group Resource @@ -154,7 +155,7 @@ resource "aws_flow_log" "test_flow_log" { resource "aws_flow_log" "test_flow_log_subnet" { # log_group_name needs to exist before hand # until we have a CloudWatch Log Group Resource - log_group_name = "%s" + log_group_name = "${aws_cloudwatch_log_group.foobar.name}" iam_role_arn = "${aws_iam_role.test_role.arn}" subnet_id = "${aws_subnet.test_subnet.id}" traffic_type = "ALL" From 938281024f57dc6c0012d721a226c398b88d3ded Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 20 Nov 2015 13:41:34 -0600 Subject: [PATCH 34/73] helper/schema: name test cases w/ strings I promised myself that next time I jumped in this file I'd fix this up. Now we don't have to manually index the file with comments, we can just add descriptive names to the test cases! --- helper/schema/schema_test.go | 306 +++++++++++------------------------ 1 file changed, 92 insertions(+), 214 deletions(-) diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index e43300c99..024b74c6d 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -124,7 +124,7 @@ func TestValueType_Zero(t *testing.T) { } func TestSchemaMap_Diff(t *testing.T) { - cases := []struct { + cases := map[string]struct { Schema map[string]*Schema State *terraform.InstanceState Config map[string]interface{} @@ -132,12 +132,7 @@ func TestSchemaMap_Diff(t *testing.T) { Diff *terraform.InstanceDiff Err bool }{ - /* - * String decode - */ - - // #0 - { + "#0": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -166,8 +161,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #1 - { + "#1": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -194,8 +188,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #2 - { + "#2": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -216,8 +209,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #3 Computed, but set in config - { + "#3 Computed, but set in config": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -248,8 +240,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #4 Default - { + "#4 Default": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -274,8 +265,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #5 DefaultFunc, value - { + "#5 DefaultFunc, value": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -302,8 +292,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #6 DefaultFunc, configuration set - { + "#6 DefaultFunc, configuration set": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -332,8 +321,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #7 String with StateFunc - { + "#7 String with StateFunc": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -364,8 +352,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #8 Variable (just checking) - { + "#8 Variable (just checking)": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -395,8 +382,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #9 Variable computed - { + "#9 Variable computed": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -426,12 +412,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * Int decode - */ - - // #10 - { + "#10 Int decode": { Schema: map[string]*Schema{ "port": &Schema{ Type: TypeInt, @@ -460,12 +441,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * Bool decode - */ - - // #11 - { + "#11 bool decode": { Schema: map[string]*Schema{ "port": &Schema{ Type: TypeBool, @@ -494,12 +470,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * Bool - */ - - // #12 - { + "#12 Bool": { Schema: map[string]*Schema{ "delete": &Schema{ Type: TypeBool, @@ -521,12 +492,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * List decode - */ - - // #13 - { + "#13 List decode": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -565,8 +531,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #14 - { + "#14": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -609,8 +574,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #15 - { + "#15": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -643,8 +607,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #16 - { + "#16": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -671,8 +634,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #17 - { + "#17": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -709,8 +671,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #18 - { + "#18": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -754,8 +715,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #19 - { + "#19": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeList, @@ -781,12 +741,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * Set - */ - - // #20 - { + "#20 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -828,8 +783,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #21 - { + "#21 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -855,8 +809,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #22 - { + "#22 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -885,8 +838,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #23 - { + "#23 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -932,8 +884,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #24 - { + "#24 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -969,8 +920,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #25 - { + "#25 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -1018,8 +968,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #26 - { + "#26 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -1063,8 +1012,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #27 - { + "#27 Set": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -1092,8 +1040,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #28 - { + "#28 Set": { Schema: map[string]*Schema{ "ingress": &Schema{ Type: TypeSet, @@ -1145,12 +1092,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * List of structure decode - */ - - // #29 - { + "#29 List of structure decode": { Schema: map[string]*Schema{ "ingress": &Schema{ Type: TypeList, @@ -1192,12 +1134,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * ComputedWhen - */ - - // #30 - { + "#30 ComputedWhen": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -1227,8 +1164,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #31 - { + "#31": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -1306,12 +1242,7 @@ func TestSchemaMap_Diff(t *testing.T) { }, */ - /* - * Maps - */ - - // #32 - { + "#32 Maps": { Schema: map[string]*Schema{ "config_vars": &Schema{ Type: TypeMap, @@ -1345,8 +1276,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #33 - { + "#33 Maps": { Schema: map[string]*Schema{ "config_vars": &Schema{ Type: TypeMap, @@ -1383,8 +1313,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #34 - { + "#34 Maps": { Schema: map[string]*Schema{ "vars": &Schema{ Type: TypeMap, @@ -1424,8 +1353,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #35 - { + "#35 Maps": { Schema: map[string]*Schema{ "vars": &Schema{ Type: TypeMap, @@ -1446,8 +1374,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #36 - { + "#36 Maps": { Schema: map[string]*Schema{ "config_vars": &Schema{ Type: TypeList, @@ -1486,8 +1413,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #37 - { + "#37 Maps": { Schema: map[string]*Schema{ "config_vars": &Schema{ Type: TypeList, @@ -1529,12 +1455,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - /* - * ForceNews - */ - - // #38 - { + "#38 ForceNews": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -1579,8 +1500,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #39 Set - { + "#39 Set": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -1630,8 +1550,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #40 Set - { + "#40 Set": { Schema: map[string]*Schema{ "instances": &Schema{ Type: TypeSet, @@ -1669,8 +1588,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #41 Set - { + "#41 Set": { Schema: map[string]*Schema{ "route": &Schema{ Type: TypeSet, @@ -1730,8 +1648,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #42 Set - { + "#42 Set": { Schema: map[string]*Schema{ "route": &Schema{ Type: TypeSet, @@ -1796,8 +1713,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #43 - Computed maps - { + "#43 - Computed maps": { Schema: map[string]*Schema{ "vars": &Schema{ Type: TypeMap, @@ -1821,8 +1737,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #44 - Computed maps - { + "#44 - Computed maps": { Schema: map[string]*Schema{ "vars": &Schema{ Type: TypeMap, @@ -1858,8 +1773,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #45 - Empty - { + "#45 - Empty": { Schema: map[string]*Schema{}, State: &terraform.InstanceState{}, @@ -1871,8 +1785,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #46 - Float - { + "#46 - Float": { Schema: map[string]*Schema{ "some_threshold": &Schema{ Type: TypeFloat, @@ -1901,8 +1814,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #47 - https://github.com/hashicorp/terraform/issues/824 - { + "#47 - https://github.com/hashicorp/terraform/issues/824": { Schema: map[string]*Schema{ "block_device": &Schema{ Type: TypeSet, @@ -1955,8 +1867,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #48 - Zero value in state shouldn't result in diff - { + "#48 - Zero value in state shouldn't result in diff": { Schema: map[string]*Schema{ "port": &Schema{ Type: TypeBool, @@ -1978,8 +1889,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #49 Set - Same as #48 but for sets - { + "#49 Set - Same as #48 but for sets": { Schema: map[string]*Schema{ "route": &Schema{ Type: TypeSet, @@ -2021,8 +1931,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #50 - A set computed element shouldn't cause a diff - { + "#50 - A set computed element shouldn't cause a diff": { Schema: map[string]*Schema{ "active": &Schema{ Type: TypeBool, @@ -2044,8 +1953,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #51 - An empty set should show up in the diff - { + "#51 - An empty set should show up in the diff": { Schema: map[string]*Schema{ "instances": &Schema{ Type: TypeSet, @@ -2085,8 +1993,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #52 - Map with empty value - { + "#52 - Map with empty value": { Schema: map[string]*Schema{ "vars": &Schema{ Type: TypeMap, @@ -2117,8 +2024,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #53 - Unset bool, not in state - { + "#53 - Unset bool, not in state": { Schema: map[string]*Schema{ "force": &Schema{ Type: TypeBool, @@ -2136,8 +2042,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #54 - Unset set, not in state - { + "#54 - Unset set, not in state": { Schema: map[string]*Schema{ "metadata_keys": &Schema{ Type: TypeSet, @@ -2157,8 +2062,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #55 - Unset list in state, should not show up computed - { + "#55 - Unset list in state, should not show up computed": { Schema: map[string]*Schema{ "metadata_keys": &Schema{ Type: TypeList, @@ -2182,8 +2086,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #56 - Set element computed substring - { + "#56 - Set element computed substring": { Schema: map[string]*Schema{ "ports": &Schema{ Type: TypeSet, @@ -2218,9 +2121,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #57 - Computed map without config that's known to be empty does not - // generate diff - { + "#57 Computed map without config that's known to be empty does not generate diff": { Schema: map[string]*Schema{ "tags": &Schema{ Type: TypeMap, @@ -2241,8 +2142,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #58 Set with hyphen keys - { + "#58 Set with hyphen keys": { Schema: map[string]*Schema{ "route": &Schema{ Type: TypeSet, @@ -2298,8 +2198,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #59: StateFunc in nested set (#1759) - { + "#59: StateFunc in nested set (#1759)": { Schema: map[string]*Schema{ "service_account": &Schema{ Type: TypeList, @@ -2364,8 +2263,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - // #60 - Removing set elements - { + "#60 - Removing set elements": { Schema: map[string]*Schema{ "instances": &Schema{ Type: TypeSet, @@ -2418,10 +2316,10 @@ func TestSchemaMap_Diff(t *testing.T) { }, } - for i, tc := range cases { + for tn, tc := range cases { c, err := config.NewRawConfig(tc.Config) if err != nil { - t.Fatalf("#%d err: %s", i, err) + t.Fatalf("#%q err: %s", tn, err) } if len(tc.ConfigVariables) > 0 { @@ -2431,18 +2329,18 @@ func TestSchemaMap_Diff(t *testing.T) { } if err := c.Interpolate(vars); err != nil { - t.Fatalf("#%d err: %s", i, err) + t.Fatalf("#%q err: %s", tn, err) } } d, err := schemaMap(tc.Schema).Diff( tc.State, terraform.NewResourceConfig(c)) if err != nil != tc.Err { - t.Fatalf("#%d err: %s", i, err) + t.Fatalf("#%q err: %s", tn, err) } if !reflect.DeepEqual(tc.Diff, d) { - t.Fatalf("#%d:\n\nexpected: %#v\n\ngot:\n\n%#v", i, tc.Diff, d) + t.Fatalf("#%q:\n\nexpected: %#v\n\ngot:\n\n%#v", tn, tc.Diff, d) } } } @@ -2640,17 +2538,16 @@ func TestSchemaMap_InputDefault(t *testing.T) { } func TestSchemaMap_InternalValidate(t *testing.T) { - cases := []struct { + cases := map[string]struct { In map[string]*Schema Err bool }{ - { + "nothing": { nil, false, }, - // No optional and no required - { + "Both optional and required": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2661,8 +2558,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // No optional and no required - { + "No optional and no required": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2671,8 +2567,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Missing Type - { + "Missing Type": { map[string]*Schema{ "foo": &Schema{ Required: true, @@ -2681,8 +2576,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Required but computed - { + "Required but computed": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2693,8 +2587,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Looks good - { + "Looks good": { map[string]*Schema{ "foo": &Schema{ Type: TypeString, @@ -2704,8 +2597,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { false, }, - // Computed but has default - { + "Computed but has default": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2717,8 +2609,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Required but has default - { + "Required but has default": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2730,8 +2621,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // List element not set - { + "List element not set": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2740,8 +2630,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // List default - { + "List default": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2752,8 +2641,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // List element computed - { + "List element computed": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2767,8 +2655,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // List element with Set set - { + "List element with Set set": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2780,8 +2667,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Set element with no Set set - { + "Set element with no Set set": { map[string]*Schema{ "foo": &Schema{ Type: TypeSet, @@ -2792,8 +2678,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { false, }, - // Required but computed - { + "Required but computedWhen": { map[string]*Schema{ "foo": &Schema{ Type: TypeInt, @@ -2804,8 +2689,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Conflicting attributes cannot be required - { + "Conflicting attributes cannot be required": { map[string]*Schema{ "blacklist": &Schema{ Type: TypeBool, @@ -2820,8 +2704,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Attribute with conflicts cannot be required - { + "Attribute with conflicts cannot be required": { map[string]*Schema{ "whitelist": &Schema{ Type: TypeBool, @@ -2832,8 +2715,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // ConflictsWith cannot be used w/ Computed - { + "ConflictsWith cannot be used w/ Computed": { map[string]*Schema{ "blacklist": &Schema{ Type: TypeBool, @@ -2848,8 +2730,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // ConflictsWith cannot be used w/ ComputedWhen - { + "ConflictsWith cannot be used w/ ComputedWhen": { map[string]*Schema{ "blacklist": &Schema{ Type: TypeBool, @@ -2864,8 +2745,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Sub-resource invalid - { + "Sub-resource invalid": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2880,8 +2760,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { true, }, - // Sub-resource valid - { + "Sub-resource valid": { map[string]*Schema{ "foo": &Schema{ Type: TypeList, @@ -2899,8 +2778,7 @@ func TestSchemaMap_InternalValidate(t *testing.T) { false, }, - // ValidateFunc on non-primitive - { + "ValidateFunc on non-primitive": { map[string]*Schema{ "foo": &Schema{ Type: TypeSet, @@ -2914,13 +2792,13 @@ func TestSchemaMap_InternalValidate(t *testing.T) { }, } - for i, tc := range cases { + for tn, tc := range cases { err := schemaMap(tc.In).InternalValidate(schemaMap{}) if err != nil != tc.Err { if tc.Err { - t.Fatalf("%d: Expected error did not occur:\n\n%#v", i, tc.In) + t.Fatalf("%q: Expected error did not occur:\n\n%#v", tn, tc.In) } - t.Fatalf("%d: Unexpected error occurred:\n\n%#v", i, tc.In) + t.Fatalf("%q: Unexpected error occurred:\n\n%#v", tn, tc.In) } } From c7dc1c10a3fff425637bdde7da8ab2f7729e526d Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 20 Nov 2015 14:02:20 -0600 Subject: [PATCH 35/73] helper/schema: skip StateFunc when value is nil This takes the nil checking burden off of StateFunc. fixes #3586, see that issue for further discussion --- helper/schema/schema.go | 2 +- helper/schema/schema_test.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 8ed813526..8c85be2cf 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -919,7 +919,7 @@ func (m schemaMap) diffString( var originalN interface{} var os, ns string o, n, _, _ := d.diffChange(k) - if schema.StateFunc != nil { + if schema.StateFunc != nil && n != nil { originalN = n n = schema.StateFunc(n) } diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 024b74c6d..c70827a5f 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -321,7 +321,7 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, - "#7 String with StateFunc": { + "String with StateFunc": { Schema: map[string]*Schema{ "availability_zone": &Schema{ Type: TypeString, @@ -352,6 +352,36 @@ func TestSchemaMap_Diff(t *testing.T) { Err: false, }, + "StateFunc not called with nil value": { + Schema: map[string]*Schema{ + "availability_zone": &Schema{ + Type: TypeString, + Optional: true, + Computed: true, + StateFunc: func(a interface{}) string { + t.Fatalf("should not get here!") + return "" + }, + }, + }, + + State: nil, + + Config: map[string]interface{}{}, + + Diff: &terraform.InstanceDiff{ + Attributes: map[string]*terraform.ResourceAttrDiff{ + "availability_zone": &terraform.ResourceAttrDiff{ + Old: "", + New: "", + NewComputed: true, + }, + }, + }, + + Err: false, + }, + "#8 Variable (just checking)": { Schema: map[string]*Schema{ "availability_zone": &Schema{ From f26be080912ad0f6413757dd0cd355f06afc256f Mon Sep 17 00:00:00 2001 From: Clint Date: Fri, 20 Nov 2015 14:49:24 -0600 Subject: [PATCH 36/73] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 280237966..a8dfb3810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,8 @@ BUG FIXES: * provider/aws: Fix policy encoding issue with SNS Topics [GH-3700] * provider/aws: Correctly export ARN in `aws_iam_saml_provider` [GH-3827] * provider/aws: Fix crash in Route53 Record if Zone not found [GH-3945] - * providers/aws: Fix typo in error checking for IAM Policy Attachments #3970 + * providers/aws: Fix typo in error checking for IAM Policy Attachments [GH-3970] + * provider/aws: Fix issue with LB Cookie Stickiness and empty expiration period [GH-3908] * provider/aws: Tolerate ElastiCache clusters being deleted outside Terraform [GH-3767] * provider/aws: Downcase Route 53 record names in statefile to match API output [GH-3574] * provider/aws: Fix issue that could occur if no ECS Cluster was found for a give name [GH-3829] From cf87ede5ddc5014a93dabde66c26eec1b7094ba2 Mon Sep 17 00:00:00 2001 From: Clint Date: Fri, 20 Nov 2015 14:50:41 -0600 Subject: [PATCH 37/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8dfb3810..11ee3c333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ BUG FIXES: * provider/aws: Fix policy encoding issue with SNS Topics [GH-3700] * provider/aws: Correctly export ARN in `aws_iam_saml_provider` [GH-3827] * provider/aws: Fix crash in Route53 Record if Zone not found [GH-3945] + * providers/aws: Retry deleting IAM Server Cert on dependency violation [GH-3898] * providers/aws: Fix typo in error checking for IAM Policy Attachments [GH-3970] * provider/aws: Fix issue with LB Cookie Stickiness and empty expiration period [GH-3908] * provider/aws: Tolerate ElastiCache clusters being deleted outside Terraform [GH-3767] From 233aab6e0a556ac5db9d1b613e228175628abf76 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 20 Nov 2015 16:54:26 -0600 Subject: [PATCH 38/73] provider/aws: Fix issue deleting users who are attached to a group If you want to delete an IAM user, that user must not belong to any groups --- .../resource_aws_iam_group_membership_test.go | 28 ++++++++++++++ .../providers/aws/resource_aws_iam_user.go | 38 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/builtin/providers/aws/resource_aws_iam_group_membership_test.go b/builtin/providers/aws/resource_aws_iam_group_membership_test.go index fc868cb7c..26076dd9b 100644 --- a/builtin/providers/aws/resource_aws_iam_group_membership_test.go +++ b/builtin/providers/aws/resource_aws_iam_group_membership_test.go @@ -33,6 +33,14 @@ func TestAccAWSGroupMembership_basic(t *testing.T) { testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-two", "test-user-three"}), ), }, + + resource.TestStep{ + Config: testAccAWSGroupMemberConfigUpdateDown, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSGroupMembershipExists("aws_iam_group_membership.team", &group), + testAccCheckAWSGroupMembershipAttributes(&group, []string{"test-user-three"}), + ), + }, }, }) } @@ -167,3 +175,23 @@ resource "aws_iam_group_membership" "team" { group = "${aws_iam_group.group.name}" } ` + +const testAccAWSGroupMemberConfigUpdateDown = ` +resource "aws_iam_group" "group" { + name = "test-group" + path = "/" +} + +resource "aws_iam_user" "user_three" { + name = "test-user-three" + path = "/" +} + +resource "aws_iam_group_membership" "team" { + name = "tf-testing-group-membership" + users = [ + "${aws_iam_user.user_three.name}", + ] + group = "${aws_iam_group.group.name}" +} +` diff --git a/builtin/providers/aws/resource_aws_iam_user.go b/builtin/providers/aws/resource_aws_iam_user.go index d058047d0..99a12edf9 100644 --- a/builtin/providers/aws/resource_aws_iam_user.go +++ b/builtin/providers/aws/resource_aws_iam_user.go @@ -132,6 +132,44 @@ func resourceAwsIamUserUpdate(d *schema.ResourceData, meta interface{}) error { func resourceAwsIamUserDelete(d *schema.ResourceData, meta interface{}) error { iamconn := meta.(*AWSClient).iamconn + // IAM Users must be removed from all groups before they can be deleted + var groups []string + var marker *string + truncated := aws.Bool(true) + + for *truncated == true { + listOpts := iam.ListGroupsForUserInput{ + UserName: aws.String(d.Id()), + } + + if marker != nil { + listOpts.Marker = marker + } + + r, err := iamconn.ListGroupsForUser(&listOpts) + if err != nil { + return err + } + + for _, g := range r.Groups { + groups = append(groups, *g.GroupName) + } + + // if there's a marker present, we need to save it for pagination + if r.Marker != nil { + *marker = *r.Marker + } + *truncated = *r.IsTruncated + } + + for _, g := range groups { + // use iam group membership func to remove user from all groups + log.Printf("[DEBUG] Removing IAM User %s from IAM Group %s", d.Id(), g) + if err := removeUsersFromGroup(iamconn, []*string{aws.String(d.Id())}, g); err != nil { + return err + } + } + request := &iam.DeleteUserInput{ UserName: aws.String(d.Id()), } From 52db098292e3768208ac914f6a5240a8d5e74f9e Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Fri, 20 Nov 2015 22:51:40 -0700 Subject: [PATCH 39/73] Add enable_logging to AWS CloudTrail The AWS CloudTrail resource is capable of creating CloudTrail resources, but AWS defaults the actual logging of the trails to `false`, and Terraform has no method to enable or monitor the status of logging. CloudTrail trails that are inactive aren't very useful, and it's a surprise to discover they aren't logging on creation. Added an `enable_logging` parameter to resource_aws_cloudtrail to enable logging. This requires some extra API calls, which are wrapped in new internal functions. For compatibility with AWS, the default of `enable_logging` is set to `false`. --- .../providers/aws/resource_aws_cloudtrail.go | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/builtin/providers/aws/resource_aws_cloudtrail.go b/builtin/providers/aws/resource_aws_cloudtrail.go index eed420edf..82a9172c0 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail.go +++ b/builtin/providers/aws/resource_aws_cloudtrail.go @@ -22,6 +22,11 @@ func resourceAwsCloudTrail() *schema.Resource { Required: true, ForceNew: true, }, + "enable_logging": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "s3_bucket_name": &schema.Schema{ Type: schema.TypeString, Required: true, @@ -84,6 +89,11 @@ func resourceAwsCloudTrailCreate(d *schema.ResourceData, meta interface{}) error d.SetId(*t.Name) + // AWS CloudTrail sets newly-created trails to false. + if v, ok := d.GetOk("enable_logging"); ok && v.(bool) { + cloudTrailSetLogging(conn, v.(bool), d.Id()) + } + return resourceAwsCloudTrailRead(d, meta) } @@ -115,6 +125,12 @@ func resourceAwsCloudTrailRead(d *schema.ResourceData, meta interface{}) error { d.Set("include_global_service_events", trail.IncludeGlobalServiceEvents) d.Set("sns_topic_name", trail.SnsTopicName) + logstatus, err := cloudTrailGetLoggingStatus(conn, *trail.Name) + if err != nil { + return err + } + d.Set("enable_logging", logstatus) + return nil } @@ -149,6 +165,15 @@ func resourceAwsCloudTrailUpdate(d *schema.ResourceData, meta interface{}) error if err != nil { return err } + + if d.HasChange("enable_logging") { + log.Printf("[DEBUG] Updating logging on CloudTrail: %s", input) + err := cloudTrailSetLogging(conn, d.Get("enable_logging").(bool), *input.Name) + if err != nil { + return err + } + } + log.Printf("[DEBUG] CloudTrail updated: %s", t) return resourceAwsCloudTrailRead(d, meta) @@ -165,3 +190,42 @@ func resourceAwsCloudTrailDelete(d *schema.ResourceData, meta interface{}) error return err } + +func cloudTrailGetLoggingStatus(conn *cloudtrail.CloudTrail, id string) (bool, error) { + GetTrailStatusOpts := &cloudtrail.GetTrailStatusInput{ + Name: aws.String(id), + } + resp, err := conn.GetTrailStatus(GetTrailStatusOpts) + + return *resp.IsLogging, err +} + +func cloudTrailSetLogging(conn *cloudtrail.CloudTrail, enabled bool, id string) error { + if enabled { + log.Printf( + "[DEBUG] Starting logging on CloudTrail (%s)", + id) + StartLoggingOpts := &cloudtrail.StartLoggingInput{ + Name: aws.String(id), + } + if _, err := conn.StartLogging(StartLoggingOpts); err != nil { + return fmt.Errorf( + "Error starting logging on CloudTrail (%s): %s", + id, err) + } + } else { + log.Printf( + "[DEBUG] Stopping logging on CloudTrail (%s)", + id) + StopLoggingOpts := &cloudtrail.StopLoggingInput{ + Name: aws.String(id), + } + if _, err := conn.StopLogging(StopLoggingOpts); err != nil { + return fmt.Errorf( + "Error stopping logging on CloudTrail (%s): %s", + id, err) + } + } + + return nil +} From f98dbbb580b078214e4412333e3314182d38684b Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sat, 21 Nov 2015 00:15:29 -0700 Subject: [PATCH 40/73] Tests and docs for AWS CloudTrail "enable_logging" Add acceptance tests for creation, enable, and disable logging. Add option to docs and example. --- .../aws/resource_aws_cloudtrail_test.go | 58 +++++++++++++++++++ .../providers/aws/r/cloudtrail.html.markdown | 2 + 2 files changed, 60 insertions(+) diff --git a/builtin/providers/aws/resource_aws_cloudtrail_test.go b/builtin/providers/aws/resource_aws_cloudtrail_test.go index 10ed17a5b..7bf76219b 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail_test.go +++ b/builtin/providers/aws/resource_aws_cloudtrail_test.go @@ -39,6 +39,39 @@ func TestAccAWSCloudTrail_basic(t *testing.T) { }) } +func TestAccAWSCloudTrail_enable_logging(t *testing.T) { + var trail cloudtrail.Trail + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudTrailDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSCloudTrailConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), + ), + }, + resource.TestStep{ + Config: testAccAWSCloudTrailConfigModified, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", true, &trail), + ), + }, + resource.TestStep{ + Config: testAccAWSCloudTrailConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), + ), + }, + }, + }) +} + func testAccCheckCloudTrailExists(n string, trail *cloudtrail.Trail) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -63,6 +96,30 @@ func testAccCheckCloudTrailExists(n string, trail *cloudtrail.Trail) resource.Te } } +func testAccCheckCloudTrailLoggingEnabled(n string, desired bool, trail *cloudtrail.Trail) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := testAccProvider.Meta().(*AWSClient).cloudtrailconn + params := cloudtrail.GetTrailStatusInput{ + Name: aws.String(rs.Primary.ID), + } + resp, err := conn.GetTrailStatus(¶ms) + + if err != nil { + return err + } + if *resp.IsLogging != desired { + return fmt.Errorf("Logging status is incorrect") + } + + return nil + } +} + func testAccCheckAWSCloudTrailDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).cloudtrailconn @@ -134,6 +191,7 @@ resource "aws_cloudtrail" "foobar" { s3_bucket_name = "${aws_s3_bucket.foo.id}" s3_key_prefix = "/prefix" include_global_service_events = false + enable_logging = true } resource "aws_s3_bucket" "foo" { diff --git a/website/source/docs/providers/aws/r/cloudtrail.html.markdown b/website/source/docs/providers/aws/r/cloudtrail.html.markdown index d4ba604fc..0f21f4670 100644 --- a/website/source/docs/providers/aws/r/cloudtrail.html.markdown +++ b/website/source/docs/providers/aws/r/cloudtrail.html.markdown @@ -16,6 +16,7 @@ resource "aws_cloudtrail" "foobar" { name = "tf-trail-foobar" s3_bucket_name = "${aws_s3_bucket.foo.id}" s3_key_prefix = "/prefix" + enable_logging = true include_global_service_events = false } @@ -63,6 +64,7 @@ The following arguments are supported: endpoint to assume to write to a user’s log group. * `cloud_watch_logs_group_arn` - (Optional) Specifies a log group name using an Amazon Resource Name (ARN), that represents the log group to which CloudTrail logs will be delivered. +* `enable_logging` - (Optional) Enables logging for the trail. Defaults to `false`. * `include_global_service_events` - (Optional) Specifies whether the trail is publishing events from global services such as IAM to the log files. Defaults to `true`. * `sns_topic_name` - (Optional) Specifies the name of the Amazon SNS topic From c9eeb161e0d25732902cb59d06362addd54fb52d Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sat, 21 Nov 2015 14:55:08 -0700 Subject: [PATCH 41/73] Add a comment in tests The purpose of the first test of enable_logging wasn't quite clear. It's future-proofing against the assumptions made about AWS behavior. --- builtin/providers/aws/resource_aws_cloudtrail_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin/providers/aws/resource_aws_cloudtrail_test.go b/builtin/providers/aws/resource_aws_cloudtrail_test.go index 7bf76219b..47a89083c 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail_test.go +++ b/builtin/providers/aws/resource_aws_cloudtrail_test.go @@ -51,6 +51,8 @@ func TestAccAWSCloudTrail_enable_logging(t *testing.T) { Config: testAccAWSCloudTrailConfig, Check: resource.ComposeTestCheckFunc( testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + // This is a warning test. AWS sets up new trails with logging disabled + // Should that change in the future, this test should fail. testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), ), }, From 484887c0c5585b5db336da6f5484234bda1c853f Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sun, 22 Nov 2015 10:47:23 -0700 Subject: [PATCH 42/73] Change default for logging in CloudTrail to true The default for `enable_logging`, which defines whether CloudTrail actually logs events was originally written as defaulting to `false`, since that's how AWS creates trails. `true` is likely a better default for Terraform users. Changed the default and updated the docs. Changed the acceptance tests to verify new default behavior. --- builtin/providers/aws/resource_aws_cloudtrail.go | 2 +- .../providers/aws/resource_aws_cloudtrail_test.go | 12 ++++++------ .../docs/providers/aws/r/cloudtrail.html.markdown | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/builtin/providers/aws/resource_aws_cloudtrail.go b/builtin/providers/aws/resource_aws_cloudtrail.go index 82a9172c0..303073422 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail.go +++ b/builtin/providers/aws/resource_aws_cloudtrail.go @@ -25,7 +25,7 @@ func resourceAwsCloudTrail() *schema.Resource { "enable_logging": &schema.Schema{ Type: schema.TypeBool, Optional: true, - Default: false, + Default: true, }, "s3_bucket_name": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/aws/resource_aws_cloudtrail_test.go b/builtin/providers/aws/resource_aws_cloudtrail_test.go index 47a89083c..2d3e807c6 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail_test.go +++ b/builtin/providers/aws/resource_aws_cloudtrail_test.go @@ -51,23 +51,23 @@ func TestAccAWSCloudTrail_enable_logging(t *testing.T) { Config: testAccAWSCloudTrailConfig, Check: resource.ComposeTestCheckFunc( testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), - // This is a warning test. AWS sets up new trails with logging disabled - // Should that change in the future, this test should fail. - testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), + // AWS will create the trail with logging turned off. + // Test that "enable_logging" default works. + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", true, &trail), ), }, resource.TestStep{ Config: testAccAWSCloudTrailConfigModified, Check: resource.ComposeTestCheckFunc( testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), - testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", true, &trail), + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), ), }, resource.TestStep{ Config: testAccAWSCloudTrailConfig, Check: resource.ComposeTestCheckFunc( testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), - testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", false, &trail), + testAccCheckCloudTrailLoggingEnabled("aws_cloudtrail.foobar", true, &trail), ), }, }, @@ -193,7 +193,7 @@ resource "aws_cloudtrail" "foobar" { s3_bucket_name = "${aws_s3_bucket.foo.id}" s3_key_prefix = "/prefix" include_global_service_events = false - enable_logging = true + enable_logging = false } resource "aws_s3_bucket" "foo" { diff --git a/website/source/docs/providers/aws/r/cloudtrail.html.markdown b/website/source/docs/providers/aws/r/cloudtrail.html.markdown index 0f21f4670..e63a22dd2 100644 --- a/website/source/docs/providers/aws/r/cloudtrail.html.markdown +++ b/website/source/docs/providers/aws/r/cloudtrail.html.markdown @@ -64,7 +64,8 @@ The following arguments are supported: endpoint to assume to write to a user’s log group. * `cloud_watch_logs_group_arn` - (Optional) Specifies a log group name using an Amazon Resource Name (ARN), that represents the log group to which CloudTrail logs will be delivered. -* `enable_logging` - (Optional) Enables logging for the trail. Defaults to `false`. +* `enable_logging` - (Optional) Enables logging for the trail. Defaults to `true`. + Setting this to `false` will pause logging. * `include_global_service_events` - (Optional) Specifies whether the trail is publishing events from global services such as IAM to the log files. Defaults to `true`. * `sns_topic_name` - (Optional) Specifies the name of the Amazon SNS topic From 9cec40ea3cdac35f4dde5453cfa7dc674f437c8b Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sun, 22 Nov 2015 12:54:11 -0700 Subject: [PATCH 43/73] Add missing error-checks from code review Some error-checking was omitted. Specifically, the cloudTrailSetLogging call in the Create function was ignoring the return and cloudTrailGetLoggingStatus could crash on a nil-dereference during the return. Fixed both. Fixed some needless casting in cloudTrailGetLoggingStatus. Clarified error message in acceptance tests. Removed needless option from example in docs. --- builtin/providers/aws/resource_aws_cloudtrail.go | 14 ++++++++++---- .../providers/aws/resource_aws_cloudtrail_test.go | 2 +- .../docs/providers/aws/r/cloudtrail.html.markdown | 1 - 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/builtin/providers/aws/resource_aws_cloudtrail.go b/builtin/providers/aws/resource_aws_cloudtrail.go index 303073422..53def2fb1 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail.go +++ b/builtin/providers/aws/resource_aws_cloudtrail.go @@ -91,7 +91,10 @@ func resourceAwsCloudTrailCreate(d *schema.ResourceData, meta interface{}) error // AWS CloudTrail sets newly-created trails to false. if v, ok := d.GetOk("enable_logging"); ok && v.(bool) { - cloudTrailSetLogging(conn, v.(bool), d.Id()) + err := cloudTrailSetLogging(conn, v.(bool), d.Id()) + if err != nil { + return err + } } return resourceAwsCloudTrailRead(d, meta) @@ -125,7 +128,7 @@ func resourceAwsCloudTrailRead(d *schema.ResourceData, meta interface{}) error { d.Set("include_global_service_events", trail.IncludeGlobalServiceEvents) d.Set("sns_topic_name", trail.SnsTopicName) - logstatus, err := cloudTrailGetLoggingStatus(conn, *trail.Name) + logstatus, err := cloudTrailGetLoggingStatus(conn, trail.Name) if err != nil { return err } @@ -191,11 +194,14 @@ func resourceAwsCloudTrailDelete(d *schema.ResourceData, meta interface{}) error return err } -func cloudTrailGetLoggingStatus(conn *cloudtrail.CloudTrail, id string) (bool, error) { +func cloudTrailGetLoggingStatus(conn *cloudtrail.CloudTrail, id *string) (bool, error) { GetTrailStatusOpts := &cloudtrail.GetTrailStatusInput{ - Name: aws.String(id), + Name: id, } resp, err := conn.GetTrailStatus(GetTrailStatusOpts) + if err != nil { + return false, fmt.Errorf("Error retrieving logging status of CloudTrail (%s): %s", id, err) + } return *resp.IsLogging, err } diff --git a/builtin/providers/aws/resource_aws_cloudtrail_test.go b/builtin/providers/aws/resource_aws_cloudtrail_test.go index 2d3e807c6..c276135ce 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail_test.go +++ b/builtin/providers/aws/resource_aws_cloudtrail_test.go @@ -115,7 +115,7 @@ func testAccCheckCloudTrailLoggingEnabled(n string, desired bool, trail *cloudtr return err } if *resp.IsLogging != desired { - return fmt.Errorf("Logging status is incorrect") + return fmt.Errorf("Expected logging status %t, given %t", desired, *resp.IsLogging) } return nil diff --git a/website/source/docs/providers/aws/r/cloudtrail.html.markdown b/website/source/docs/providers/aws/r/cloudtrail.html.markdown index e63a22dd2..6bffee09e 100644 --- a/website/source/docs/providers/aws/r/cloudtrail.html.markdown +++ b/website/source/docs/providers/aws/r/cloudtrail.html.markdown @@ -16,7 +16,6 @@ resource "aws_cloudtrail" "foobar" { name = "tf-trail-foobar" s3_bucket_name = "${aws_s3_bucket.foo.id}" s3_key_prefix = "/prefix" - enable_logging = true include_global_service_events = false } From 52aad0493086ca393a73beb2e77012856b391101 Mon Sep 17 00:00:00 2001 From: Paul Forman Date: Sun, 22 Nov 2015 13:23:08 -0700 Subject: [PATCH 44/73] Mistake in type refactor in cloudTrailGetLoggingStatus When adjusting the types to prevent casting, I didn't change the error message to handle the pointer change. "go tool vet" caught this. --- builtin/providers/aws/resource_aws_cloudtrail.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_cloudtrail.go b/builtin/providers/aws/resource_aws_cloudtrail.go index 53def2fb1..5041a3741 100644 --- a/builtin/providers/aws/resource_aws_cloudtrail.go +++ b/builtin/providers/aws/resource_aws_cloudtrail.go @@ -200,7 +200,7 @@ func cloudTrailGetLoggingStatus(conn *cloudtrail.CloudTrail, id *string) (bool, } resp, err := conn.GetTrailStatus(GetTrailStatusOpts) if err != nil { - return false, fmt.Errorf("Error retrieving logging status of CloudTrail (%s): %s", id, err) + return false, fmt.Errorf("Error retrieving logging status of CloudTrail (%s): %s", *id, err) } return *resp.IsLogging, err From ddc98d7733e4752fe45f3a91967d8e8309c51932 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 23 Nov 2015 08:00:49 +0000 Subject: [PATCH 45/73] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ee3c333..8c3d26e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ FEATURES: * **New provider: `tls`** - A utility provider for generating TLS keys/self-signed certificates for development and testing [GH-2778] * **New provider: `dyn`** - Manage DNS records on Dyn * **New resource: `aws_cloudformation_stack`** [GH-2636] - * **New resource: `aws_cloudtrail`** [GH-3094] + * **New resource: `aws_cloudtrail`** [GH-3094], [GH-4010] * **New resource: `aws_route`** [GH-3548] * **New resource: `aws_codecommit_repository`** [GH-3274] * **New resource: `aws_kinesis_firehose_delivery_stream`** [GH-3833] From 890e214c00ce70ea545457785119e515e010d30b Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 10:32:46 +0200 Subject: [PATCH 46/73] Add failing test replicating #4013 --- command/refresh_test.go | 29 +++++++++++++++++++ .../test-fixtures/refresh-unset-var/main.tf | 7 +++++ 2 files changed, 36 insertions(+) create mode 100644 command/test-fixtures/refresh-unset-var/main.tf diff --git a/command/refresh_test.go b/command/refresh_test.go index 7f574e725..df0142d28 100644 --- a/command/refresh_test.go +++ b/command/refresh_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "bytes" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" ) @@ -413,6 +414,34 @@ func TestRefresh_varFileDefault(t *testing.T) { } } +func TestRefresh_varsUnset(t *testing.T) { + // Disable test mode so input would be asked + test = false + defer func() { test = true }() + + defaultInputReader = bytes.NewBufferString("bar\n") + + state := testState() + statePath := testStateFile(t, state) + + p := testProvider() + ui := new(cli.MockUi) + c := &RefreshCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{ + "-state", statePath, + testFixturePath("refresh-unset-var"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + func TestRefresh_backup(t *testing.T) { state := testState() statePath := testStateFile(t, state) diff --git a/command/test-fixtures/refresh-unset-var/main.tf b/command/test-fixtures/refresh-unset-var/main.tf new file mode 100644 index 000000000..446cf70e2 --- /dev/null +++ b/command/test-fixtures/refresh-unset-var/main.tf @@ -0,0 +1,7 @@ +variable "should_ask" {} + +provider "test" {} + +resource "test_instance" "foo" { + ami = "${var.should_ask}" +} From 50b5e7c1a58283728e789b4b858b0cd8ac550066 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 10:33:40 +0200 Subject: [PATCH 47/73] Validate context after input of vars on refresh Fixes #4013. --- command/refresh.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/command/refresh.go b/command/refresh.go index 99190bf87..c64505b2e 100644 --- a/command/refresh.go +++ b/command/refresh.go @@ -87,14 +87,16 @@ func (c *RefreshCommand) Run(args []string) int { c.Ui.Error(err.Error()) return 1 } - if !validateContext(ctx, c.Ui) { - return 1 - } + if err := ctx.Input(c.InputMode()); err != nil { c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) return 1 } + if !validateContext(ctx, c.Ui) { + return 1 + } + newState, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) From 0375c5a4ab07bba196016b6c81ed24aefb704000 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 10:40:38 +0200 Subject: [PATCH 48/73] Ignore .test files generated by some editors Seems like Emacs is generating .test files when saving _test.go files - we don't need these in the repo. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5a230d5ca..b559977b6 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ website/node_modules *~ .*.swp .idea +*.test From 150e997a96641b80b7a6893045e513a6b759e9e2 Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 23 Nov 2015 12:54:56 +0000 Subject: [PATCH 49/73] Changing the AWS ElastiCache cluster maintenance_window to enforce lowercase --- builtin/providers/aws/resource_aws_elasticache_cluster.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin/providers/aws/resource_aws_elasticache_cluster.go b/builtin/providers/aws/resource_aws_elasticache_cluster.go index d03ae89e8..cffcdab2d 100644 --- a/builtin/providers/aws/resource_aws_elasticache_cluster.go +++ b/builtin/providers/aws/resource_aws_elasticache_cluster.go @@ -71,6 +71,11 @@ func resourceAwsElasticacheCluster() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, + StateFunc: func(val interface{}) string { + // Elasticache always changes the maintenance + // to lowercase + return strings.ToLower(val.(string)) + }, }, "subnet_group_name": &schema.Schema{ Type: schema.TypeString, From 1f5de8ee43618bc9e1703fd3f0346266eb6a96d6 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 15:10:38 +0200 Subject: [PATCH 50/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3d26e41..2e15388c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ BUG FIXES: * provider/aws: Fix issue with order of Termincation Policies in AutoScaling Groups. This will introduce plans on upgrade to this version, in order to correct the ordering [GH-2890] * provider/aws: Allow cluster name, not only ARN for `aws_ecs_service` [GH-3668] + * provider/aws: Fix a bug where a non-lower-cased `maintenance_window` can cause unnecessary planned changes [GH-4020] * provider/aws: Only set `weight` on an `aws_route53_record` if it has been set in configuration [GH-3900] * provider/aws: ignore association not exist on route table destroy [GH-3615] * provider/aws: Fix policy encoding issue with SNS Topics [GH-3700] From fd2c62b3c7e96b5dd752ccda8734204a308177a2 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 23 Nov 2015 08:19:23 -0600 Subject: [PATCH 51/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e15388c5..eecc977a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ BUG FIXES: * core: modules on Git branches now update properly [GH-1568] * core: Fix issue preventing input prompts for unset variables during plan [GH-3843] * core: Orphan resources can now be targets [GH-3912] + * helper/schema: skip StateFunc when value is nil [GH-4002] * provider/google: Timeout when deleting large instance_group_manager [GH-3591] * provider/aws: Fix issue with order of Termincation Policies in AutoScaling Groups. This will introduce plans on upgrade to this version, in order to correct the ordering [GH-2890] From 38a5502efbf9b895e93c4db6f2ff381d82260ba4 Mon Sep 17 00:00:00 2001 From: Clint Date: Mon, 23 Nov 2015 08:22:33 -0600 Subject: [PATCH 52/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eecc977a4..c4fe31c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ BUG FIXES: * provider/aws: Correctly export ARN in `aws_iam_saml_provider` [GH-3827] * provider/aws: Fix crash in Route53 Record if Zone not found [GH-3945] * providers/aws: Retry deleting IAM Server Cert on dependency violation [GH-3898] + * providers/aws: Update Spot Instance request to provide connection information [GH-3940] * providers/aws: Fix typo in error checking for IAM Policy Attachments [GH-3970] * provider/aws: Fix issue with LB Cookie Stickiness and empty expiration period [GH-3908] * provider/aws: Tolerate ElastiCache clusters being deleted outside Terraform [GH-3767] From c74145f7115067b0240a9f3b91fe9282dd6a3175 Mon Sep 17 00:00:00 2001 From: Clint Date: Mon, 23 Nov 2015 08:23:10 -0600 Subject: [PATCH 53/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4fe31c40..ba8497327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ BUG FIXES: * provider/aws: ignore association not exist on route table destroy [GH-3615] * provider/aws: Fix policy encoding issue with SNS Topics [GH-3700] * provider/aws: Correctly export ARN in `aws_iam_saml_provider` [GH-3827] + * provider/aws: Fix issue deleting users who are attached to a group [GH-4005] * provider/aws: Fix crash in Route53 Record if Zone not found [GH-3945] * providers/aws: Retry deleting IAM Server Cert on dependency violation [GH-3898] * providers/aws: Update Spot Instance request to provide connection information [GH-3940] From 6543b944b7ab74b0bece724eed10d3e082b6a834 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 16:33:31 +0200 Subject: [PATCH 54/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8497327..31c73eeb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ BUG FIXES: * `terraform remote config`: update `--help` output [GH-3632] * core: modules on Git branches now update properly [GH-1568] * core: Fix issue preventing input prompts for unset variables during plan [GH-3843] + * core: Fix issue preventing input prompts for unset variables during refresh [GH-4017] * core: Orphan resources can now be targets [GH-3912] * helper/schema: skip StateFunc when value is nil [GH-4002] * provider/google: Timeout when deleting large instance_group_manager [GH-3591] From 99e3d62cd8d9e8ab8bf47b74c2f63a1c20725235 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 23 Nov 2015 16:39:12 +0200 Subject: [PATCH 55/73] provider/aws: Make VPC ID required on subnets --- builtin/providers/aws/resource_aws_subnet.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_subnet.go b/builtin/providers/aws/resource_aws_subnet.go index 95a9e3208..880a16098 100644 --- a/builtin/providers/aws/resource_aws_subnet.go +++ b/builtin/providers/aws/resource_aws_subnet.go @@ -22,9 +22,8 @@ func resourceAwsSubnet() *schema.Resource { Schema: map[string]*schema.Schema{ "vpc_id": &schema.Schema{ Type: schema.TypeString, - Optional: true, + Required: true, ForceNew: true, - Computed: true, }, "cidr_block": &schema.Schema{ From c524682f2df1ee2908d93c99ed5115a94563a2d3 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Mon, 23 Nov 2015 09:02:45 -0600 Subject: [PATCH 56/73] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c73eeb6..fe5de605b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ BUG FIXES: * provider/aws: Fix issue with updating the `aws_ecs_task_definition` where `aws_ecs_service` didn't wait for a new computed ARN [GH-3924] * provider/aws: Prevent crashing when deleting `aws_ecs_service` that is already gone [GH-3914] * provider/aws: Allow spaces in `aws_db_subnet_group.name` (undocumented in the API) [GH-3955] + * provider/aws: Make VPC ID required on subnets [GH-4021] * provider/azure: various bugfixes [GH-3695] * provider/digitalocean: fix issue preventing SSH fingerprints from working [GH-3633] * provider/digitalocean: Fixing the DigitalOcean Droplet 404 potential on refresh of state [GH-3768] From 7e0cbb4da7bc031b66cb3fe02d906acc7a0c2241 Mon Sep 17 00:00:00 2001 From: clint shryock Date: Mon, 23 Nov 2015 11:11:55 -0600 Subject: [PATCH 57/73] Update Vagrant file to use Go 1.5.1 --- Vagrantfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 59709339d..9ee9a8676 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -5,6 +5,7 @@ VAGRANTFILE_API_VERSION = "2" $script = <