From dfb34c85b923b27e4b6ee9dad5a8778971e37ee3 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 14:02:20 -0600 Subject: [PATCH 001/101] Add randomness to aws ses tests --- .../aws/resource_aws_ses_configuration_set_test.go | 12 +++++++----- .../aws/resource_aws_ses_event_destination_test.go | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ses_configuration_set_test.go b/builtin/providers/aws/resource_aws_ses_configuration_set_test.go index 7cbbe4232e..5a5bd1ec8a 100644 --- a/builtin/providers/aws/resource_aws_ses_configuration_set_test.go +++ b/builtin/providers/aws/resource_aws_ses_configuration_set_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/ses" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -42,7 +43,7 @@ func testAccCheckSESConfigurationSetDestroy(s *terraform.State) error { found := false for _, element := range response.ConfigurationSets { - if *element.Name == "some-configuration-set" { + if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) { found = true } } @@ -77,7 +78,7 @@ func testAccCheckAwsSESConfigurationSetExists(n string) resource.TestCheckFunc { found := false for _, element := range response.ConfigurationSets { - if *element.Name == "some-configuration-set" { + if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) { found = true } } @@ -90,8 +91,9 @@ func testAccCheckAwsSESConfigurationSetExists(n string) resource.TestCheckFunc { } } -const testAccAWSSESConfigurationSetConfig = ` +var escRandomInteger = acctest.RandInt() +var testAccAWSSESConfigurationSetConfig = fmt.Sprintf(` resource "aws_ses_configuration_set" "test" { - name = "some-configuration-set" + name = "some-configuration-set-%d" } -` +`, escRandomInteger) diff --git a/builtin/providers/aws/resource_aws_ses_event_destination_test.go b/builtin/providers/aws/resource_aws_ses_event_destination_test.go index 378e2042c8..624ce0c832 100644 --- a/builtin/providers/aws/resource_aws_ses_event_destination_test.go +++ b/builtin/providers/aws/resource_aws_ses_event_destination_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/ses" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -46,7 +47,7 @@ func testAccCheckSESEventDestinationDestroy(s *terraform.State) error { found := false for _, element := range response.ConfigurationSets { - if *element.Name == "some-configuration-set" { + if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) { found = true } } @@ -81,7 +82,7 @@ func testAccCheckAwsSESEventDestinationExists(n string) resource.TestCheckFunc { found := false for _, element := range response.ConfigurationSets { - if *element.Name == "some-configuration-set" { + if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) { found = true } } @@ -94,7 +95,8 @@ func testAccCheckAwsSESEventDestinationExists(n string) resource.TestCheckFunc { } } -const testAccAWSSESEventDestinationConfig = ` +var edRandomInteger = acctest.RandInt() +var testAccAWSSESEventDestinationConfig = fmt.Sprintf(` resource "aws_s3_bucket" "bucket" { bucket = "tf-test-bucket-format" acl = "private" @@ -155,7 +157,7 @@ data "aws_iam_policy_document" "fh_felivery_document" { } resource "aws_ses_configuration_set" "test" { - name = "some-configuration-set" + name = "some-configuration-set-%d" } resource "aws_ses_event_destination" "kinesis" { @@ -182,4 +184,4 @@ resource "aws_ses_event_destination" "cloudwatch" { value_source = "emailHeader" } } -` +`, edRandomInteger) From 63cd65d138bd8371c01b30c51110cdc51bea33ac Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 14:26:30 -0600 Subject: [PATCH 002/101] Add randomness to ses receipt rule --- .../aws/resource_aws_ses_receipt_rule_test.go | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go b/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go index f5770fcc4d..d39c4c03f9 100644 --- a/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go +++ b/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ses" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -111,7 +112,7 @@ func testAccCheckAwsSESReceiptRuleExists(n string) resource.TestCheckFunc { params := &ses.DescribeReceiptRuleInput{ RuleName: aws.String("basic"), - RuleSetName: aws.String("test-me"), + RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)), } response, err := conn.DescribeReceiptRule(params) @@ -153,7 +154,7 @@ func testAccCheckAwsSESReceiptRuleOrder(n string) resource.TestCheckFunc { conn := testAccProvider.Meta().(*AWSClient).sesConn params := &ses.DescribeReceiptRuleSetInput{ - RuleSetName: aws.String("test-me"), + RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)), } response, err := conn.DescribeReceiptRuleSet(params) @@ -186,7 +187,7 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc { params := &ses.DescribeReceiptRuleInput{ RuleName: aws.String("actions"), - RuleSetName: aws.String("test-me"), + RuleSetName: aws.String(fmt.Sprintf("test-me")), } response, err := conn.DescribeReceiptRule(params) @@ -227,9 +228,10 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc { } } -const testAccAWSSESReceiptRuleBasicConfig = ` +var srrsRandomInt = acctest.RandInt() +var testAccAWSSESReceiptRuleBasicConfig = fmt.Sprintf(` resource "aws_ses_receipt_rule_set" "test" { - rule_set_name = "test-me" + rule_set_name = "test-me-%d" } resource "aws_ses_receipt_rule" "basic" { @@ -240,11 +242,11 @@ resource "aws_ses_receipt_rule" "basic" { scan_enabled = true tls_policy = "Require" } -` +`, srrsRandomInt) -const testAccAWSSESReceiptRuleOrderConfig = ` +var testAccAWSSESReceiptRuleOrderConfig = fmt.Sprintf(` resource "aws_ses_receipt_rule_set" "test" { - rule_set_name = "test-me" + rule_set_name = "test-me-%d" } resource "aws_ses_receipt_rule" "second" { @@ -257,9 +259,9 @@ resource "aws_ses_receipt_rule" "first" { name = "first" rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}" } -` +`, srrsRandomInt) -const testAccAWSSESReceiptRuleActionsConfig = ` +var testAccAWSSESReceiptRuleActionsConfig = fmt.Sprintf(` resource "aws_s3_bucket" "emails" { bucket = "ses-terraform-emails" } @@ -289,4 +291,4 @@ resource "aws_ses_receipt_rule" "actions" { position = 2 } } -` +`) From 1c40518e8035f2e73f1eae041e3ae1a1c058f014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serta=C3=A7=20=C3=96zercan?= Date: Mon, 27 Mar 2017 14:00:29 -0700 Subject: [PATCH 003/101] provider/azurerm: Update vault_certificates instructions for clarity --- .../azurerm/r/virtual_machine.html.markdown | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown b/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown index e6cc76524c..9bfbff12ea 100644 --- a/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown +++ b/website/source/docs/providers/azurerm/r/virtual_machine.html.markdown @@ -304,11 +304,20 @@ For more information on the different example configurations, please check out t `os_profile_secrets` supports the following: * `source_vault_id` - (Required) Specifies the key vault to use. -* `vault_certificates` - (Required, on windows machines) A collection of Vault Certificates as documented below +* `vault_certificates` - (Required) A collection of Vault Certificates as documented below `vault_certificates` support the following: -* `certificate_url` - (Required) It is the Base64 encoding of a JSON Object that which is encoded in UTF-8 of which the contents need to be `data`, `dataType` and `password`. +* `certificate_url` - (Required) Specifies the URI of the key vault secrets in the format of `https:///secrets//`. Stored secret is the Base64 encoding of a JSON Object that which is encoded in UTF-8 of which the contents need to be + +``` +{ + "data":"", + "dataType":"pfx", + "password":"" +} +``` + * `certificate_store` - (Required, on windows machines) Specifies the certificate store on the Virtual Machine where the certificate should be added to. ## Attributes Reference From 5ea834d27fe58ea549a4f985a68413fd31d1b336 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 15:22:58 -0600 Subject: [PATCH 004/101] Randomize test name --- .../aws/resource_aws_ses_receipt_rule_test.go | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go b/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go index d39c4c03f9..64d04f923b 100644 --- a/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go +++ b/builtin/providers/aws/resource_aws_ses_receipt_rule_test.go @@ -186,8 +186,8 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc { conn := testAccProvider.Meta().(*AWSClient).sesConn params := &ses.DescribeReceiptRuleInput{ - RuleName: aws.String("actions"), - RuleSetName: aws.String(fmt.Sprintf("test-me")), + RuleName: aws.String("actions4"), + RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)), } response, err := conn.DescribeReceiptRule(params) @@ -267,28 +267,28 @@ resource "aws_s3_bucket" "emails" { } resource "aws_ses_receipt_rule_set" "test" { - rule_set_name = "test-me" + rule_set_name = "test-me-%d" } resource "aws_ses_receipt_rule" "actions" { - name = "actions" + name = "actions4" rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}" add_header_action { - header_name = "Added-By" - header_value = "Terraform" - position = 1 + header_name = "Added-By" + header_value = "Terraform" + position = 1 } add_header_action { - header_name = "Another-Header" - header_value = "First" - position = 0 + header_name = "Another-Header" + header_value = "First" + position = 0 } stop_action { - scope = "RuleSet" - position = 2 + scope = "RuleSet" + position = 2 } } -`) +`, srrsRandomInt) From 24d9458feb4b947fd6fa76913620f77c9dd02be1 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 28 Mar 2017 15:43:15 +0100 Subject: [PATCH 005/101] Removing the deprecated `location` parameter from the LB Docs --- .../azurerm/r/loadbalancer_backend_address_pool.html.markdown | 1 - .../providers/azurerm/r/loadbalancer_nat_rule.html.markdown | 2 -- 2 files changed, 3 deletions(-) diff --git a/website/source/docs/providers/azurerm/r/loadbalancer_backend_address_pool.html.markdown b/website/source/docs/providers/azurerm/r/loadbalancer_backend_address_pool.html.markdown index 385e2843c3..f63b782d92 100644 --- a/website/source/docs/providers/azurerm/r/loadbalancer_backend_address_pool.html.markdown +++ b/website/source/docs/providers/azurerm/r/loadbalancer_backend_address_pool.html.markdown @@ -51,7 +51,6 @@ The following arguments are supported: * `name` - (Required) Specifies the name of the Backend Address Pool. * `resource_group_name` - (Required) The name of the resource group in which to create the resource. -* `location` - (Required) Specifies the supported Azure location where the resource exists. * `loadbalancer_id` - (Required) The ID of the LoadBalancer in which to create the Backend Address Pool. ## Attributes Reference diff --git a/website/source/docs/providers/azurerm/r/loadbalancer_nat_rule.html.markdown b/website/source/docs/providers/azurerm/r/loadbalancer_nat_rule.html.markdown index 527f4de741..4eaaef7247 100644 --- a/website/source/docs/providers/azurerm/r/loadbalancer_nat_rule.html.markdown +++ b/website/source/docs/providers/azurerm/r/loadbalancer_nat_rule.html.markdown @@ -39,7 +39,6 @@ resource "azurerm_lb" "test" { } resource "azurerm_lb_nat_rule" "test" { - location = "West US" resource_group_name = "${azurerm_resource_group.test.name}" loadbalancer_id = "${azurerm_lb.test.id}" name = "RDP Access" @@ -56,7 +55,6 @@ The following arguments are supported: * `name` - (Required) Specifies the name of the NAT Rule. * `resource_group_name` - (Required) The name of the resource group in which to create the resource. -* `location` - (Required) Specifies the supported Azure location where the resource exists. * `loadbalancer_id` - (Required) The ID of the LoadBalancer in which to create the NAT Rule. * `frontend_ip_configuration_name` - (Required) The name of the frontend IP configuration exposing this rule. * `protocol` - (Required) The transport protocol for the external endpoint. Possible values are `Udp` or `Tcp`. From 52390473e260b6058d07fac3ae270089a5ebb11a Mon Sep 17 00:00:00 2001 From: = Date: Tue, 28 Mar 2017 09:46:22 -0600 Subject: [PATCH 006/101] Add randomness to roles for dms replication --- .../providers/aws/resource_aws_dms_replication_instance_test.go | 2 +- .../aws/resource_aws_dms_replication_subnet_group_test.go | 2 +- builtin/providers/aws/resource_aws_dms_replication_task_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_dms_replication_instance_test.go b/builtin/providers/aws/resource_aws_dms_replication_instance_test.go index 17e7f85c8d..3b6bb0d0ef 100644 --- a/builtin/providers/aws/resource_aws_dms_replication_instance_test.go +++ b/builtin/providers/aws/resource_aws_dms_replication_instance_test.go @@ -169,7 +169,7 @@ resource "aws_dms_replication_instance" "dms_replication_instance" { func dmsReplicationInstanceConfigUpdate(randId string) string { return fmt.Sprintf(` resource "aws_iam_role" "dms_iam_role" { - name = "dms-vpc-role" + name = "dms-vpc-role-%[1]s" assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" } diff --git a/builtin/providers/aws/resource_aws_dms_replication_subnet_group_test.go b/builtin/providers/aws/resource_aws_dms_replication_subnet_group_test.go index 574745f9e2..a6da50c9cc 100644 --- a/builtin/providers/aws/resource_aws_dms_replication_subnet_group_test.go +++ b/builtin/providers/aws/resource_aws_dms_replication_subnet_group_test.go @@ -102,7 +102,7 @@ func dmsReplicationSubnetGroupDestroy(s *terraform.State) error { func dmsReplicationSubnetGroupConfig(randId string) string { return fmt.Sprintf(` resource "aws_iam_role" "dms_iam_role" { - name = "dms-vpc-role" + name = "dms-vpc-role-%[1]s" assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" } diff --git a/builtin/providers/aws/resource_aws_dms_replication_task_test.go b/builtin/providers/aws/resource_aws_dms_replication_task_test.go index 07ac7f58f3..8b20abf863 100644 --- a/builtin/providers/aws/resource_aws_dms_replication_task_test.go +++ b/builtin/providers/aws/resource_aws_dms_replication_task_test.go @@ -102,7 +102,7 @@ func dmsReplicationTaskDestroy(s *terraform.State) error { func dmsReplicationTaskConfig(randId string) string { return fmt.Sprintf(` resource "aws_iam_role" "dms_iam_role" { - name = "dms-vpc-role" + name = "dms-vpc-role-%[1]s" assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" } From da7ef1bb76f99c24639e0cba1fb814b93208d921 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 28 Mar 2017 12:00:56 -0600 Subject: [PATCH 007/101] added randomness to elastic beanstalk environment tests --- ..._aws_elastic_beanstalk_environment_test.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elastic_beanstalk_environment_test.go b/builtin/providers/aws/resource_aws_elastic_beanstalk_environment_test.go index 305ab9c4a6..2a3b1e7629 100644 --- a/builtin/providers/aws/resource_aws_elastic_beanstalk_environment_test.go +++ b/builtin/providers/aws/resource_aws_elastic_beanstalk_environment_test.go @@ -661,7 +661,7 @@ resource "aws_elastic_beanstalk_environment" "tfenvtest" { func testAccBeanstalkWorkerEnvConfig(rInt int) string { return fmt.Sprintf(` resource "aws_iam_instance_profile" "tftest" { - name = "tftest_profile" + name = "tftest_profile-%d" roles = ["${aws_iam_role.tftest.name}"] } @@ -693,7 +693,7 @@ func testAccBeanstalkWorkerEnvConfig(rInt int) string { name = "IamInstanceProfile" value = "${aws_iam_instance_profile.tftest.name}" } - }`, rInt, rInt) + }`, rInt, rInt, rInt) } func testAccBeanstalkEnvCnamePrefixConfig(randString string, rInt int) string { @@ -937,24 +937,24 @@ resource "aws_s3_bucket_object" "default" { } resource "aws_elastic_beanstalk_application" "default" { - name = "tf-test-name" + name = "tf-test-name-%d" description = "tf-test-desc" } resource "aws_elastic_beanstalk_application_version" "default" { - application = "tf-test-name" + application = "tf-test-name-%d" name = "tf-test-version-label" bucket = "${aws_s3_bucket.default.id}" key = "${aws_s3_bucket_object.default.id}" } resource "aws_elastic_beanstalk_environment" "default" { - name = "tf-test-name" + name = "tf-test-name-%d" application = "${aws_elastic_beanstalk_application.default.name}" version_label = "${aws_elastic_beanstalk_application_version.default.name}" solution_stack_name = "64bit Amazon Linux running Python" } -`, randInt) +`, randInt, randInt, randInt, randInt) } func testAccBeanstalkEnvApplicationVersionConfigUpdate(randInt int) string { @@ -970,22 +970,22 @@ resource "aws_s3_bucket_object" "default" { } resource "aws_elastic_beanstalk_application" "default" { - name = "tf-test-name" + name = "tf-test-name-%d" description = "tf-test-desc" } resource "aws_elastic_beanstalk_application_version" "default" { - application = "tf-test-name" + application = "tf-test-name-%d" name = "tf-test-version-label-v2" bucket = "${aws_s3_bucket.default.id}" key = "${aws_s3_bucket_object.default.id}" } resource "aws_elastic_beanstalk_environment" "default" { - name = "tf-test-name" + name = "tf-test-name-%d" application = "${aws_elastic_beanstalk_application.default.name}" version_label = "${aws_elastic_beanstalk_application_version.default.name}" solution_stack_name = "64bit Amazon Linux running Python" } -`, randInt) +`, randInt, randInt, randInt, randInt) } From 5662b7e60fe363a69ef6890decf2e5474f34e97d Mon Sep 17 00:00:00 2001 From: = Date: Tue, 28 Mar 2017 14:56:57 -0600 Subject: [PATCH 008/101] add randomness to gateway and efs file system tests --- .../aws/import_aws_customer_gateway_test.go | 5 +- .../aws/import_aws_efs_file_system_test.go | 4 +- .../aws/resource_aws_customer_gateway_test.go | 114 ++++++++++-------- .../aws/resource_aws_efs_file_system_test.go | 58 +++++---- 4 files changed, 101 insertions(+), 80 deletions(-) diff --git a/builtin/providers/aws/import_aws_customer_gateway_test.go b/builtin/providers/aws/import_aws_customer_gateway_test.go index 37662760db..0c066a33f0 100644 --- a/builtin/providers/aws/import_aws_customer_gateway_test.go +++ b/builtin/providers/aws/import_aws_customer_gateway_test.go @@ -3,19 +3,20 @@ package aws import ( "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) func TestAccAWSCustomerGateway_importBasic(t *testing.T) { resourceName := "aws_customer_gateway.foo" - + randInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccCustomerGatewayConfig, + Config: testAccCustomerGatewayConfig(randInt), }, resource.TestStep{ diff --git a/builtin/providers/aws/import_aws_efs_file_system_test.go b/builtin/providers/aws/import_aws_efs_file_system_test.go index 46e0de4595..885ee9ddda 100644 --- a/builtin/providers/aws/import_aws_efs_file_system_test.go +++ b/builtin/providers/aws/import_aws_efs_file_system_test.go @@ -3,11 +3,13 @@ package aws import ( "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) func TestAccAWSEFSFileSystem_importBasic(t *testing.T) { resourceName := "aws_efs_file_system.foo-with-tags" + rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -15,7 +17,7 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) { CheckDestroy: testAccCheckEfsFileSystemDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccAWSEFSFileSystemConfigWithTags, + Config: testAccAWSEFSFileSystemConfigWithTags(rInt), }, resource.TestStep{ diff --git a/builtin/providers/aws/resource_aws_customer_gateway_test.go b/builtin/providers/aws/resource_aws_customer_gateway_test.go index 1938ce0bdd..118c2d71e2 100644 --- a/builtin/providers/aws/resource_aws_customer_gateway_test.go +++ b/builtin/providers/aws/resource_aws_customer_gateway_test.go @@ -10,12 +10,14 @@ import ( "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) func TestAccAWSCustomerGateway_basic(t *testing.T) { var gateway ec2.CustomerGateway + randInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_customer_gateway.foo", @@ -23,19 +25,19 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) { CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccCustomerGatewayConfig, + Config: testAccCustomerGatewayConfig(randInt), Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), }, { - Config: testAccCustomerGatewayConfigUpdateTags, + Config: testAccCustomerGatewayConfigUpdateTags(randInt), Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), }, { - Config: testAccCustomerGatewayConfigForceReplace, + Config: testAccCustomerGatewayConfigForceReplace(randInt), Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), @@ -46,6 +48,7 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) { func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) { var gateway ec2.CustomerGateway + randInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_customer_gateway.foo", @@ -53,13 +56,13 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) { CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccCustomerGatewayConfig, + Config: testAccCustomerGatewayConfig(randInt), Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), }, { - Config: testAccCustomerGatewayConfigIdentical, + Config: testAccCustomerGatewayConfigIdentical(randInt), ExpectError: regexp.MustCompile("An existing customer gateway"), }, }, @@ -68,13 +71,14 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) { func TestAccAWSCustomerGateway_disappears(t *testing.T) { var gateway ec2.CustomerGateway + randInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ { - Config: testAccCustomerGatewayConfig, + Config: testAccCustomerGatewayConfig(randInt), Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), testAccAWSCustomerGatewayDisappears(&gateway), @@ -190,59 +194,67 @@ func testAccCheckCustomerGateway(gatewayResource string, cgw *ec2.CustomerGatewa } } -const testAccCustomerGatewayConfig = ` -resource "aws_customer_gateway" "foo" { - bgp_asn = 65000 - ip_address = "172.0.0.1" - type = "ipsec.1" - tags { - Name = "foo-gateway" - } -} -` - -const testAccCustomerGatewayConfigIdentical = ` -resource "aws_customer_gateway" "foo" { - bgp_asn = 65000 - ip_address = "172.0.0.1" - type = "ipsec.1" - tags { - Name = "foo-gateway" +func testAccCustomerGatewayConfig(randInt int) string { + return fmt.Sprintf(` + resource "aws_customer_gateway" "foo" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-%d" + } } + `, randInt) } -resource "aws_customer_gateway" "identical" { - bgp_asn = 65000 - ip_address = "172.0.0.1" - type = "ipsec.1" - tags { - Name = "foo-gateway-identical" - } +func testAccCustomerGatewayConfigIdentical(randInt int) string { + return fmt.Sprintf(` + resource "aws_customer_gateway" "foo" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-%d" + } + } + + resource "aws_customer_gateway" "identical" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-identical-%d" + } + } + `, randInt, randInt) } -` // Add the Another: "tag" tag. -const testAccCustomerGatewayConfigUpdateTags = ` -resource "aws_customer_gateway" "foo" { - bgp_asn = 65000 - ip_address = "172.0.0.1" - type = "ipsec.1" - tags { - Name = "foo-gateway" - Another = "tag" - } +func testAccCustomerGatewayConfigUpdateTags(randInt int) string { + return fmt.Sprintf(` + resource "aws_customer_gateway" "foo" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-%d" + Another = "tag-%d" + } + } + `, randInt, randInt) } -` // Change the ip_address. -const testAccCustomerGatewayConfigForceReplace = ` -resource "aws_customer_gateway" "foo" { - bgp_asn = 65000 - ip_address = "172.10.10.1" - type = "ipsec.1" - tags { - Name = "foo-gateway" - Another = "tag" +func testAccCustomerGatewayConfigForceReplace(randInt int) string { + return fmt.Sprintf(` + resource "aws_customer_gateway" "foo" { + bgp_asn = 65000 + ip_address = "172.10.10.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-%d" + Another = "tag-%d" + } } + `, randInt, randInt) } -` diff --git a/builtin/providers/aws/resource_aws_efs_file_system_test.go b/builtin/providers/aws/resource_aws_efs_file_system_test.go index c404679c2b..b242fbf163 100644 --- a/builtin/providers/aws/resource_aws_efs_file_system_test.go +++ b/builtin/providers/aws/resource_aws_efs_file_system_test.go @@ -82,6 +82,7 @@ func TestResourceAWSEFSFileSystem_hasEmptyFileSystems(t *testing.T) { } func TestAccAWSEFSFileSystem_basic(t *testing.T) { + rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -104,7 +105,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) { ), }, { - Config: testAccAWSEFSFileSystemConfigWithTags, + Config: testAccAWSEFSFileSystemConfigWithTags(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckEfsFileSystem( "aws_efs_file_system.foo-with-tags", @@ -116,7 +117,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) { testAccCheckEfsFileSystemTags( "aws_efs_file_system.foo-with-tags", map[string]string{ - "Name": "foo-efs", + "Name": fmt.Sprintf("foo-efs-%d", rInt), "Another": "tag", }, ), @@ -143,13 +144,14 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) { } func TestAccAWSEFSFileSystem_pagedTags(t *testing.T) { + rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckEfsFileSystemDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSEFSFileSystemConfigPagedTags, + Config: testAccAWSEFSFileSystemConfigPagedTags(rInt), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr( "aws_efs_file_system.foo", @@ -312,34 +314,38 @@ resource "aws_efs_file_system" "foo" { } ` -const testAccAWSEFSFileSystemConfigPagedTags = ` -resource "aws_efs_file_system" "foo" { - creation_token = "radeksimko" - tags { - Name = "foo-efs" - Another = "tag" - Test = "yes" - User = "root" - Page = "1" - Environment = "prod" - CostCenter = "terraform" - AcceptanceTest = "PagedTags" - CreationToken = "radek" - PerfMode = "max" - Region = "us-west-2" +func testAccAWSEFSFileSystemConfigPagedTags(rInt int) string { + return fmt.Sprintf(` + resource "aws_efs_file_system" "foo" { + creation_token = "radeksimko" + tags { + Name = "foo-efs-%d" + Another = "tag" + Test = "yes" + User = "root" + Page = "1" + Environment = "prod" + CostCenter = "terraform" + AcceptanceTest = "PagedTags" + CreationToken = "radek" + PerfMode = "max" + Region = "us-west-2" + } } + `, rInt) } -` -const testAccAWSEFSFileSystemConfigWithTags = ` -resource "aws_efs_file_system" "foo-with-tags" { - creation_token = "yada_yada" - tags { - Name = "foo-efs" - Another = "tag" +func testAccAWSEFSFileSystemConfigWithTags(rInt int) string { + return fmt.Sprintf(` + resource "aws_efs_file_system" "foo-with-tags" { + creation_token = "yada_yada" + tags { + Name = "foo-efs-%d" + Another = "tag" + } } + `, rInt) } -` const testAccAWSEFSFileSystemConfigWithPerformanceMode = ` resource "aws_efs_file_system" "foo-with-performance-mode" { From 4619897f920d80acc72802b26bc28ea61c195857 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Tue, 28 Mar 2017 17:10:34 -0500 Subject: [PATCH 009/101] provider/aws: Don't set DBName on `aws_db_instance` from snapshot It turns out if you're trying to write a config to conditionally restore an instance from a snapshot, you end up painted into a corner with the combination of `snapshot_identifier` and `name`. You need `name` to be specified for DBs you're creating, but when `snapshot_identifier` is populated you need it to be blank or else the AWS API throws an error. The logical next step is to drop a ternary in: ```tf resource "aws_db_instance" "foo" { # ... snapshot_identifier = "${var.snap}" name = "${var.snap != "" ? "" : var.dbname}" } ``` **BUT** the above config will _replace_ the DB on subsequent runs as the config basically has `name = ""` which will trigger a ForceNew diff once the `name` is populated from the snapshot restore. **SO** we can get a workable solution by actively avoiding populating DBName when we're using one of the engines the docs explicitly mention. It does not look like there are any tests covering `snapshot_identifer`, so I'll subject this to some manual tests and follow up with some results. --- builtin/providers/aws/resource_aws_db_instance.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index e944489ab9..192ccab97d 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -407,7 +407,14 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error } if attr, ok := d.GetOk("name"); ok { - opts.DBName = aws.String(attr.(string)) + // "Note: This parameter [DBName] doesn't apply to the MySQL, PostgreSQL, or MariaDB engines." + // https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_RestoreDBInstanceFromDBSnapshot.html + switch strings.ToLower(d.Get("engine").(string)) { + case "mysql", "postgres", "mariadb": + // skip + default: + opts.DBName = aws.String(attr.(string)) + } } if attr, ok := d.GetOk("availability_zone"); ok { From 1a6f766376605f072a2a998b1bd9526495a79bb2 Mon Sep 17 00:00:00 2001 From: Daisuke Fujita Date: Wed, 29 Mar 2017 11:56:44 +0900 Subject: [PATCH 010/101] Remove alb_listener ssl_policy restriction --- website/source/docs/providers/aws/r/alb_listener.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/alb_listener.html.markdown b/website/source/docs/providers/aws/r/alb_listener.html.markdown index 7ca8672c15..96a827cbc9 100644 --- a/website/source/docs/providers/aws/r/alb_listener.html.markdown +++ b/website/source/docs/providers/aws/r/alb_listener.html.markdown @@ -43,7 +43,7 @@ The following arguments are supported: * `load_balancer_arn` - (Required, Forces New Resource) The ARN of the load balancer. * `port` - (Required) The port on which the load balancer is listening. * `protocol` - (Optional) The protocol for connections from clients to the load balancer. Valid values are `HTTP` and `HTTPS`. Defaults to `HTTP`. -* `ssl_policy` - (Optional) The name of the SSL Policy for the listener. Required if `protocol` is `HTTPS`. The only valid value is currently `ELBSecurityPolicy-2015-05`. +* `ssl_policy` - (Optional) The name of the SSL Policy for the listener. Required if `protocol` is `HTTPS`. * `certificate_arn` - (Optional) The ARN of the SSL server certificate. Exactly one certificate is required if the protocol is HTTPS. * `default_action` - (Required) An Action block. Action blocks are documented below. From 10f53e3471283b2fe2c91370181087852b6974fe Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 10:37:36 +0100 Subject: [PATCH 011/101] Add links to details about sensitive data in state (#13145) --- .../docs/providers/aws/r/api_gateway_domain_name.html.markdown | 3 +++ website/source/docs/providers/aws/r/db_instance.html.markdown | 2 ++ .../providers/aws/r/directory_service_directory.html.markdown | 3 +++ .../source/docs/providers/aws/r/dms_certificate.html.markdown | 3 +++ website/source/docs/providers/aws/r/dms_endpoint.html.markdown | 3 +++ .../docs/providers/aws/r/iam_server_certificate.html.markdown | 3 +++ .../docs/providers/aws/r/opsworks_mysql_layer.html.markdown | 3 +++ .../providers/aws/r/opsworks_rds_db_instance.html.markdown | 3 +++ website/source/docs/providers/aws/r/rds_cluster.html.markdown | 3 +++ .../source/docs/providers/aws/r/redshift_cluster.html.markdown | 3 +++ .../docs/providers/azurerm/r/container_registry.html.markdown | 3 +++ .../docs/providers/azurerm/r/container_service.html.markdown | 3 +++ .../source/docs/providers/azurerm/r/sql_server.html.markdown | 3 +++ .../azurerm/r/virtual_machine_scale_sets.html.markdown | 3 +++ .../source/docs/providers/google/r/compute_disk.html.markdown | 3 +++ .../docs/providers/google/r/container_cluster.html.markdown | 3 +++ website/source/docs/providers/google/r/sql_user.html.markdown | 3 +++ website/source/docs/providers/mysql/r/user.html.markdown | 3 +++ .../docs/providers/postgresql/r/postgresql_role.html.markdown | 3 +++ website/source/docs/providers/rabbitmq/r/user.html.markdown | 3 +++ 20 files changed, 59 insertions(+) diff --git a/website/source/docs/providers/aws/r/api_gateway_domain_name.html.markdown b/website/source/docs/providers/aws/r/api_gateway_domain_name.html.markdown index b58f4e6882..a83862b4c7 100644 --- a/website/source/docs/providers/aws/r/api_gateway_domain_name.html.markdown +++ b/website/source/docs/providers/aws/r/api_gateway_domain_name.html.markdown @@ -22,6 +22,9 @@ given domain name which is an alias (either Route53 alias or traditional CNAME) to the Cloudfront domain name exported in the `cloudfront_domain_name` attribute. +~> **Note:** All arguments including the private key will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/db_instance.html.markdown b/website/source/docs/providers/aws/r/db_instance.html.markdown index cc957a9ea5..e491e37842 100644 --- a/website/source/docs/providers/aws/r/db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/db_instance.html.markdown @@ -25,6 +25,8 @@ When upgrading the major version of an engine, `allow_major_version_upgrade` mus brief downtime as the server reboots. See the AWS Docs on [RDS Maintenance][2] for more information. +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). ## Example Usage diff --git a/website/source/docs/providers/aws/r/directory_service_directory.html.markdown b/website/source/docs/providers/aws/r/directory_service_directory.html.markdown index 63fa6090f7..23b1767bbf 100644 --- a/website/source/docs/providers/aws/r/directory_service_directory.html.markdown +++ b/website/source/docs/providers/aws/r/directory_service_directory.html.markdown @@ -10,6 +10,9 @@ description: |- Provides a Simple or Managed Microsoft directory in AWS Directory Service. +~> **Note:** All arguments including the password and customer username will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/dms_certificate.html.markdown b/website/source/docs/providers/aws/r/dms_certificate.html.markdown index 9bb3d7ff93..0e2e50eb6c 100644 --- a/website/source/docs/providers/aws/r/dms_certificate.html.markdown +++ b/website/source/docs/providers/aws/r/dms_certificate.html.markdown @@ -10,6 +10,9 @@ description: |- Provides a DMS (Data Migration Service) certificate resource. DMS certificates can be created, deleted, and imported. +~> **Note:** All arguments including the PEM encoded certificate will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/dms_endpoint.html.markdown b/website/source/docs/providers/aws/r/dms_endpoint.html.markdown index 669f1f2442..2ef626fad2 100644 --- a/website/source/docs/providers/aws/r/dms_endpoint.html.markdown +++ b/website/source/docs/providers/aws/r/dms_endpoint.html.markdown @@ -10,6 +10,9 @@ description: |- Provides a DMS (Data Migration Service) endpoint resource. DMS endpoints can be created, updated, deleted, and imported. +~> **Note:** All arguments including the password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/iam_server_certificate.html.markdown b/website/source/docs/providers/aws/r/iam_server_certificate.html.markdown index 4c6c4a673f..6430d1fb84 100644 --- a/website/source/docs/providers/aws/r/iam_server_certificate.html.markdown +++ b/website/source/docs/providers/aws/r/iam_server_certificate.html.markdown @@ -19,6 +19,9 @@ Certs uploaded to IAM can easily work with other AWS services such as: For information about server certificates in IAM, see [Managing Server Certificates][2] in AWS Documentation. +~> **Note:** All arguments including the private key will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage **Using certs on file:** diff --git a/website/source/docs/providers/aws/r/opsworks_mysql_layer.html.markdown b/website/source/docs/providers/aws/r/opsworks_mysql_layer.html.markdown index a79d7502d8..6032083eff 100644 --- a/website/source/docs/providers/aws/r/opsworks_mysql_layer.html.markdown +++ b/website/source/docs/providers/aws/r/opsworks_mysql_layer.html.markdown @@ -10,6 +10,9 @@ description: |- Provides an OpsWorks MySQL layer resource. +~> **Note:** All arguments including the root password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown b/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown index c39d79e9a0..542dd956f4 100644 --- a/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/opsworks_rds_db_instance.html.markdown @@ -10,6 +10,9 @@ description: |- Provides an OpsWorks RDS DB Instance resource. +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/rds_cluster.html.markdown b/website/source/docs/providers/aws/r/rds_cluster.html.markdown index c1f29e0f1c..b40cbab402 100644 --- a/website/source/docs/providers/aws/r/rds_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster.html.markdown @@ -26,6 +26,9 @@ phase because a modification has not yet taken place. You can use the brief downtime as the server reboots. See the AWS Docs on [RDS Maintenance][4] for more information. +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/aws/r/redshift_cluster.html.markdown b/website/source/docs/providers/aws/r/redshift_cluster.html.markdown index d4801b676a..99d6ac66ec 100644 --- a/website/source/docs/providers/aws/r/redshift_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/redshift_cluster.html.markdown @@ -8,6 +8,9 @@ sidebar_current: "docs-aws-resource-redshift-cluster" Provides a Redshift Cluster Resource. +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/azurerm/r/container_registry.html.markdown b/website/source/docs/providers/azurerm/r/container_registry.html.markdown index f9b25d4f58..df79b72550 100644 --- a/website/source/docs/providers/azurerm/r/container_registry.html.markdown +++ b/website/source/docs/providers/azurerm/r/container_registry.html.markdown @@ -10,6 +10,9 @@ description: |- Create as an Azure Container Registry instance. +~> **Note:** All arguments including the access key will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/azurerm/r/container_service.html.markdown b/website/source/docs/providers/azurerm/r/container_service.html.markdown index 082d610fe4..5319ce1438 100644 --- a/website/source/docs/providers/azurerm/r/container_service.html.markdown +++ b/website/source/docs/providers/azurerm/r/container_service.html.markdown @@ -10,6 +10,9 @@ description: |- Creates an Azure Container Service Instance +~> **Note:** All arguments including the client secret will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage (DCOS) ``` resource "azurerm_resource_group" "test" { diff --git a/website/source/docs/providers/azurerm/r/sql_server.html.markdown b/website/source/docs/providers/azurerm/r/sql_server.html.markdown index d61d1efa51..58b4a5fbb5 100644 --- a/website/source/docs/providers/azurerm/r/sql_server.html.markdown +++ b/website/source/docs/providers/azurerm/r/sql_server.html.markdown @@ -10,6 +10,9 @@ description: |- Allows you to manage an Azure SQL Database Server +~> **Note:** All arguments including the administrator login and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown b/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown index adfe9dd769..fb17859f6f 100644 --- a/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown +++ b/website/source/docs/providers/azurerm/r/virtual_machine_scale_sets.html.markdown @@ -10,6 +10,9 @@ description: |- Create a virtual machine scale set. +~> **Note:** All arguments including the administrator login and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/google/r/compute_disk.html.markdown b/website/source/docs/providers/google/r/compute_disk.html.markdown index 6544707dd0..08e73c23ff 100644 --- a/website/source/docs/providers/google/r/compute_disk.html.markdown +++ b/website/source/docs/providers/google/r/compute_disk.html.markdown @@ -10,6 +10,9 @@ description: |- Creates a new persistent disk within GCE, based on another disk. +~> **Note:** All arguments including the disk encryption key will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ```js diff --git a/website/source/docs/providers/google/r/container_cluster.html.markdown b/website/source/docs/providers/google/r/container_cluster.html.markdown index 1962ac2440..025603eb15 100644 --- a/website/source/docs/providers/google/r/container_cluster.html.markdown +++ b/website/source/docs/providers/google/r/container_cluster.html.markdown @@ -12,6 +12,9 @@ description: |- `node_version` are non-updateable. Changing any will cause recreation of the whole cluster! +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example usage ```js diff --git a/website/source/docs/providers/google/r/sql_user.html.markdown b/website/source/docs/providers/google/r/sql_user.html.markdown index a34a154aa5..7e4937d8ee 100644 --- a/website/source/docs/providers/google/r/sql_user.html.markdown +++ b/website/source/docs/providers/google/r/sql_user.html.markdown @@ -10,6 +10,9 @@ description: |- Creates a new Google SQL User on a Google SQL User Instance. For more information, see the [official documentation](https://cloud.google.com/sql/), or the [JSON API](https://cloud.google.com/sql/docs/admin-api/v1beta4/users). +~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage Example creating a SQL User. diff --git a/website/source/docs/providers/mysql/r/user.html.markdown b/website/source/docs/providers/mysql/r/user.html.markdown index 17500f03ae..ebf1011740 100644 --- a/website/source/docs/providers/mysql/r/user.html.markdown +++ b/website/source/docs/providers/mysql/r/user.html.markdown @@ -11,6 +11,9 @@ description: |- The ``mysql_user`` resource creates and manages a user on a MySQL server. +~> **Note:** All arguments including username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` diff --git a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown index cd79cfc4cf..c14f181f0c 100644 --- a/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown +++ b/website/source/docs/providers/postgresql/r/postgresql_role.html.markdown @@ -21,6 +21,9 @@ specified PostgreSQL ROLE owns objects in multiple PostgreSQL databases in the same PostgreSQL Cluster, one PostgreSQL provider per database must be created and all but the final ``postgresql_role`` must specify a `skip_drop_role`. +~> **Note:** All arguments including role name and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Usage ``` diff --git a/website/source/docs/providers/rabbitmq/r/user.html.markdown b/website/source/docs/providers/rabbitmq/r/user.html.markdown index b023cba802..2c3f9893d8 100644 --- a/website/source/docs/providers/rabbitmq/r/user.html.markdown +++ b/website/source/docs/providers/rabbitmq/r/user.html.markdown @@ -10,6 +10,9 @@ description: |- The ``rabbitmq_user`` resource creates and manages a user. +~> **Note:** All arguments including username and password will be stored in the raw state as plain-text. +[Read more about sensitive data in state](/docs/state/sensitive-data.html). + ## Example Usage ``` From bee2791286a07b8a5622c8104f64c596ecfbd276 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 12:43:23 +0300 Subject: [PATCH 012/101] provider/aws: Make alb_target_group_attachment port optional (#13139) Fixes: #9460 ``` % TF_LOG=1 make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBTargetGroupAttachment_' 2>~/tf.log ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBTargetGroupAttachment_ -timeout 120m === RUN TestAccAWSALBTargetGroupAttachment_basic --- PASS: TestAccAWSALBTargetGroupAttachment_basic (267.66s) === RUN TestAccAWSALBTargetGroupAttachment_withoutPort --- PASS: TestAccAWSALBTargetGroupAttachment_withoutPort (147.89s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 415.589s ``` --- ...esource_aws_alb_target_group_attachment.go | 56 +++++---- ...ce_aws_alb_target_group_attachment_test.go | 115 +++++++++++++++--- .../alb_target_group_attachment.html.markdown | 2 +- 3 files changed, 131 insertions(+), 42 deletions(-) diff --git a/builtin/providers/aws/resource_aws_alb_target_group_attachment.go b/builtin/providers/aws/resource_aws_alb_target_group_attachment.go index e6ba35b94c..55a3b73923 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group_attachment.go +++ b/builtin/providers/aws/resource_aws_alb_target_group_attachment.go @@ -34,7 +34,7 @@ func resourceAwsAlbTargetGroupAttachment() *schema.Resource { "port": { Type: schema.TypeInt, ForceNew: true, - Required: true, + Optional: true, }, }, } @@ -43,18 +43,21 @@ func resourceAwsAlbTargetGroupAttachment() *schema.Resource { func resourceAwsAlbAttachmentCreate(d *schema.ResourceData, meta interface{}) error { elbconn := meta.(*AWSClient).elbv2conn - params := &elbv2.RegisterTargetsInput{ - TargetGroupArn: aws.String(d.Get("target_group_arn").(string)), - Targets: []*elbv2.TargetDescription{ - { - Id: aws.String(d.Get("target_id").(string)), - Port: aws.Int64(int64(d.Get("port").(int))), - }, - }, + target := &elbv2.TargetDescription{ + Id: aws.String(d.Get("target_id").(string)), } - log.Printf("[INFO] Registering Target %s (%d) with Target Group %s", d.Get("target_id").(string), - d.Get("port").(int), d.Get("target_group_arn").(string)) + if v, ok := d.GetOk("port"); ok { + target.Port = aws.Int64(int64(v.(int))) + } + + params := &elbv2.RegisterTargetsInput{ + TargetGroupArn: aws.String(d.Get("target_group_arn").(string)), + Targets: []*elbv2.TargetDescription{target}, + } + + log.Printf("[INFO] Registering Target %s with Target Group %s", d.Get("target_id").(string), + d.Get("target_group_arn").(string)) _, err := elbconn.RegisterTargets(params) if err != nil { @@ -69,14 +72,17 @@ func resourceAwsAlbAttachmentCreate(d *schema.ResourceData, meta interface{}) er func resourceAwsAlbAttachmentDelete(d *schema.ResourceData, meta interface{}) error { elbconn := meta.(*AWSClient).elbv2conn + target := &elbv2.TargetDescription{ + Id: aws.String(d.Get("target_id").(string)), + } + + if v, ok := d.GetOk("port"); ok { + target.Port = aws.Int64(int64(v.(int))) + } + params := &elbv2.DeregisterTargetsInput{ TargetGroupArn: aws.String(d.Get("target_group_arn").(string)), - Targets: []*elbv2.TargetDescription{ - { - Id: aws.String(d.Get("target_id").(string)), - Port: aws.Int64(int64(d.Get("port").(int))), - }, - }, + Targets: []*elbv2.TargetDescription{target}, } _, err := elbconn.DeregisterTargets(params) @@ -93,14 +99,18 @@ func resourceAwsAlbAttachmentDelete(d *schema.ResourceData, meta interface{}) er // target, so there is no work to do beyond ensuring that the target and group still exist. func resourceAwsAlbAttachmentRead(d *schema.ResourceData, meta interface{}) error { elbconn := meta.(*AWSClient).elbv2conn + + target := &elbv2.TargetDescription{ + Id: aws.String(d.Get("target_id").(string)), + } + + if v, ok := d.GetOk("port"); ok { + target.Port = aws.Int64(int64(v.(int))) + } + resp, err := elbconn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{ TargetGroupArn: aws.String(d.Get("target_group_arn").(string)), - Targets: []*elbv2.TargetDescription{ - { - Id: aws.String(d.Get("target_id").(string)), - Port: aws.Int64(int64(d.Get("port").(int))), - }, - }, + Targets: []*elbv2.TargetDescription{target}, }) if err != nil { if isTargetGroupNotFound(err) { diff --git a/builtin/providers/aws/resource_aws_alb_target_group_attachment_test.go b/builtin/providers/aws/resource_aws_alb_target_group_attachment_test.go index bd063516db..32369d5d52 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group_attachment_test.go +++ b/builtin/providers/aws/resource_aws_alb_target_group_attachment_test.go @@ -3,14 +3,15 @@ package aws import ( "errors" "fmt" + "strconv" + "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elbv2" "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "strconv" - "testing" ) func TestAccAWSALBTargetGroupAttachment_basic(t *testing.T) { @@ -32,6 +33,25 @@ func TestAccAWSALBTargetGroupAttachment_basic(t *testing.T) { }) } +func TestAccAWSALBTargetGroupAttachment_withoutPort(t *testing.T) { + targetGroupName := fmt.Sprintf("test-target-group-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_alb_target_group.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSALBTargetGroupAttachmentDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSALBTargetGroupAttachmentConfigWithoutPort(targetGroupName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSALBTargetGroupAttachmentExists("aws_alb_target_group_attachment.test"), + ), + }, + }, + }) +} + func testAccCheckAWSALBTargetGroupAttachmentExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -45,15 +65,20 @@ func testAccCheckAWSALBTargetGroupAttachmentExists(n string) resource.TestCheckF conn := testAccProvider.Meta().(*AWSClient).elbv2conn - port, _ := strconv.Atoi(rs.Primary.Attributes["port"]) + _, hasPort := rs.Primary.Attributes["port"] + targetGroupArn, _ := rs.Primary.Attributes["target_group_arn"] + + target := &elbv2.TargetDescription{ + Id: aws.String(rs.Primary.Attributes["target_id"]), + } + if hasPort == true { + port, _ := strconv.Atoi(rs.Primary.Attributes["port"]) + target.Port = aws.Int64(int64(port)) + } + describe, err := conn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{ - TargetGroupArn: aws.String(rs.Primary.Attributes["target_group_arn"]), - Targets: []*elbv2.TargetDescription{ - { - Id: aws.String(rs.Primary.Attributes["target_id"]), - Port: aws.Int64(int64(port)), - }, - }, + TargetGroupArn: aws.String(targetGroupArn), + Targets: []*elbv2.TargetDescription{target}, }) if err != nil { @@ -76,15 +101,20 @@ func testAccCheckAWSALBTargetGroupAttachmentDestroy(s *terraform.State) error { continue } - port, _ := strconv.Atoi(rs.Primary.Attributes["port"]) + _, hasPort := rs.Primary.Attributes["port"] + targetGroupArn, _ := rs.Primary.Attributes["target_group_arn"] + + target := &elbv2.TargetDescription{ + Id: aws.String(rs.Primary.Attributes["target_id"]), + } + if hasPort == true { + port, _ := strconv.Atoi(rs.Primary.Attributes["port"]) + target.Port = aws.Int64(int64(port)) + } + describe, err := conn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{ - TargetGroupArn: aws.String(rs.Primary.Attributes["target_group_arn"]), - Targets: []*elbv2.TargetDescription{ - { - Id: aws.String(rs.Primary.Attributes["target_id"]), - Port: aws.Int64(int64(port)), - }, - }, + TargetGroupArn: aws.String(targetGroupArn), + Targets: []*elbv2.TargetDescription{target}, }) if err == nil { if len(describe.TargetHealthDescriptions) != 0 { @@ -103,6 +133,55 @@ func testAccCheckAWSALBTargetGroupAttachmentDestroy(s *terraform.State) error { return nil } +func testAccAWSALBTargetGroupAttachmentConfigWithoutPort(targetGroupName string) string { + return fmt.Sprintf(` +resource "aws_alb_target_group_attachment" "test" { + target_group_arn = "${aws_alb_target_group.test.arn}" + target_id = "${aws_instance.test.id}" +} + +resource "aws_instance" "test" { + ami = "ami-f701cb97" + instance_type = "t2.micro" + subnet_id = "${aws_subnet.subnet.id}" +} + +resource "aws_alb_target_group" "test" { + name = "%s" + port = 443 + protocol = "HTTPS" + vpc_id = "${aws_vpc.test.id}" + + deregistration_delay = 200 + + stickiness { + type = "lb_cookie" + cookie_duration = 10000 + } + + health_check { + path = "/health" + interval = 60 + port = 8081 + protocol = "HTTP" + timeout = 3 + healthy_threshold = 3 + unhealthy_threshold = 3 + matcher = "200-299" + } +} + +resource "aws_subnet" "subnet" { + cidr_block = "10.0.1.0/24" + vpc_id = "${aws_vpc.test.id}" + +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +}`, targetGroupName) +} + func testAccAWSALBTargetGroupAttachmentConfig_basic(targetGroupName string) string { return fmt.Sprintf(` resource "aws_alb_target_group_attachment" "test" { diff --git a/website/source/docs/providers/aws/r/alb_target_group_attachment.html.markdown b/website/source/docs/providers/aws/r/alb_target_group_attachment.html.markdown index e440f9eb9f..547f4c9911 100644 --- a/website/source/docs/providers/aws/r/alb_target_group_attachment.html.markdown +++ b/website/source/docs/providers/aws/r/alb_target_group_attachment.html.markdown @@ -36,7 +36,7 @@ The following arguments are supported: * `target_group_arn` - (Required) The ARN of the target group with which to register targets * `target_id` (Required) The ID of the target. This is the Instance ID for an instance, or the container ID for an ECS container. -* `port` - (Required) The port on which targets receive traffic. +* `port` - (Optional) The port on which targets receive traffic. ## Attributes Reference From 6c967d95b98d392490394969bfe1e10f3ce6a8e6 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 12:43:56 +0300 Subject: [PATCH 013/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a440d97d..806a4fbc01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ IMPROVEMENTS: * provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125] * provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125] * provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130] + * provider/aws: Make alb_target_group_attachment port optional [GH-13139] ## 0.9.2 (March 28, 2017) From f44f0d8c867a9763d4835d272f0b7319c338f0cd Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 12:44:44 +0300 Subject: [PATCH 014/101] provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance (#13134) * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance Fixes: #9489 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRDSClusterInstance_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/28 23:08:45 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRDSClusterInstance_basic -timeout 120m === RUN TestAccAWSRDSClusterInstance_basic --- PASS: TestAccAWSRDSClusterInstance_basic (1433.41s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1433.438s ``` * Update rds_cluster_instance.html.markdown * Update rds_cluster_instance.html.markdown --- .../aws/resource_aws_rds_cluster_instance.go | 44 +++++++++++++++++++ .../resource_aws_rds_cluster_instance_test.go | 2 + .../aws/r/rds_cluster_instance.html.markdown | 6 ++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance.go b/builtin/providers/aws/resource_aws_rds_cluster_instance.go index 6dedec175a..36f1116286 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -105,6 +106,27 @@ func resourceAwsRDSClusterInstance() *schema.Resource { Computed: true, }, + "preferred_maintenance_window": { + Type: schema.TypeString, + Optional: true, + Computed: true, + StateFunc: func(v interface{}) string { + if v != nil { + value := v.(string) + return strings.ToLower(value) + } + return "" + }, + ValidateFunc: validateOnceAWeekWindowFormat, + }, + + "preferred_backup_window": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateOnceADayWindowFormat, + }, + "monitoring_interval": { Type: schema.TypeInt, Optional: true, @@ -154,6 +176,14 @@ func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{ createOpts.MonitoringRoleArn = aws.String(attr.(string)) } + if attr, ok := d.GetOk("preferred_backup_window"); ok { + createOpts.PreferredBackupWindow = aws.String(attr.(string)) + } + + if attr, ok := d.GetOk("preferred_maintenance_window"); ok { + createOpts.PreferredMaintenanceWindow = aws.String(attr.(string)) + } + if attr, ok := d.GetOk("monitoring_interval"); ok { createOpts.MonitoringInterval = aws.Int64(int64(attr.(int))) } @@ -239,6 +269,8 @@ func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) d.Set("kms_key_id", db.KmsKeyId) d.Set("auto_minor_version_upgrade", db.AutoMinorVersionUpgrade) d.Set("promotion_tier", db.PromotionTier) + d.Set("preferred_backup_window", db.PreferredBackupWindow) + d.Set("preferred_maintenance_window", db.PreferredMaintenanceWindow) if db.MonitoringInterval != nil { d.Set("monitoring_interval", db.MonitoringInterval) @@ -290,6 +322,18 @@ func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{ requestUpdate = true } + if d.HasChange("preferred_backup_window") { + d.SetPartial("preferred_backup_window") + req.PreferredBackupWindow = aws.String(d.Get("preferred_backup_window").(string)) + requestUpdate = true + } + + if d.HasChange("preferred_maintenance_window") { + d.SetPartial("preferred_maintenance_window") + req.PreferredMaintenanceWindow = aws.String(d.Get("preferred_maintenance_window").(string)) + requestUpdate = true + } + if d.HasChange("monitoring_interval") { d.SetPartial("monitoring_interval") req.MonitoringInterval = aws.Int64(int64(d.Get("monitoring_interval").(int))) diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go index ab37a5bab9..b1e66ea7e2 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go @@ -30,6 +30,8 @@ func TestAccAWSRDSClusterInstance_basic(t *testing.T) { testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.cluster_instances", &v), testAccCheckAWSDBClusterInstanceAttributes(&v), resource.TestCheckResourceAttr("aws_rds_cluster_instance.cluster_instances", "auto_minor_version_upgrade", "true"), + resource.TestCheckResourceAttrSet("aws_rds_cluster_instance.cluster_instances", "preferred_maintenance_window"), + resource.TestCheckResourceAttrSet("aws_rds_cluster_instance.cluster_instances", "preferred_backup_window"), ), }, { diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index 095c36beac..d5196d7338 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -70,7 +70,11 @@ details on controlling this property. enhanced monitoring metrics to CloudWatch Logs. You can find more information on the [AWS Documentation](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Monitoring.html) what IAM permissions are needed to allow Enhanced Monitoring for RDS Instances. * `monitoring_interval` - (Optional) The interval, in seconds, between points when Enhanced Monitoring metrics are collected for the DB instance. To disable collecting Enhanced Monitoring metrics, specify 0. The default is 0. Valid Values: 0, 1, 5, 10, 15, 30, 60. -* `promotion_tier` - (Optional) Default 0. Failover Priority setting on instance level. The reader who has lower tier has higher priority to get promoter to writer +* `promotion_tier` - (Optional) Default 0. Failover Priority setting on instance level. The reader who has lower tier has higher priority to get promoter to writer. +* `preferred_backup_window` - (Optional) The daily time range during which automated backups are created if automated backups are enabled. + Eg: "04:00-09:00" +* `preferred_maintenance_window` - (Optional) The window to perform maintenance in. + Syntax: "ddd:hh24:mi-ddd:hh24:mi". Eg: "Mon:00:00-Mon:03:00". * `tags` - (Optional) A mapping of tags to assign to the instance. ## Attributes Reference From 55230c03df153ded05b64dda5534840ea8fcbc05 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 12:45:13 +0300 Subject: [PATCH 015/101] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 806a4fbc01..1753371039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ IMPROVEMENTS: * provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130] * provider/aws: Make alb_target_group_attachment port optional [GH-13139] +BUG FIXES: + + * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] + ## 0.9.2 (March 28, 2017) From 5db819d852f0b0ca99b2e2e984b40b827d0a96cf Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 11:15:22 +0100 Subject: [PATCH 016/101] provider/aws: Mark password fields as sensitive (#13147) --- .../aws/resource_aws_api_gateway_domain_name.go | 1 + .../aws/resource_aws_directory_service_directory.go | 7 ++++--- .../resource_aws_kinesis_firehose_delivery_stream.go | 5 +++-- .../providers/aws/resource_aws_opsworks_application.go | 10 ++++++---- builtin/providers/aws/resource_aws_opsworks_stack.go | 5 +++-- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/builtin/providers/aws/resource_aws_api_gateway_domain_name.go b/builtin/providers/aws/resource_aws_api_gateway_domain_name.go index 103f7bed4e..be90c40ec5 100644 --- a/builtin/providers/aws/resource_aws_api_gateway_domain_name.go +++ b/builtin/providers/aws/resource_aws_api_gateway_domain_name.go @@ -48,6 +48,7 @@ func resourceAwsApiGatewayDomainName() *schema.Resource { Type: schema.TypeString, ForceNew: true, Optional: true, + Sensitive: true, ConflictsWith: []string{"certificate_arn"}, }, diff --git a/builtin/providers/aws/resource_aws_directory_service_directory.go b/builtin/providers/aws/resource_aws_directory_service_directory.go index 773a5afd88..a9bd952dd2 100644 --- a/builtin/providers/aws/resource_aws_directory_service_directory.go +++ b/builtin/providers/aws/resource_aws_directory_service_directory.go @@ -33,9 +33,10 @@ func resourceAwsDirectoryServiceDirectory() *schema.Resource { ForceNew: true, }, "password": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, }, "size": &schema.Schema{ Type: schema.TypeString, diff --git a/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go index b82ec56bb3..3cd476be9b 100644 --- a/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go +++ b/builtin/providers/aws/resource_aws_kinesis_firehose_delivery_stream.go @@ -150,8 +150,9 @@ func resourceAwsKinesisFirehoseDeliveryStream() *schema.Resource { }, "password": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Sensitive: true, }, "role_arn": { diff --git a/builtin/providers/aws/resource_aws_opsworks_application.go b/builtin/providers/aws/resource_aws_opsworks_application.go index 3ffdb9ae4d..7333018e5f 100644 --- a/builtin/providers/aws/resource_aws_opsworks_application.go +++ b/builtin/providers/aws/resource_aws_opsworks_application.go @@ -102,8 +102,9 @@ func resourceAwsOpsworksApplication() *schema.Resource { }, "password": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, }, "revision": { @@ -187,8 +188,9 @@ func resourceAwsOpsworksApplication() *schema.Resource { }, }, "private_key": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + Sensitive: true, StateFunc: func(v interface{}) string { switch v.(type) { case string: diff --git a/builtin/providers/aws/resource_aws_opsworks_stack.go b/builtin/providers/aws/resource_aws_opsworks_stack.go index aae83c2679..ad6388ed8e 100644 --- a/builtin/providers/aws/resource_aws_opsworks_stack.go +++ b/builtin/providers/aws/resource_aws_opsworks_stack.go @@ -111,8 +111,9 @@ func resourceAwsOpsworksStack() *schema.Resource { }, "password": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, }, "revision": { From 23f0475872b51af2e3325c57d4dbffd243fc8ed2 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 11:19:58 +0100 Subject: [PATCH 017/101] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1753371039..c280d0a947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ IMPROVEMENTS: * provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125] * provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130] * provider/aws: Make alb_target_group_attachment port optional [GH-13139] + * provider/aws: `aws_api_gateway_domain_name` `certificate_private_key` field marked as sensitive [GH-13147] + * provider/aws: `aws_directory_service_directory` `password` field marked as sensitive [GH-13147] + * provider/aws: `aws_kinesis_firehose_delivery_stream` `password` field marked as sensitive [GH-13147] + * provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147] + * provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147] BUG FIXES: From 9e7839b0ed361807ba7bec1d128511a45a9c11fc Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 11:22:33 +0100 Subject: [PATCH 018/101] provider/google: Mark GKE pass as sensitive (#13148) --- .../providers/google/resource_container_cluster.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/builtin/providers/google/resource_container_cluster.go b/builtin/providers/google/resource_container_cluster.go index 203a990b85..084456f2af 100644 --- a/builtin/providers/google/resource_container_cluster.go +++ b/builtin/providers/google/resource_container_cluster.go @@ -40,17 +40,19 @@ func resourceContainerCluster() *schema.Resource { Computed: true, }, "client_key": &schema.Schema{ - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Sensitive: true, }, "cluster_ca_certificate": &schema.Schema{ Type: schema.TypeString, Computed: true, }, "password": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, }, "username": &schema.Schema{ Type: schema.TypeString, From ed2338ed86b8faf4a831f4af3aa02fda88f1630c Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 11:24:58 +0100 Subject: [PATCH 019/101] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c280d0a947..55d81b6440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ IMPROVEMENTS: * helper/resource: Allow unknown "pending" states [GH-13099] * provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547] * provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125] - * provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125] * provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130] * provider/aws: Make alb_target_group_attachment port optional [GH-13139] * provider/aws: `aws_api_gateway_domain_name` `certificate_private_key` field marked as sensitive [GH-13147] @@ -14,7 +13,8 @@ IMPROVEMENTS: * provider/aws: `aws_kinesis_firehose_delivery_stream` `password` field marked as sensitive [GH-13147] * provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147] * provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147] - + * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] + BUG FIXES: * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] From 0de008e07d41ad4635e7be7727b88109623f2828 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 13:32:17 +0300 Subject: [PATCH 020/101] docs/community: Removing @jen20 from the community page (#13150) --- website/source/community.html.erb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/website/source/community.html.erb b/website/source/community.html.erb index 92d9fff370..3db2397a3c 100644 --- a/website/source/community.html.erb +++ b/website/source/community.html.erb @@ -80,18 +80,6 @@ disappear from this list as contributors come and go. -
- -
-

James Nugent (@jen20)

-

- James Nugent is a HashiCorp Engineer working on Terraform. He is one of the - principal developers working in Terraform's core, though he can also be - found working on providers from time to time as well. -

-
-
-
From e899ab82892bcace3e3548f605213ee1db327fc2 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Wed, 29 Mar 2017 12:39:03 +0200 Subject: [PATCH 021/101] Adding myself to the community page --- website/source/community.html.erb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/website/source/community.html.erb b/website/source/community.html.erb index 3db2397a3c..556eed0d0f 100644 --- a/website/source/community.html.erb +++ b/website/source/community.html.erb @@ -102,7 +102,7 @@ disappear from this list as contributors come and go.

- +
@@ -114,5 +114,16 @@ disappear from this list as contributors come and go.
+
+ +
+

Tom Harvey (@tombuildsstuff)

+

+ Tom Harvey is a HashiCorp Engineer working on Terraform with an emphasis on + the Terraform Provider Ecosystem. +

+
+
+
From 21cd5595e2f3a2793b26df72c98b58236104aebd Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 28 Mar 2017 14:32:30 -0700 Subject: [PATCH 022/101] Update stringer-generated files to new boilerplate golang/tools commit 23ca8a263 changed the format of the leading comment to comply with some new standards discussed here: https://golang.org/issue/13560 This is the result of running generate with the latest version of stringer. Everyone working on Terraform will need to update stringer after this is merged, to avoid reverting this: go get -u golang.org/x/tools/cmd/stringer --- backend/local/counthookaction_string.go | 2 +- backend/operationtype_string.go | 2 +- command/counthookaction_string.go | 2 +- config/resource_mode_string.go | 2 +- helper/schema/getsource_string.go | 2 +- helper/schema/valuetype_string.go | 2 +- terraform/graphtype_string.go | 2 +- terraform/instancetype_string.go | 2 +- terraform/walkoperation_string.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/local/counthookaction_string.go b/backend/local/counthookaction_string.go index 574a8c6cb2..92b2624a53 100644 --- a/backend/local/counthookaction_string.go +++ b/backend/local/counthookaction_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT +// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT. package local diff --git a/backend/operationtype_string.go b/backend/operationtype_string.go index 6edadb919c..15fbba6ecc 100644 --- a/backend/operationtype_string.go +++ b/backend/operationtype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT +// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT. package backend diff --git a/command/counthookaction_string.go b/command/counthookaction_string.go index c0c40d0de6..423cae07ee 100644 --- a/command/counthookaction_string.go +++ b/command/counthookaction_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT +// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT. package command diff --git a/config/resource_mode_string.go b/config/resource_mode_string.go index 930645fa87..ea68b4fcdb 100644 --- a/config/resource_mode_string.go +++ b/config/resource_mode_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT +// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT. package config diff --git a/helper/schema/getsource_string.go b/helper/schema/getsource_string.go index 790dbff919..3a97629394 100644 --- a/helper/schema/getsource_string.go +++ b/helper/schema/getsource_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT +// Code generated by "stringer -type=getSource resource_data_get_source.go"; DO NOT EDIT. package schema diff --git a/helper/schema/valuetype_string.go b/helper/schema/valuetype_string.go index 08f008450f..1610cec2d3 100644 --- a/helper/schema/valuetype_string.go +++ b/helper/schema/valuetype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT +// Code generated by "stringer -type=ValueType valuetype.go"; DO NOT EDIT. package schema diff --git a/terraform/graphtype_string.go b/terraform/graphtype_string.go index 88ecad4f6b..e97b4855a9 100644 --- a/terraform/graphtype_string.go +++ b/terraform/graphtype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT +// Code generated by "stringer -type=GraphType context_graph_type.go"; DO NOT EDIT. package terraform diff --git a/terraform/instancetype_string.go b/terraform/instancetype_string.go index f65414b347..f69267cd52 100644 --- a/terraform/instancetype_string.go +++ b/terraform/instancetype_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT +// Code generated by "stringer -type=InstanceType instancetype.go"; DO NOT EDIT. package terraform diff --git a/terraform/walkoperation_string.go b/terraform/walkoperation_string.go index 8fb33d7b5a..cbd78dd93f 100644 --- a/terraform/walkoperation_string.go +++ b/terraform/walkoperation_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT +// Code generated by "stringer -type=walkOperation graph_walk_operation.go"; DO NOT EDIT. package terraform From f1fba64926862e3c8074edf18a8aff66c1e50b1a Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 16:47:42 +0100 Subject: [PATCH 023/101] docs/aws: Improve ElasticSearch Domain docs (#13157) --- .../providers/aws/r/elasticsearch_domain.html.markdown | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/elasticsearch_domain.html.markdown b/website/source/docs/providers/aws/r/elasticsearch_domain.html.markdown index c422e6ccb4..cf18732e76 100644 --- a/website/source/docs/providers/aws/r/elasticsearch_domain.html.markdown +++ b/website/source/docs/providers/aws/r/elasticsearch_domain.html.markdown @@ -15,6 +15,9 @@ description: |- resource "aws_elasticsearch_domain" "es" { domain_name = "tf-test" elasticsearch_version = "1.5" + cluster_config { + instance_type = "r3.large.elasticsearch" + } advanced_options { "rest.action.multi.allow_explicit_index" = true @@ -53,7 +56,7 @@ The following arguments are supported: * `domain_name` - (Required) Name of the domain. * `access_policies` - (Optional) IAM policy document specifying the access policies for the domain * `advanced_options` - (Optional) Key-value string pairs to specify advanced configuration options. -* `ebs_options` - (Optional) EBS related options, see below. +* `ebs_options` - (Optional) EBS related options, may be required based on chosen [instance size](https://aws.amazon.com/elasticsearch-service/pricing/). See below. * `cluster_config` - (Optional) Cluster configuration of the domain, see below. * `snapshot_options` - (Optional) Snapshot related options, see below. * `elasticsearch_version` - (Optional) The version of ElasticSearch to deploy. Defaults to `1.5` @@ -63,7 +66,7 @@ The following arguments are supported: * `ebs_enabled` - (Required) Whether EBS volumes are attached to data nodes in the domain * `volume_type` - (Optional) The type of EBS volumes attached to data nodes. -* `volume_size` - The size of EBS volumes attached to data nodes. +* `volume_size` - The size of EBS volumes attached to data nodes (in GB). **Required** if `ebs_enabled` is set to `true`. * `iops` - (Optional) The baseline input/output (I/O) performance of EBS volumes attached to data nodes. Applicable only for the Provisioned IOPS EBS volume type. From 25da34054315c95a80ac2369409fd3bb90eaf1d1 Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Wed, 29 Mar 2017 17:55:20 +0200 Subject: [PATCH 024/101] Ignoring the case for NSG Protocol's in the state (#13153) --- .../providers/azurerm/resource_arm_network_security_group.go | 1 + .../azurerm/resource_arm_network_security_group_test.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/providers/azurerm/resource_arm_network_security_group.go b/builtin/providers/azurerm/resource_arm_network_security_group.go index 7ba17e8a7a..c538e6103e 100644 --- a/builtin/providers/azurerm/resource_arm_network_security_group.go +++ b/builtin/providers/azurerm/resource_arm_network_security_group.go @@ -66,6 +66,7 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validateNetworkSecurityRuleProtocol, + StateFunc: ignoreCaseStateFunc, }, "source_port_range": { diff --git a/builtin/providers/azurerm/resource_arm_network_security_group_test.go b/builtin/providers/azurerm/resource_arm_network_security_group_test.go index 39fa092e05..3e7685ae31 100644 --- a/builtin/providers/azurerm/resource_arm_network_security_group_test.go +++ b/builtin/providers/azurerm/resource_arm_network_security_group_test.go @@ -204,7 +204,7 @@ resource "azurerm_network_security_group" "test" { priority = 100 direction = "Inbound" access = "Allow" - protocol = "Tcp" + protocol = "TCP" source_port_range = "*" destination_port_range = "*" source_address_prefix = "*" From 3c296dc5d005f064876bf2db2284ccd4eb373d1e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 29 Mar 2017 18:55:47 +0300 Subject: [PATCH 025/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55d81b6440..4aed6fcf26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ IMPROVEMENTS: BUG FIXES: * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] + * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 76dca009e0f048ea35e86f3737e7f63503378789 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 08:34:45 -0700 Subject: [PATCH 026/101] Allow escaped interpolation-like sequences in variable defaults The variable validator assumes that any AST node it gets from an interpolation walk is an indicator of an interpolation. Unfortunately, back in f223be15 we changed the interpolation walker to emit a LiteralNode as a way to signal that the result is a literal but not identical to the input due to escapes. The existence of this issue suggests a bit of a design smell in that the interpolation walker interface at first glance appears to skip over all literals, but it actually emits them in this one situation. In the long run we should perhaps think about whether the abstraction is right here, but this is a shallow, tactical change that fixes #13001. --- command/test-fixtures/validate-valid/main.tf | 5 +++++ config/config.go | 11 +++++++++-- config/config_test.go | 7 +++++++ .../validate-var-default-interpolate-escaped/main.tf | 5 +++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 config/test-fixtures/validate-var-default-interpolate-escaped/main.tf diff --git a/command/test-fixtures/validate-valid/main.tf b/command/test-fixtures/validate-valid/main.tf index fd9da13e00..2dcb1eccd0 100644 --- a/command/test-fixtures/validate-valid/main.tf +++ b/command/test-fixtures/validate-valid/main.tf @@ -1,3 +1,8 @@ +variable "var_with_escaped_interp" { + # This is here because in the past it failed. See Github #13001 + default = "foo-$${bar.baz}" +} + resource "test_instance" "foo" { ami = "bar" diff --git a/config/config.go b/config/config.go index bf064e57a8..f944cadd32 100644 --- a/config/config.go +++ b/config/config.go @@ -285,8 +285,15 @@ func (c *Config) Validate() error { } interp := false - fn := func(ast.Node) (interface{}, error) { - interp = true + fn := func(n ast.Node) (interface{}, error) { + // LiteralNode is a literal string (outside of a ${ ... } sequence). + // interpolationWalker skips most of these. but in particular it + // visits those that have escaped sequences (like $${foo}) as a + // signal that *some* processing is required on this string. For + // our purposes here though, this is fine and not an interpolation. + if _, ok := n.(*ast.LiteralNode); !ok { + interp = true + } return "", nil } diff --git a/config/config_test.go b/config/config_test.go index b391295c86..68a93d9b63 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -572,6 +572,13 @@ func TestConfigValidate_varDefaultInterpolate(t *testing.T) { } } +func TestConfigValidate_varDefaultInterpolateEscaped(t *testing.T) { + c := testConfig(t, "validate-var-default-interpolate-escaped") + if err := c.Validate(); err != nil { + t.Fatalf("should be valid, but got err: %s", err) + } +} + func TestConfigValidate_varDup(t *testing.T) { c := testConfig(t, "validate-var-dup") if err := c.Validate(); err == nil { diff --git a/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf b/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf new file mode 100644 index 0000000000..da2758f6ab --- /dev/null +++ b/config/test-fixtures/validate-var-default-interpolate-escaped/main.tf @@ -0,0 +1,5 @@ +variable "foo" { + # This should be considered valid since the sequence is escaped and is + # thus not actually an interpolation. + default = "foo bar $${aws_instance.foo.bar}" +} From 4ed0f769222b70c6b8458fe0800aa98d9ed4629b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:28:12 -0700 Subject: [PATCH 027/101] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aed6fcf26..6dfc29f036 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ BUG FIXES: * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] - + * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] ## 0.9.2 (March 28, 2017) From 51081678a45fdb4276f0a914c848d6a661879ab8 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:35:09 -0700 Subject: [PATCH 028/101] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dfc29f036..3a7f57a38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,9 @@ IMPROVEMENTS: BUG FIXES: + * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] - * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] ## 0.9.2 (March 28, 2017) From 261f5f8a76ac6b912b44cc943f966c2ae2534ba6 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:06:17 +0100 Subject: [PATCH 029/101] provider/aws: Increase timeout for AMI registration (#13159) --- builtin/providers/aws/resource_aws_ami.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_ami.go b/builtin/providers/aws/resource_aws_ami.go index 6e4ee15220..95b29678ca 100644 --- a/builtin/providers/aws/resource_aws_ami.go +++ b/builtin/providers/aws/resource_aws_ami.go @@ -18,7 +18,7 @@ import ( ) const ( - AWSAMIRetryTimeout = 10 * time.Minute + AWSAMIRetryTimeout = 20 * time.Minute AWSAMIDeleteRetryTimeout = 20 * time.Minute AWSAMIRetryDelay = 5 * time.Second AWSAMIRetryMinTimeout = 3 * time.Second From 4b8e235510eecc9ba6f143252b99aa2412d888fb Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:06:51 +0100 Subject: [PATCH 030/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7f57a38c..c3c1358cdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ BUG FIXES: * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] + * provider/aws: Increase timeout for AMI registration [GH-13159] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 75979afcb729ec5d42e906f62930f352f7f1170c Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:07:11 +0100 Subject: [PATCH 031/101] provider/aws: Fix ES Domain acceptance tests (#13160) --- .../aws/resource_aws_elasticsearch_domain.go | 1 + ...rce_aws_elasticsearch_domain_policy_test.go | 7 +++++++ .../resource_aws_elasticsearch_domain_test.go | 18 +++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain.go b/builtin/providers/aws/resource_aws_elasticsearch_domain.go index 7a8753efd0..c931b119ec 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain.go @@ -83,6 +83,7 @@ func resourceAwsElasticSearchDomain() *schema.Resource { "volume_type": { Type: schema.TypeString, Optional: true, + Computed: true, }, }, }, diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go b/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go index 76f650f6c2..5efd3eb994 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain_policy_test.go @@ -85,6 +85,13 @@ func testAccESDomainPolicyConfig(randInt int, policy string) string { resource "aws_elasticsearch_domain" "example" { domain_name = "tf-test-%d" elasticsearch_version = "2.3" + cluster_config { + instance_type = "t2.micro.elasticsearch" + } + ebs_options { + ebs_enabled = true + volume_size = 10 + } } resource "aws_elasticsearch_domain_policy" "main" { diff --git a/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go b/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go index ed96dcd0db..e5bf282dee 100644 --- a/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go +++ b/builtin/providers/aws/resource_aws_elasticsearch_domain_test.go @@ -96,7 +96,7 @@ func TestAccAWSElasticSearchDomain_complex(t *testing.T) { }) } -func TestAccAWSElasticSearch_tags(t *testing.T) { +func TestAccAWSElasticSearchDomain_tags(t *testing.T) { var domain elasticsearch.ElasticsearchDomainStatus var td elasticsearch.ListTagsOutput ri := acctest.RandInt() @@ -198,6 +198,10 @@ func testAccESDomainConfig(randInt int) string { return fmt.Sprintf(` resource "aws_elasticsearch_domain" "example" { domain_name = "tf-test-%d" + ebs_options { + ebs_enabled = true + volume_size = 10 + } } `, randInt) } @@ -206,6 +210,10 @@ func testAccESDomainConfig_TagUpdate(randInt int) string { return fmt.Sprintf(` resource "aws_elasticsearch_domain" "example" { domain_name = "tf-test-%d" + ebs_options { + ebs_enabled = true + volume_size = 10 + } tags { foo = "bar" @@ -220,6 +228,10 @@ func testAccESDomainConfig_complex(randInt int) string { resource "aws_elasticsearch_domain" "example" { domain_name = "tf-test-%d" + cluster_config { + instance_type = "r3.large.elasticsearch" + } + advanced_options { "indices.fielddata.cache.size" = 80 } @@ -248,6 +260,10 @@ func testAccESDomainConfigV23(randInt int) string { return fmt.Sprintf(` resource "aws_elasticsearch_domain" "example" { domain_name = "tf-test-%d" + ebs_options { + ebs_enabled = true + volume_size = 10 + } elasticsearch_version = "2.3" } `, randInt) From 0c561535e9a4f4db354c99634e3e1680c46b212f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:09:37 +0100 Subject: [PATCH 032/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3c1358cdc..12f5d25733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ BUG FIXES: * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/aws: Increase timeout for AMI registration [GH-13159] + * provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From dc5b1d936ba09152b5dfe237f0c258bdb10fa2f0 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:10:38 +0100 Subject: [PATCH 033/101] provider/aws: Increase timeouts for ELB (#13161) --- builtin/providers/aws/resource_aws_elb.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 2379ea49a1..3878c9611f 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -287,7 +287,7 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts) - err = resource.Retry(1*time.Minute, func() *resource.RetryError { + err = resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := elbconn.CreateLoadBalancer(elbOpts) if err != nil { @@ -488,7 +488,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { // Occasionally AWS will error with a 'duplicate listener', without any // other listeners on the ELB. Retry here to eliminate that. - err := resource.Retry(1*time.Minute, func() *resource.RetryError { + err := resource.Retry(5*time.Minute, func() *resource.RetryError { log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts) if _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts); err != nil { if awsErr, ok := err.(awserr.Error); ok { @@ -746,7 +746,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] ELB attach subnets opts: %s", attachOpts) - err := resource.Retry(1*time.Minute, func() *resource.RetryError { + err := resource.Retry(5*time.Minute, func() *resource.RetryError { _, err := elbconn.AttachLoadBalancerToSubnets(attachOpts) if err != nil { if awsErr, ok := err.(awserr.Error); ok { From 6cae2a5eb1264534ef7843c05d6101838721e328 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 29 Mar 2017 18:11:06 +0100 Subject: [PATCH 034/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f5d25733..1273e04236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ BUG FIXES: * core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137] * provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134] * provider/aws: Increase timeout for AMI registration [GH-13159] + * provider/aws: Increase timeouts for ELB [GH-13161] * provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] From 0d6d2f1cb489255080072af320e38041a3b21f8b Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:48:58 -0700 Subject: [PATCH 035/101] website: community people pics consistently spaced Most of the bios have five lines of text, so that's the driver for the layout except for @grubernaut's and @mbfrahry's where there's only three lines. This makes the pictures following the short bios look misaligned. This quick fix just always leaves enough room for five lines of bio text, ensuring that the photos appear at a consistent vertical rhythm. It's annoying to hard-code a particular value here, since this value won't survive e.g. a change to the typesetting, but a more involved fix here (using flexbox layout, or something else complicated) doesn't seem warranted. --- website/source/assets/stylesheets/_community.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/website/source/assets/stylesheets/_community.scss b/website/source/assets/stylesheets/_community.scss index de945fa388..ff8c3412ea 100644 --- a/website/source/assets/stylesheets/_community.scss +++ b/website/source/assets/stylesheets/_community.scss @@ -3,6 +3,7 @@ .person { margin-bottom: 40px; + min-height: 165px; // enough space for five lines of bio text h3 { text-transform: none; From 80a1539bca01293740d5760ce2cb4dd56c43ca62 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 09:57:21 -0700 Subject: [PATCH 036/101] Add apparentlymart to the community page. --- website/source/community.html.erb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/website/source/community.html.erb b/website/source/community.html.erb index 556eed0d0f..ca475cdd78 100644 --- a/website/source/community.html.erb +++ b/website/source/community.html.erb @@ -125,5 +125,16 @@ disappear from this list as contributors come and go. +
+ +
+

Martin Atkins (@apparentlymart)

+

+ Martin Atkins is a community contributor turned HashiCorp Engineer working + on Terraform with a focus on the core. +

+
+
+
From 3456f12f6a2b68216377fd276256b611a8b2923a Mon Sep 17 00:00:00 2001 From: joelcollin Date: Wed, 29 Mar 2017 15:06:26 -0400 Subject: [PATCH 037/101] Fixed typo in subscription (was subscripion) (#13172) --- .../docs/providers/google/r/pubsub_subscription.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/google/r/pubsub_subscription.html.markdown b/website/source/docs/providers/google/r/pubsub_subscription.html.markdown index 66715dab73..d5b7aed183 100644 --- a/website/source/docs/providers/google/r/pubsub_subscription.html.markdown +++ b/website/source/docs/providers/google/r/pubsub_subscription.html.markdown @@ -6,7 +6,7 @@ description: |- Creates a subscription in Google's pubsub queueing system --- -# google\_pubsub\_subscripion +# google\_pubsub\_subscription Creates a subscription in Google's pubsub queueing system. For more information see [the official documentation](https://cloud.google.com/pubsub/docs) and From b49e4035733121eaff6ef5cd44a374fc41358559 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Wed, 29 Mar 2017 14:18:12 -0500 Subject: [PATCH 038/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1273e04236..5fda0ede40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ BUG FIXES: * provider/aws: Increase timeout for AMI registration [GH-13159] * provider/aws: Increase timeouts for ELB [GH-13161] * provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160] + * provider/aws: Don't set DBName on `aws_db_instance` from snapshot [GH-13140] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From ff2d753062b9a9d40dafbdb02839c2aff0701dfc Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 12:50:20 -0400 Subject: [PATCH 039/101] add Rehash to terraform.BackendState This method mirrors that of config.Backend, so we can compare the configration of a backend read from a config vs that of a backend read from a state. This will prevent init from reinitializing when using `-backend-config` options that match the existing state. --- command/init_test.go | 58 +++++++++++++++++++ command/meta_backend.go | 18 +++++- .../test-fixtures/init-backend-empty/main.tf | 4 ++ config/config_terraform.go | 2 +- terraform/state.go | 27 +++++++++ 5 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 command/test-fixtures/init-backend-empty/main.tf diff --git a/command/init_test.go b/command/init_test.go index dee54495d1..ede29cfc32 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -406,6 +406,64 @@ func TestInit_copyBackendDst(t *testing.T) { } } +func TestInit_backendReinitWithExtra(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + m := testMetaBackend(t, nil) + opts := &BackendOpts{ + ConfigExtra: map[string]interface{}{"path": "hello"}, + Init: true, + } + + b, err := m.backendConfig(opts) + if err != nil { + t.Fatal(err) + } + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{"-backend-config", "path=hello"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + // Read our saved backend config and verify we have our settings + state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "hello" { + t.Fatalf("bad: %#v", v) + } + + if state.Backend.Hash != b.Hash { + t.Fatal("mismatched state and config backend hashes") + } + + if state.Backend.Rehash() != b.Rehash() { + t.Fatal("mismatched state and config re-hashes") + } + + // init again and make sure nothing changes + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + state = testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "hello" { + t.Fatalf("bad: %#v", v) + } + + if state.Backend.Hash != b.Hash { + t.Fatal("mismatched state and config backend hashes") + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta_backend.go b/command/meta_backend.go index b30659dc0e..8cf6390446 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -415,8 +415,16 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { case c != nil && s.Remote.Empty() && !s.Backend.Empty(): // If our configuration is the same, then we're just initializing // a previously configured remote backend. - if !s.Backend.Empty() && s.Backend.Hash == cHash { - return m.backend_C_r_S_unchanged(c, sMgr) + if !s.Backend.Empty() { + hash := s.Backend.Hash + // on init we need an updated hash containing any extra options + // that were added after merging. + if opts.Init { + hash = s.Backend.Rehash() + } + if hash == cHash { + return m.backend_C_r_S_unchanged(c, sMgr) + } } if !opts.Init { @@ -451,7 +459,11 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { case c != nil && !s.Remote.Empty() && !s.Backend.Empty(): // If the hashes are the same, we have a legacy remote state with // an unchanged stored backend state. - if s.Backend.Hash == cHash { + hash := s.Backend.Hash + if opts.Init { + hash = s.Backend.Rehash() + } + if hash == cHash { if !opts.Init { initReason := fmt.Sprintf( "Legacy remote state found with configured backend %q", diff --git a/command/test-fixtures/init-backend-empty/main.tf b/command/test-fixtures/init-backend-empty/main.tf new file mode 100644 index 0000000000..7f62e0e197 --- /dev/null +++ b/command/test-fixtures/init-backend-empty/main.tf @@ -0,0 +1,4 @@ +terraform { + backend "local" { + } +} diff --git a/config/config_terraform.go b/config/config_terraform.go index a547cc798d..8535c96485 100644 --- a/config/config_terraform.go +++ b/config/config_terraform.go @@ -72,7 +72,7 @@ type Backend struct { Hash uint64 } -// Hash returns a unique content hash for this backend's configuration +// Rehash returns a unique content hash for this backend's configuration // as a uint64 value. func (b *Backend) Rehash() uint64 { // If we have no backend, the value is zero diff --git a/terraform/state.go b/terraform/state.go index 4e5aa713f9..d5adefca10 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/config" "github.com/mitchellh/copystructure" + "github.com/mitchellh/hashstructure" "github.com/satori/go.uuid" ) @@ -801,6 +802,32 @@ func (s *BackendState) Empty() bool { return s == nil || s.Type == "" } +// Rehash returns a unique content hash for this backend's configuration +// as a uint64 value. +// The Hash stored in the backend state needs to match the config itself, but +// we need to compare the backend config after it has been combined with all +// options. +// This function must match the implementation used by config.Backend. +func (s *BackendState) Rehash() uint64 { + if s == nil { + return 0 + } + + // Use hashstructure to hash only our type with the config. + code, err := hashstructure.Hash(map[string]interface{}{ + "type": s.Type, + "config": s.Config, + }, nil) + + // This should never happen since we have just some basic primitives + // so panic if there is an error. + if err != nil { + panic(err) + } + + return code +} + // RemoteState is used to track the information about a remote // state store that we push/pull state to. type RemoteState struct { From c891ab50b7bd1c2f282a97d324a6ef4ea63a0cd3 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 15:51:24 -0400 Subject: [PATCH 040/101] detect when backend.Hash needs update It's possible to not change the backend config, but require updating the stored backend state by moving init options from the config file to the `-backend-config` flag. If the config is the same, but the hash doesn't match, update the stored state. --- command/init_test.go | 44 +++++++++++++++++++++++++++++++++++++++++ command/meta_backend.go | 10 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/command/init_test.go b/command/init_test.go index ede29cfc32..a0a8c32a03 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -464,6 +464,50 @@ func TestInit_backendReinitWithExtra(t *testing.T) { } } +// move option from config to -backend-config args +func TestInit_backendReinitConfigToExtra(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + if code := c.Run([]string{"-input=false"}); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + // Read our saved backend config and verify we have our settings + state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + if v := state.Backend.Config["path"]; v != "foo" { + t.Fatalf("bad: %#v", v) + } + + backendHash := state.Backend.Hash + + // init again but remove the path option from the config + cfg := "terraform {\n backend \"local\" {}\n}\n" + if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil { + t.Fatal(err) + } + + args := []string{"-input=false", "-backend-config=path=foo"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + state = testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename)) + + if state.Backend.Hash == backendHash { + t.Fatal("state.Backend.Hash was not updated") + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta_backend.go b/command/meta_backend.go index 8cf6390446..c50214116b 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -1158,6 +1158,16 @@ func (m *Meta) backend_C_r_S_unchanged( c *config.Backend, sMgr state.State) (backend.Backend, error) { s := sMgr.State() + // it's possible for a backend to be unchanged, and the config itself to + // have changed by moving a paramter from the config to `-backend-config` + // In this case we only need to update the Hash. + if c != nil && s.Backend.Hash != c.Hash { + s.Backend.Hash = c.Hash + if err := sMgr.WriteState(s); err != nil { + return nil, fmt.Errorf(errBackendWriteSaved, err) + } + } + // Create the config. We do this from the backend state since this // has the complete configuration data whereas the config itself // may require input. From da1905d5e1a2c197f9ceceb47e69438709573362 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 29 Mar 2017 13:51:01 -0700 Subject: [PATCH 041/101] Remove .gitattributes, treating all files as binary This was added earlier in an attempt to tolerate CRLF and convert CRLF line endings on Windows, but it causes issues where vendored files (which could be using either LF or CRLF depending on the original author's preference) get permanent diffs when inconsistent with the platform's preference. The goal of this change, therefore, is to treat all of the files as binary, with the standard that all of Terraform's own files will use Unix-style LF endings and the vendor stuff will just be verbatim, byte-for-byte copies of what's upstream. This will apparently cause some difficulty for people hacking on Terraform on Windows machines, because gofmt on Windows reportedly wants to convert all files to CRLF endings. Unfortunately we're forced to compromise here and treat development on Windows as an edge case in order to avoid the weirdness with inconsistent endings in the vendor tree. --- .gitattributes | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 6eb3a078e0..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -# Set the default behavior, in case people don't have core.autocrlf set. -* text=auto - -*.go eol=lf From 7d23e1ef2090d1190ff514fdb0515111eded9cb5 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 17:50:55 -0400 Subject: [PATCH 042/101] add equivalent tests to meta_backend_test --- command/meta_backend_test.go | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/command/meta_backend_test.go b/command/meta_backend_test.go index 8c1fe2d668..143759f985 100644 --- a/command/meta_backend_test.go +++ b/command/meta_backend_test.go @@ -3217,6 +3217,110 @@ func TestMetaBackend_planLegacy(t *testing.T) { } } +// init a backend using -backend-config options multiple times +func TestMetaBackend_configureWithExtra(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend-empty"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + extras := map[string]interface{}{"path": "hello"} + m := testMetaBackend(t, nil) + opts := &BackendOpts{ + ConfigExtra: extras, + Init: true, + } + + backendCfg, err := m.backendConfig(opts) + if err != nil { + t.Fatal(err) + } + + // init the backend + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s := testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + if s.Backend.Hash != backendCfg.Hash { + t.Fatal("mismatched state and config backend hashes") + } + if s.Backend.Rehash() == s.Backend.Hash { + t.Fatal("saved hash should not match actual hash") + } + if s.Backend.Rehash() != backendCfg.Rehash() { + t.Fatal("mismatched state and config re-hashes") + } + + // init the backend again with the same options + m = testMetaBackend(t, nil) + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s = testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + if s.Backend.Hash != backendCfg.Hash { + t.Fatal("mismatched state and config backend hashes") + } +} + +// move options from config to -backend-config +func TestMetaBackend_configToExtra(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // init the backend + m := testMetaBackend(t, nil) + _, err := m.Backend(&BackendOpts{ + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + // Check the state + s := testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + backendHash := s.Backend.Hash + + // init again but remove the path option from the config + cfg := "terraform {\n backend \"local\" {}\n}\n" + if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil { + t.Fatal(err) + } + + // init the backend again with the options + extras := map[string]interface{}{"path": "hello"} + m = testMetaBackend(t, nil) + m.forceInitCopy = true + _, err = m.Backend(&BackendOpts{ + ConfigExtra: extras, + Init: true, + }) + if err != nil { + t.Fatalf("bad: %s", err) + } + + s = testStateRead(t, filepath.Join(DefaultDataDir, backendlocal.DefaultStateFilename)) + + if s.Backend.Hash == backendHash { + t.Fatal("state.Backend.Hash was not updated") + } +} + func testMetaBackend(t *testing.T, args []string) *Meta { var m Meta m.Ui = new(cli.MockUi) From c55a5082f5117709d7fddf090fdd80ebc4e9f185 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 18:01:03 -0400 Subject: [PATCH 043/101] delegate BackendState.Rehash to config.Backend --- terraform/state.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/terraform/state.go b/terraform/state.go index d5adefca10..905ec3dcbd 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/go-version" "github.com/hashicorp/terraform/config" "github.com/mitchellh/copystructure" - "github.com/mitchellh/hashstructure" "github.com/satori/go.uuid" ) @@ -813,19 +812,14 @@ func (s *BackendState) Rehash() uint64 { return 0 } - // Use hashstructure to hash only our type with the config. - code, err := hashstructure.Hash(map[string]interface{}{ - "type": s.Type, - "config": s.Config, - }, nil) - - // This should never happen since we have just some basic primitives - // so panic if there is an error. - if err != nil { - panic(err) + cfg := config.Backend{ + Type: s.Type, + RawConfig: &config.RawConfig{ + Raw: s.Config, + }, } - return code + return cfg.Rehash() } // RemoteState is used to track the information about a remote From 50023e9a60aac7324f9e28fc1aa79a2415a4ca78 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 29 Mar 2017 16:45:25 -0400 Subject: [PATCH 044/101] honor `input=false` in state migration return an error when confirming a copy if -input=false --- command/init_test.go | 26 ++++++++++++++++++++++++++ command/meta.go | 4 ++++ 2 files changed, 30 insertions(+) diff --git a/command/init_test.go b/command/init_test.go index a0a8c32a03..eea5797c16 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -508,6 +508,32 @@ func TestInit_backendReinitConfigToExtra(t *testing.T) { } } +// make sure inputFalse stops execution on migrate +func TestInit_inputFalse(t *testing.T) { + td := tempDir(t) + copy.CopyDir(testFixturePath("init-backend"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{"-input=false", "-backend-config=path=foo"} + if code := c.Run([]string{"-input=false"}); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter) + } + + args = []string{"-input=false", "-backend-config=path=bar"} + if code := c.Run(args); code == 0 { + t.Fatal("init should have failed", ui.OutputWriter) + } +} + /* func TestInit_remoteState(t *testing.T) { tmp, cwd := testCwd(t) diff --git a/command/meta.go b/command/meta.go index 0dd4c78843..daf949a295 100644 --- a/command/meta.go +++ b/command/meta.go @@ -3,6 +3,7 @@ package command import ( "bufio" "bytes" + "errors" "flag" "fmt" "io" @@ -341,6 +342,9 @@ func (m *Meta) uiHook() *UiHook { // confirm asks a yes/no confirmation. func (m *Meta) confirm(opts *terraform.InputOpts) (bool, error) { + if !m.input { + return false, errors.New("input disabled") + } for { v, err := m.UIInput().Input(opts) if err != nil { From 11fa03cfb664e9b8667bd61aa80bfc40af96f3d6 Mon Sep 17 00:00:00 2001 From: Brian Hahn Date: Wed, 29 Mar 2017 23:03:08 -0700 Subject: [PATCH 045/101] fix docs typo (#13183) --- website/source/docs/backends/legacy-0-8.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/backends/legacy-0-8.html.md b/website/source/docs/backends/legacy-0-8.html.md index c3197d23c8..a5feee4055 100644 --- a/website/source/docs/backends/legacy-0-8.html.md +++ b/website/source/docs/backends/legacy-0-8.html.md @@ -124,7 +124,7 @@ You should be able to very easily migrate `terraform remote config` scripting to the new `terraform init` command. The new `terraform init` command takes a `-backend-config` flag which is -eitheran HCL file or a string in the format of `key=value`. This configuration +either an HCL file or a string in the format of `key=value`. This configuration is merged with the backend configuration in your Terraform files. This lets you keep secrets out of your actual configuration. We call this "partial configuration" and you can learn more in the From 1dca12201ab744259b49c78aa9caa02c0e898822 Mon Sep 17 00:00:00 2001 From: Ian Morgan Date: Thu, 30 Mar 2017 01:12:15 -0700 Subject: [PATCH 046/101] fix error message in route53 data source (#13174) --- builtin/providers/aws/data_source_aws_route53_zone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/data_source_aws_route53_zone.go b/builtin/providers/aws/data_source_aws_route53_zone.go index a4df2c7547..b3de4eed49 100644 --- a/builtin/providers/aws/data_source_aws_route53_zone.go +++ b/builtin/providers/aws/data_source_aws_route53_zone.go @@ -136,7 +136,7 @@ func dataSourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) erro if matchingTags && matchingVPC { if hostedZoneFound != nil { - return fmt.Errorf("multplie Route53Zone found please use vpc_id option to filter") + return fmt.Errorf("multiple Route53Zone found please use vpc_id option to filter") } else { hostedZoneFound = hostedZone } From c2b657a03977287018aad1ccfe8b2b4b6ee6f9a8 Mon Sep 17 00:00:00 2001 From: Marc Rooding Date: Thu, 30 Mar 2017 10:24:40 +0200 Subject: [PATCH 047/101] kubernetes: Add secret resource (#12960) --- builtin/providers/kubernetes/provider.go | 1 + .../kubernetes/resource_kubernetes_secret.go | 159 +++++++++ .../resource_kubernetes_secret_test.go | 320 ++++++++++++++++++ builtin/providers/kubernetes/structures.go | 18 + .../kubernetes/r/secret.html.markdown | 69 ++++ website/source/layouts/kubernetes.erb | 3 + 6 files changed, 570 insertions(+) create mode 100644 builtin/providers/kubernetes/resource_kubernetes_secret.go create mode 100644 builtin/providers/kubernetes/resource_kubernetes_secret_test.go create mode 100644 website/source/docs/providers/kubernetes/r/secret.html.markdown diff --git a/builtin/providers/kubernetes/provider.go b/builtin/providers/kubernetes/provider.go index 9d0d23cc30..861519c6a6 100644 --- a/builtin/providers/kubernetes/provider.go +++ b/builtin/providers/kubernetes/provider.go @@ -83,6 +83,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "kubernetes_config_map": resourceKubernetesConfigMap(), "kubernetes_namespace": resourceKubernetesNamespace(), + "kubernetes_secret": resourceKubernetesSecret(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/kubernetes/resource_kubernetes_secret.go b/builtin/providers/kubernetes/resource_kubernetes_secret.go new file mode 100644 index 0000000000..0fe9a71ba2 --- /dev/null +++ b/builtin/providers/kubernetes/resource_kubernetes_secret.go @@ -0,0 +1,159 @@ +package kubernetes + +import ( + "log" + + "fmt" + "github.com/hashicorp/terraform/helper/schema" + pkgApi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/errors" + api "k8s.io/kubernetes/pkg/api/v1" + kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5" +) + +func resourceKubernetesSecret() *schema.Resource { + return &schema.Resource{ + Create: resourceKubernetesSecretCreate, + Read: resourceKubernetesSecretRead, + Exists: resourceKubernetesSecretExists, + Update: resourceKubernetesSecretUpdate, + Delete: resourceKubernetesSecretDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "metadata": namespacedMetadataSchema("secret", true), + "data": { + Type: schema.TypeMap, + Description: "A map of the secret data.", + Optional: true, + Sensitive: true, + }, + "type": { + Type: schema.TypeString, + Description: "Type of secret", + Default: "Opaque", + Optional: true, + ForceNew: true, + }, + }, + } +} + +func resourceKubernetesSecretCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*kubernetes.Clientset) + + metadata := expandMetadata(d.Get("metadata").([]interface{})) + secret := api.Secret{ + ObjectMeta: metadata, + StringData: expandStringMap(d.Get("data").(map[string]interface{})), + } + + if v, ok := d.GetOk("type"); ok { + secret.Type = api.SecretType(v.(string)) + } + + log.Printf("[INFO] Creating new secret: %#v", secret) + out, err := conn.CoreV1().Secrets(metadata.Namespace).Create(&secret) + if err != nil { + return err + } + + log.Printf("[INFO] Submitting new secret: %#v", out) + d.SetId(buildId(out.ObjectMeta)) + + return resourceKubernetesSecretRead(d, meta) +} + +func resourceKubernetesSecretRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*kubernetes.Clientset) + + namespace, name := idParts(d.Id()) + + log.Printf("[INFO] Reading secret %s", name) + secret, err := conn.CoreV1().Secrets(namespace).Get(name) + if err != nil { + return err + } + + log.Printf("[INFO] Received secret: %#v", secret) + err = d.Set("metadata", flattenMetadata(secret.ObjectMeta)) + if err != nil { + return err + } + + d.Set("data", byteMapToStringMap(secret.Data)) + d.Set("type", secret.Type) + + return nil +} + +func resourceKubernetesSecretUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*kubernetes.Clientset) + + namespace, name := idParts(d.Id()) + + ops := patchMetadata("metadata.0.", "/metadata/", d) + if d.HasChange("data") { + oldV, newV := d.GetChange("data") + + oldV = base64EncodeStringMap(oldV.(map[string]interface{})) + newV = base64EncodeStringMap(newV.(map[string]interface{})) + + diffOps := diffStringMap("/data/", oldV.(map[string]interface{}), newV.(map[string]interface{})) + + ops = append(ops, diffOps...) + } + + data, err := ops.MarshalJSON() + if err != nil { + return fmt.Errorf("Failed to marshal update operations: %s", err) + } + + log.Printf("[INFO] Updating secret %q: %v", name, data) + out, err := conn.CoreV1().Secrets(namespace).Patch(name, pkgApi.JSONPatchType, data) + if err != nil { + return fmt.Errorf("Failed to update secret: %s", err) + } + + log.Printf("[INFO] Submitting updated secret: %#v", out) + d.SetId(buildId(out.ObjectMeta)) + + return resourceKubernetesSecretRead(d, meta) +} + +func resourceKubernetesSecretDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*kubernetes.Clientset) + + namespace, name := idParts(d.Id()) + + log.Printf("[INFO] Deleting secret: %q", name) + err := conn.CoreV1().Secrets(namespace).Delete(name, &api.DeleteOptions{}) + if err != nil { + return err + } + + log.Printf("[INFO] Secret %s deleted", name) + + d.SetId("") + + return nil +} + +func resourceKubernetesSecretExists(d *schema.ResourceData, meta interface{}) (bool, error) { + conn := meta.(*kubernetes.Clientset) + + namespace, name := idParts(d.Id()) + + log.Printf("[INFO] Checking secret %s", name) + _, err := conn.CoreV1().Secrets(namespace).Get(name) + if err != nil { + if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 { + return false, nil + } + log.Printf("[DEBUG] Received error: %#v", err) + } + + return true, err +} diff --git a/builtin/providers/kubernetes/resource_kubernetes_secret_test.go b/builtin/providers/kubernetes/resource_kubernetes_secret_test.go new file mode 100644 index 0000000000..0e9ef31234 --- /dev/null +++ b/builtin/providers/kubernetes/resource_kubernetes_secret_test.go @@ -0,0 +1,320 @@ +package kubernetes + +import ( + "fmt" + "reflect" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + api "k8s.io/kubernetes/pkg/api/v1" + kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5" +) + +func TestAccKubernetesSecret_basic(t *testing.T) { + var conf api.Secret + name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "kubernetes_secret.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckKubernetesSecretDestroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesSecretConfig_basic(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "2"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationOne", "one"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationTwo", "two"), + testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "TestAnnotationTwo": "two"}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "3"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelOne", "one"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelTwo", "two"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelThree", "three"), + testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelTwo": "two", "TestLabelThree": "three"}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "2"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.one", "first"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.two", "second"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "Opaque"), + testAccCheckSecretData(&conf, map[string]string{"one": "first", "two": "second"}), + ), + }, + { + Config: testAccKubernetesSecretConfig_modified(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "2"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.TestAnnotationOne", "one"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.Different", "1234"), + testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{"TestAnnotationOne": "one", "Different": "1234"}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "2"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelOne", "one"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.TestLabelThree", "three"), + testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{"TestLabelOne": "one", "TestLabelThree": "three"}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "3"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.one", "first"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.two", "second"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.nine", "ninth"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "Opaque"), + testAccCheckSecretData(&conf, map[string]string{"one": "first", "two": "second", "nine": "ninth"}), + ), + }, + { + Config: testAccKubernetesSecretConfig_noData(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"), + testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"), + testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "0"), + testAccCheckSecretData(&conf, map[string]string{}), + ), + }, + { + Config: testAccKubernetesSecretConfig_typeSpecified(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"), + testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"), + testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.name", name), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.%", "2"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.username", "admin"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "data.password", "password"), + resource.TestCheckResourceAttr("kubernetes_secret.test", "type", "kubernetes.io/basic-auth"), + testAccCheckSecretData(&conf, map[string]string{"username": "admin", "password": "password"}), + ), + }, + }, + }) +} + +func TestAccKubernetesSecret_importBasic(t *testing.T) { + resourceName := "kubernetes_secret.test" + name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckKubernetesSecretDestroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesSecretConfig_basic(name), + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccKubernetesSecret_generatedName(t *testing.T) { + var conf api.Secret + prefix := "tf-acc-test-gen-" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "kubernetes_secret.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckKubernetesSecretDestroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesSecretConfig_generatedName(prefix), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesSecretExists("kubernetes_secret.test", &conf), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.annotations.%", "0"), + testAccCheckMetaAnnotations(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.labels.%", "0"), + testAccCheckMetaLabels(&conf.ObjectMeta, map[string]string{}), + resource.TestCheckResourceAttr("kubernetes_secret.test", "metadata.0.generate_name", prefix), + resource.TestMatchResourceAttr("kubernetes_secret.test", "metadata.0.name", regexp.MustCompile("^"+prefix)), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.generation"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.self_link"), + resource.TestCheckResourceAttrSet("kubernetes_secret.test", "metadata.0.uid"), + ), + }, + }, + }) +} + +func TestAccKubernetesSecret_importGeneratedName(t *testing.T) { + resourceName := "kubernetes_secret.test" + prefix := "tf-acc-test-gen-import-" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckKubernetesSecretDestroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesSecretConfig_generatedName(prefix), + }, + + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckSecretData(m *api.Secret, expected map[string]string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(expected) == 0 && len(m.Data) == 0 { + return nil + } + if !reflect.DeepEqual(byteMapToStringMap(m.Data), expected) { + return fmt.Errorf("%s data don't match.\nExpected: %q\nGiven: %q", + m.Name, expected, m.Data) + } + return nil + } +} + +func testAccCheckKubernetesSecretDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*kubernetes.Clientset) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "kubernetes_secret" { + continue + } + namespace, name := idParts(rs.Primary.ID) + resp, err := conn.CoreV1().Secrets(namespace).Get(name) + if err == nil { + if resp.Name == rs.Primary.ID { + return fmt.Errorf("Secret still exists: %s", rs.Primary.ID) + } + } + } + + return nil +} + +func testAccCheckKubernetesSecretExists(n string, obj *api.Secret) 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().(*kubernetes.Clientset) + namespace, name := idParts(rs.Primary.ID) + out, err := conn.CoreV1().Secrets(namespace).Get(name) + if err != nil { + return err + } + + *obj = *out + return nil + } +} + +func testAccKubernetesSecretConfig_basic(name string) string { + return fmt.Sprintf(` +resource "kubernetes_secret" "test" { + metadata { + annotations { + TestAnnotationOne = "one" + TestAnnotationTwo = "two" + } + labels { + TestLabelOne = "one" + TestLabelTwo = "two" + TestLabelThree = "three" + } + name = "%s" + } + data { + one = "first" + two = "second" + } +}`, name) +} + +func testAccKubernetesSecretConfig_modified(name string) string { + return fmt.Sprintf(` +resource "kubernetes_secret" "test" { + metadata { + annotations { + TestAnnotationOne = "one" + Different = "1234" + } + labels { + TestLabelOne = "one" + TestLabelThree = "three" + } + name = "%s" + } + data { + one = "first" + two = "second" + nine = "ninth" + } +}`, name) +} + +func testAccKubernetesSecretConfig_noData(name string) string { + return fmt.Sprintf(` +resource "kubernetes_secret" "test" { + metadata { + name = "%s" + } +}`, name) +} + +func testAccKubernetesSecretConfig_typeSpecified(name string) string { + return fmt.Sprintf(` +resource "kubernetes_secret" "test" { + metadata { + name = "%s" + } + data { + username = "admin" + password = "password" + } + type = "kubernetes.io/basic-auth" +}`, name) +} + +func testAccKubernetesSecretConfig_generatedName(prefix string) string { + return fmt.Sprintf(` +resource "kubernetes_secret" "test" { + metadata { + generate_name = "%s" + } + data { + one = "first" + two = "second" + } +}`, prefix) +} diff --git a/builtin/providers/kubernetes/structures.go b/builtin/providers/kubernetes/structures.go index 58bc49030c..878890d565 100644 --- a/builtin/providers/kubernetes/structures.go +++ b/builtin/providers/kubernetes/structures.go @@ -5,6 +5,7 @@ import ( "net/url" "strings" + "encoding/base64" "github.com/hashicorp/terraform/helper/schema" api "k8s.io/kubernetes/pkg/api/v1" ) @@ -99,3 +100,20 @@ func isInternalAnnotationKey(annotationKey string) bool { return false } + +func byteMapToStringMap(m map[string][]byte) map[string]string { + result := make(map[string]string) + for k, v := range m { + result[k] = string(v) + } + return result +} + +func base64EncodeStringMap(m map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) + for k, v := range m { + value := v.(string) + result[k] = (base64.StdEncoding.EncodeToString([]byte(value))) + } + return result +} diff --git a/website/source/docs/providers/kubernetes/r/secret.html.markdown b/website/source/docs/providers/kubernetes/r/secret.html.markdown new file mode 100644 index 0000000000..856b04d897 --- /dev/null +++ b/website/source/docs/providers/kubernetes/r/secret.html.markdown @@ -0,0 +1,69 @@ +--- +layout: "kubernetes" +page_title: "Kubernetes: kubernetes_secret" +sidebar_current: "docs-kubernetes-resource-secret" +description: |- + The resource provides mechanisms to inject containers with sensitive information while keeping containers agnostic of Kubernetes. +--- + +# kubernetes_secret + +The resource provides mechanisms to inject containers with sensitive information, such as passwords, while keeping containers agnostic of Kubernetes. +Secrets can be used to store sensitive information either as individual properties or coarse-grained entries like entire files or JSON blobs. +The resource will by default create a secret which is available to any pod in the specified (or default) namespace. + +~> Read more about security properties and risks involved with using Kubernetes secrets: https://kubernetes.io/docs/user-guide/secrets/#security-properties + +~> **Note:** All arguments including the secret data will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + +## Example Usage + +``` +resource "kubernetes_secret" "example" { + metadata { + name = "basic-auth" + } + + data { + username = "admin" + password = "P4ssw0rd" + } + + type = "kubernetes.io/basic-auth" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `data` - (Optional) A map of the secret data. +* `metadata` - (Required) Standard secret's metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#metadata +* `type` - (Optional) The secret type. Defaults to `Opaque`. More info: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/secrets.md#proposed-design + +## Nested Blocks + +### `metadata` + +#### Arguments + +* `annotations` - (Optional) An unstructured key value map stored with the secret that may be used to store arbitrary metadata. More info: http://kubernetes.io/docs/user-guide/annotations +* `generate_name` - (Optional) Prefix, used by the server, to generate a unique name ONLY IF the `name` field has not been provided. This value will also be combined with a unique suffix. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#idempotency +* `labels` - (Optional) Map of string keys and values that can be used to organize and categorize (scope and select) the secret. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels +* `name` - (Optional) Name of the secret, must be unique. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names +* `namespace` - (Optional) Namespace defines the space within which name of the secret must be unique. + +#### Attributes + +* `generation` - A sequence number representing a specific generation of the desired state. +* `resource_version` - An opaque value that represents the internal version of this secret that can be used by clients to determine when secret has changed. Read more: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#concurrency-control-and-consistency +* `self_link` - A URL representing this secret. +* `uid` - The unique in time and space value for this secret. More info: http://kubernetes.io/docs/user-guide/identifiers#uids + +## Import + +Secret can be imported using its name, e.g. + +``` +$ terraform import kubernetes_secret.example my-secret +``` diff --git a/website/source/layouts/kubernetes.erb b/website/source/layouts/kubernetes.erb index 147bccbf4d..4f80efec79 100644 --- a/website/source/layouts/kubernetes.erb +++ b/website/source/layouts/kubernetes.erb @@ -19,6 +19,9 @@ > kubernetes_namespace + > + kubernetes_secret + From b12e7782c98453cc6c2fbdac369a83521bc952b1 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 09:26:50 +0100 Subject: [PATCH 048/101] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fda0ede40..2260e704b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.9.3 (unreleased) +FEATURES: + + * **New Resource:** `kubernetes_secret` [GH-12960] + IMPROVEMENTS: * config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080] From 204789f07c129026009a59d6ad32413175b3b441 Mon Sep 17 00:00:00 2001 From: Axel FAUVEL Date: Thu, 30 Mar 2017 10:34:55 +0200 Subject: [PATCH 049/101] fix cloudstack_disk documentation --- .../docs/providers/cloudstack/r/disk.html.markdown | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/source/docs/providers/cloudstack/r/disk.html.markdown b/website/source/docs/providers/cloudstack/r/disk.html.markdown index f9d5af30f4..5232394ddc 100644 --- a/website/source/docs/providers/cloudstack/r/disk.html.markdown +++ b/website/source/docs/providers/cloudstack/r/disk.html.markdown @@ -15,12 +15,12 @@ a virtual machine if the optional parameters are configured. ``` resource "cloudstack_disk" "default" { - name = "test-disk" - attach = "true" - disk_offering = "custom" - size = 50 - virtual_machine = "server-1" - zone = "zone-1" + name = "test-disk" + attach = "true" + disk_offering = "custom" + size = 50 + virtual_machine_id = "server-1" + zone = "zone-1" } ``` From d24dc532e54f7a2198623ac3f1ef9eae152815fb Mon Sep 17 00:00:00 2001 From: stack72 Date: Thu, 30 Mar 2017 15:54:01 +0300 Subject: [PATCH 050/101] provider/aws: Documentation changes on ALB to remove ELB refs Fixes: #13179 --- website/source/docs/providers/aws/r/alb.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/alb.html.markdown b/website/source/docs/providers/aws/r/alb.html.markdown index 18513be1cc..d91706ecaa 100644 --- a/website/source/docs/providers/aws/r/alb.html.markdown +++ b/website/source/docs/providers/aws/r/alb.html.markdown @@ -46,9 +46,9 @@ must contain only alphanumeric characters or hyphens, and must not begin or end Terraform will autogenerate a name beginning with `tf-lb`. * `name_prefix` - (Optional) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `internal` - (Optional) If true, the ALB will be internal. -* `security_groups` - (Optional) A list of security group IDs to assign to the ELB. +* `security_groups` - (Optional) A list of security group IDs to assign to the ALB. * `access_logs` - (Optional) An Access Logs block. Access Logs documented below. -* `subnets` - (Required) A list of subnet IDs to attach to the ELB. +* `subnets` - (Required) A list of subnet IDs to attach to the ALB. * `idle_timeout` - (Optional) The time in seconds that the connection is allowed to be idle. Default: 60. * `enable_deletion_protection` - (Optional) If true, deletion of the load balancer will be disabled via the AWS API. This will prevent Terraform from deleting the load balancer. Defaults to `false`. From 9ed8bb2498186008101933af5ecab0627f0046d1 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 16:20:42 +0300 Subject: [PATCH 051/101] provider/aws: Support the ability to enable / disable ipv6 support in (#12527) VPC ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSVpc_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/28 15:49:20 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSVpc_ -timeout 120m === RUN TestAccAWSVpc_importBasic --- PASS: TestAccAWSVpc_importBasic (102.01s) === RUN TestAccAWSVpc_basic --- PASS: TestAccAWSVpc_basic (63.75s) === RUN TestAccAWSVpc_enableIpv6 --- PASS: TestAccAWSVpc_enableIpv6 (231.41s) === RUN TestAccAWSVpc_dedicatedTenancy --- PASS: TestAccAWSVpc_dedicatedTenancy (66.65s) === RUN TestAccAWSVpc_tags --- PASS: TestAccAWSVpc_tags (130.26s) === RUN TestAccAWSVpc_update --- PASS: TestAccAWSVpc_update (120.21s) === RUN TestAccAWSVpc_bothDnsOptionsSet --- PASS: TestAccAWSVpc_bothDnsOptionsSet (50.10s) === RUN TestAccAWSVpc_DisabledDnsSupport --- PASS: TestAccAWSVpc_DisabledDnsSupport (67.47s) === RUN TestAccAWSVpc_classiclinkOptionSet --- PASS: TestAccAWSVpc_classiclinkOptionSet (64.57s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 896.464s ``` --- builtin/providers/aws/resource_aws_vpc.go | 100 +++++++++++++++++- .../providers/aws/resource_aws_vpc_test.go | 36 ++++++- 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_vpc.go b/builtin/providers/aws/resource_aws_vpc.go index 6807706b6c..6a8edca4b8 100644 --- a/builtin/providers/aws/resource_aws_vpc.go +++ b/builtin/providers/aws/resource_aws_vpc.go @@ -60,7 +60,6 @@ func resourceAwsVpc() *schema.Resource { "assign_generated_ipv6_cidr_block": { Type: schema.TypeBool, - ForceNew: true, Optional: true, Default: false, }, @@ -178,7 +177,7 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { d.Set("tags", tagsToMap(vpc.Tags)) for _, a := range vpc.Ipv6CidrBlockAssociationSet { - if *a.Ipv6CidrBlockState.State == "associated" { + if *a.Ipv6CidrBlockState.State == "associated" { //we can only ever have 1 IPv6 block associated at once d.Set("assign_generated_ipv6_cidr_block", true) d.Set("ipv6_association_id", a.AssociationId) d.Set("ipv6_cidr_block", a.Ipv6CidrBlock) @@ -344,6 +343,68 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { d.SetPartial("enable_classiclink") } + if d.HasChange("assign_generated_ipv6_cidr_block") && !d.IsNewResource() { + toAssign := d.Get("assign_generated_ipv6_cidr_block").(bool) + + log.Printf("[INFO] Modifying assign_generated_ipv6_cidr_block to %#v", toAssign) + + if toAssign { + modifyOpts := &ec2.AssociateVpcCidrBlockInput{ + VpcId: &vpcid, + AmazonProvidedIpv6CidrBlock: aws.Bool(toAssign), + } + log.Printf("[INFO] Enabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", + d.Id(), modifyOpts) + resp, err := conn.AssociateVpcCidrBlock(modifyOpts) + if err != nil { + return err + } + + // Wait for the CIDR to become available + log.Printf( + "[DEBUG] Waiting for IPv6 CIDR (%s) to become associated", + d.Id()) + stateConf := &resource.StateChangeConf{ + Pending: []string{"associating", "disassociated"}, + Target: []string{"associated"}, + Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), *resp.Ipv6CidrBlockAssociation.AssociationId), + Timeout: 1 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for IPv6 CIDR (%s) to become associated: %s", + d.Id(), err) + } + } else { + modifyOpts := &ec2.DisassociateVpcCidrBlockInput{ + AssociationId: aws.String(d.Get("ipv6_association_id").(string)), + } + log.Printf("[INFO] Disabling assign_generated_ipv6_cidr_block vpc attribute for %s: %#v", + d.Id(), modifyOpts) + if _, err := conn.DisassociateVpcCidrBlock(modifyOpts); err != nil { + return err + } + + // Wait for the CIDR to become available + log.Printf( + "[DEBUG] Waiting for IPv6 CIDR (%s) to become disassociated", + d.Id()) + stateConf := &resource.StateChangeConf{ + Pending: []string{"disassociating", "associated"}, + Target: []string{"disassociated"}, + Refresh: Ipv6CidrStateRefreshFunc(conn, d.Id(), d.Get("ipv6_association_id").(string)), + Timeout: 1 * time.Minute, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf( + "Error waiting for IPv6 CIDR (%s) to become disassociated: %s", + d.Id(), err) + } + } + + d.SetPartial("assign_generated_ipv6_cidr_block") + } + if err := setTags(conn, d); err != nil { return err } else { @@ -412,6 +473,41 @@ func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { } } +func Ipv6CidrStateRefreshFunc(conn *ec2.EC2, id string, associationId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + describeVpcOpts := &ec2.DescribeVpcsInput{ + VpcIds: []*string{aws.String(id)}, + } + resp, err := conn.DescribeVpcs(describeVpcOpts) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { + resp = nil + } else { + log.Printf("Error on VPCStateRefresh: %s", err) + return nil, "", err + } + } + + if resp == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our instance yet. Return an empty state. + return nil, "", nil + } + + if resp.Vpcs[0].Ipv6CidrBlockAssociationSet == nil { + return nil, "", nil + } + + for _, association := range resp.Vpcs[0].Ipv6CidrBlockAssociationSet { + if *association.AssociationId == associationId { + return association, *association.Ipv6CidrBlockState.State, nil + } + } + + return nil, "", nil + } +} + func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { filter1 := &ec2.Filter{ Name: aws.String("default"), diff --git a/builtin/providers/aws/resource_aws_vpc_test.go b/builtin/providers/aws/resource_aws_vpc_test.go index 44f672268f..ca68bdfe8c 100644 --- a/builtin/providers/aws/resource_aws_vpc_test.go +++ b/builtin/providers/aws/resource_aws_vpc_test.go @@ -46,7 +46,7 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccVpcConfigIpv6Enabled, - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckVpcExists("aws_vpc.foo", &vpc), testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), resource.TestCheckResourceAttr( @@ -55,6 +55,34 @@ func TestAccAWSVpc_enableIpv6(t *testing.T) { "aws_vpc.foo", "ipv6_association_id"), resource.TestCheckResourceAttrSet( "aws_vpc.foo", "ipv6_cidr_block"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"), + ), + }, + { + Config: testAccVpcConfigIpv6Disabled, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckVpcExists("aws_vpc.foo", &vpc), + testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "false"), + ), + }, + { + Config: testAccVpcConfigIpv6Enabled, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckVpcExists("aws_vpc.foo", &vpc), + testAccCheckVpcCidr(&vpc, "10.1.0.0/16"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "cidr_block", "10.1.0.0/16"), + resource.TestCheckResourceAttrSet( + "aws_vpc.foo", "ipv6_association_id"), + resource.TestCheckResourceAttrSet( + "aws_vpc.foo", "ipv6_cidr_block"), + resource.TestCheckResourceAttr( + "aws_vpc.foo", "assign_generated_ipv6_cidr_block", "true"), ), }, }, @@ -283,6 +311,12 @@ resource "aws_vpc" "foo" { } ` +const testAccVpcConfigIpv6Disabled = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} +` + const testAccVpcConfigUpdate = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" From 5ba7aa82966efeba6d3a768f6086a3e448668594 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 16:22:30 +0300 Subject: [PATCH 052/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2260e704b6..e151c9aa63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ IMPROVEMENTS: * provider/aws: `aws_kinesis_firehose_delivery_stream` `password` field marked as sensitive [GH-13147] * provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147] * provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147] + * provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] BUG FIXES: From 7d8a6f853311bcdf33fd164239aa291fd2554913 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 14:59:28 +0100 Subject: [PATCH 053/101] provider/aws: Add support for aws_lightsail_static_ip (#13175) --- builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_lightsail_static_ip.go | 98 +++++++++++++ .../resource_aws_lightsail_static_ip_test.go | 138 ++++++++++++++++++ .../aws/r/lightsail_static_ip.html.markdown | 35 +++++ website/source/layouts/aws.erb | 4 + 5 files changed, 276 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_lightsail_static_ip.go create mode 100644 builtin/providers/aws/resource_aws_lightsail_static_ip_test.go create mode 100644 website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 744eb21ad9..0a9de8accf 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -337,6 +337,7 @@ func Provider() terraform.ResourceProvider { "aws_lightsail_domain": resourceAwsLightsailDomain(), "aws_lightsail_instance": resourceAwsLightsailInstance(), "aws_lightsail_key_pair": resourceAwsLightsailKeyPair(), + "aws_lightsail_static_ip": resourceAwsLightsailStaticIp(), "aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(), "aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(), "aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(), diff --git a/builtin/providers/aws/resource_aws_lightsail_static_ip.go b/builtin/providers/aws/resource_aws_lightsail_static_ip.go new file mode 100644 index 0000000000..1f593ad40e --- /dev/null +++ b/builtin/providers/aws/resource_aws_lightsail_static_ip.go @@ -0,0 +1,98 @@ +package aws + +import ( + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsLightsailStaticIp() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLightsailStaticIpCreate, + Read: resourceAwsLightsailStaticIpRead, + Delete: resourceAwsLightsailStaticIpDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "ip_address": { + Type: schema.TypeString, + Computed: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "support_code": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsLightsailStaticIpCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lightsailconn + + name := d.Get("name").(string) + log.Printf("[INFO] Allocating Lightsail Static IP: %q", name) + out, err := conn.AllocateStaticIp(&lightsail.AllocateStaticIpInput{ + StaticIpName: aws.String(name), + }) + if err != nil { + return err + } + log.Printf("[INFO] Lightsail Static IP allocated: %s", *out) + + d.SetId(name) + + return resourceAwsLightsailStaticIpRead(d, meta) +} + +func resourceAwsLightsailStaticIpRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lightsailconn + + name := d.Get("name").(string) + log.Printf("[INFO] Reading Lightsail Static IP: %q", name) + out, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{ + StaticIpName: aws.String(name), + }) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "NotFoundException" { + log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + } + return err + } + log.Printf("[INFO] Received Lightsail Static IP: %s", *out) + + d.Set("arn", out.StaticIp.Arn) + d.Set("ip_address", out.StaticIp.IpAddress) + d.Set("support_code", out.StaticIp.SupportCode) + + return nil +} + +func resourceAwsLightsailStaticIpDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lightsailconn + + name := d.Get("name").(string) + log.Printf("[INFO] Deleting Lightsail Static IP: %q", name) + out, err := conn.ReleaseStaticIp(&lightsail.ReleaseStaticIpInput{ + StaticIpName: aws.String(name), + }) + if err != nil { + return err + } + log.Printf("[INFO] Deleted Lightsail Static IP: %s", *out) + return nil +} diff --git a/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go b/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go new file mode 100644 index 0000000000..275a29f202 --- /dev/null +++ b/builtin/providers/aws/resource_aws_lightsail_static_ip_test.go @@ -0,0 +1,138 @@ +package aws + +import ( + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSLightsailStaticIp_basic(t *testing.T) { + var staticIp lightsail.StaticIp + staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLightsailStaticIpDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLightsailStaticIpConfig_basic(staticIpName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSLightsailStaticIpExists("aws_lightsail_static_ip.test", &staticIp), + ), + }, + }, + }) +} + +func TestAccAWSLightsailStaticIp_disappears(t *testing.T) { + var staticIp lightsail.StaticIp + staticIpName := fmt.Sprintf("tf-test-lightsail-%s", acctest.RandString(5)) + + staticIpDestroy := func(*terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lightsailconn + _, err := conn.ReleaseStaticIp(&lightsail.ReleaseStaticIpInput{ + StaticIpName: aws.String(staticIpName), + }) + + if err != nil { + return fmt.Errorf("Error deleting Lightsail Static IP in disapear test") + } + + return nil + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLightsailStaticIpDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLightsailStaticIpConfig_basic(staticIpName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSLightsailStaticIpExists("aws_lightsail_static_ip.test", &staticIp), + staticIpDestroy, + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSLightsailStaticIpExists(n string, staticIp *lightsail.StaticIp) 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 errors.New("No Lightsail Static IP ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).lightsailconn + + resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{ + StaticIpName: aws.String(rs.Primary.ID), + }) + + if err != nil { + return err + } + + if resp == nil || resp.StaticIp == nil { + return fmt.Errorf("Static IP (%s) not found", rs.Primary.ID) + } + *staticIp = *resp.StaticIp + return nil + } +} + +func testAccCheckAWSLightsailStaticIpDestroy(s *terraform.State) error { + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lightsail_static_ip" { + continue + } + + conn := testAccProvider.Meta().(*AWSClient).lightsailconn + + resp, err := conn.GetStaticIp(&lightsail.GetStaticIpInput{ + StaticIpName: aws.String(rs.Primary.ID), + }) + + if err == nil { + if resp.StaticIp != nil { + return fmt.Errorf("Lightsail Static IP %q still exists", rs.Primary.ID) + } + } + + // Verify the error + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "NotFoundException" { + return nil + } + } + return err + } + + return nil +} + +func testAccAWSLightsailStaticIpConfig_basic(staticIpName string) string { + return fmt.Sprintf(` +provider "aws" { + region = "us-east-1" +} +resource "aws_lightsail_static_ip" "test" { + name = "%s" +} +`, staticIpName) +} diff --git a/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown b/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown new file mode 100644 index 0000000000..b3617a432a --- /dev/null +++ b/website/source/docs/providers/aws/r/lightsail_static_ip.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "aws" +page_title: "AWS: aws_lightsail_static_ip" +sidebar_current: "docs-aws-resource-lightsail-static-ip" +description: |- + Provides an Lightsail Static IP +--- + +# aws\_lightsail\_static\_ip + +Allocates a static IP address. + +~> **Note:** Lightsail is currently only supported in `us-east-1` region. + +## Example Usage + +``` +resource "aws_lightsail_static_ip" "test" { + name = "example" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name for the allocated static IP + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `arn` - The ARN of the Lightsail static IP +* `ip_address` - The allocated static IP address +* `support_code` - The support code. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 73656110a0..312a3535d5 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -881,6 +881,10 @@ aws_lightsail_key_pair + > + aws_lightsail_static_ip + + From 7bf9534b2ac961a2cc7b2f06922cb32c8432af86 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 30 Mar 2017 15:01:51 +0100 Subject: [PATCH 054/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e151c9aa63..bcc42c4f49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ FEATURES: + * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `kubernetes_secret` [GH-12960] IMPROVEMENTS: From 18513bcb8d1b011ef58e3c4ca144b448dd4b7257 Mon Sep 17 00:00:00 2001 From: lmorfitt Date: Thu, 30 Mar 2017 16:35:56 +0100 Subject: [PATCH 055/101] docs bug syntax change rev vs ref in docs. the default branch on hg is default, not master. --- website/source/docs/modules/sources.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/modules/sources.html.markdown b/website/source/docs/modules/sources.html.markdown index 0ab2978bc6..67162347f2 100644 --- a/website/source/docs/modules/sources.html.markdown +++ b/website/source/docs/modules/sources.html.markdown @@ -157,7 +157,7 @@ URLs for Mercurial repositories support the following query parameters: ``` module "consul" { - source = "hg::http://hashicorp.com/consul.hg?ref=master" + source = "hg::http://hashicorp.com/consul.hg?rev=default" } ``` From fc4cec3c40ac532c358526d8c6400ab1d0c5759f Mon Sep 17 00:00:00 2001 From: mathematician Date: Thu, 30 Mar 2017 11:09:11 -0500 Subject: [PATCH 056/101] Create AWS IAM Role data source, acceptance tests, documentation, and website link --- .../providers/aws/data_source_aws_iam_role.go | 67 +++++++++++++++++++ .../aws/data_source_aws_iam_role_test.go | 59 ++++++++++++++++ builtin/providers/aws/provider.go | 1 + .../providers/aws/d/iam_role.html.markdown | 35 ++++++++++ website/source/layouts/aws.erb | 3 + 5 files changed, 165 insertions(+) create mode 100644 builtin/providers/aws/data_source_aws_iam_role.go create mode 100644 builtin/providers/aws/data_source_aws_iam_role_test.go create mode 100644 website/source/docs/providers/aws/d/iam_role.html.markdown diff --git a/builtin/providers/aws/data_source_aws_iam_role.go b/builtin/providers/aws/data_source_aws_iam_role.go new file mode 100644 index 0000000000..f681268b96 --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_role.go @@ -0,0 +1,67 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceAwsIAMRole() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsIAMRoleRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "assume_role_policy_document": { + Type: schema.TypeString, + Computed: true, + }, + "path": { + Type: schema.TypeString, + Computed: true, + }, + "role_id": { + Type: schema.TypeString, + Computed: true, + }, + "role_name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func dataSourceAwsIAMRoleRead(d *schema.ResourceData, meta interface{}) error { + iamconn := meta.(*AWSClient).iamconn + + roleName := d.Get("role_name").(string) + + req := &iam.GetRoleInput{ + RoleName: aws.String(roleName), + } + + resp, err := iamconn.GetRole(req) + if err != nil { + return errwrap.Wrapf("Error getting roles: {{err}}", err) + } + if resp == nil { + return fmt.Errorf("no IAM role found") + } + + role := resp.Role + + d.SetId(*role.RoleId) + d.Set("arn", role.Arn) + d.Set("assume_role_policy_document", role.AssumeRolePolicyDocument) + d.Set("path", role.Path) + d.Set("role_id", role.RoleId) + + return nil +} diff --git a/builtin/providers/aws/data_source_aws_iam_role_test.go b/builtin/providers/aws/data_source_aws_iam_role_test.go new file mode 100644 index 0000000000..160e5d49be --- /dev/null +++ b/builtin/providers/aws/data_source_aws_iam_role_test.go @@ -0,0 +1,59 @@ +package aws + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAWSDataSourceIAMRole_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAwsIAMRoleConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.aws_iam_role.test", "role_id"), + resource.TestCheckResourceAttr("data.aws_iam_role.test", "assume_role_policy_document", "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D"), + resource.TestCheckResourceAttr("data.aws_iam_role.test", "path", "/testpath/"), + resource.TestCheckResourceAttr("data.aws_iam_role.test", "role_name", "TestRole"), + resource.TestMatchResourceAttr("data.aws_iam_role.test", "arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/testpath/TestRole$")), + ), + }, + }, + }) +} + +const testAccAwsIAMRoleConfig = ` +provider "aws" { + region = "us-east-1" +} + +resource "aws_iam_role" "test_role" { + name = "TestRole" + + assume_role_policy = <> aws_iam_policy_document + > + aws_iam_role + > aws_iam_server_certificate From d17623891b2097d4e5a08bcd5737fc1434b66c11 Mon Sep 17 00:00:00 2001 From: Jake Champlin Date: Thu, 30 Mar 2017 12:37:15 -0400 Subject: [PATCH 057/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcc42c4f49..66d5782976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `kubernetes_secret` [GH-12960] + * **New Data Source:** `aws_iam_role` [GH-13213] IMPROVEMENTS: From 99c8c5302b5fd49f20e906ce26c5d50f88e600a6 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Thu, 30 Mar 2017 20:21:21 +0300 Subject: [PATCH 058/101] provider/aws: Document the AWS_IAM authorizer type for api_gateway_method (#13214) Fixes: #10497 --- .../docs/providers/aws/r/api_gateway_method.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/api_gateway_method.html.markdown b/website/source/docs/providers/aws/r/api_gateway_method.html.markdown index a5aa1fad20..3445f5ca03 100644 --- a/website/source/docs/providers/aws/r/api_gateway_method.html.markdown +++ b/website/source/docs/providers/aws/r/api_gateway_method.html.markdown @@ -39,7 +39,7 @@ The following arguments are supported: * `rest_api_id` - (Required) The ID of the associated REST API * `resource_id` - (Required) The API resource ID * `http_method` - (Required) The HTTP Method (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTION`, `ANY`) -* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`) +* `authorization` - (Required) The type of authorization used for the method (`NONE`, `CUSTOM`, `AWS_IAM`) * `authorizer_id` - (Optional) The authorizer id to be used when the authorization is `CUSTOM` * `api_key_required` - (Optional) Specify if the method requires an API key * `request_models` - (Optional) A map of the API models used for the request's content type From 912bd2f9777e7b16fbb594beaeca72af0a04bcaf Mon Sep 17 00:00:00 2001 From: Phillip Shipley Date: Thu, 30 Mar 2017 14:01:13 -0400 Subject: [PATCH 059/101] Possible correction regarding remote state files Documentation has "... people have to remove state files..." when I believe it should say "...people have to use remote state files...". --- website/source/docs/state/purpose.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/state/purpose.html.md b/website/source/docs/state/purpose.html.md index d6e9beb58f..7c2893abfd 100644 --- a/website/source/docs/state/purpose.html.md +++ b/website/source/docs/state/purpose.html.md @@ -88,7 +88,7 @@ state is treated as the record of truth. ## Syncing -The primary motivation people have to remove state files is in an attempt +The primary motivation people have to remote state files is in an attempt to improve using Terraform with teams. State files can easily result in conflicts when two people modify infrastructure at the same time. From a21b599a791d6ca6849076e50fd0f1657949ed94 Mon Sep 17 00:00:00 2001 From: Devon Hubner Date: Thu, 30 Mar 2017 16:23:31 -0400 Subject: [PATCH 060/101] Expanded Joyent Triton documentation (#13205) * Added triton_vlan and triton_fabric documentation. Added Data Center information to the Triton provider documentation. Added an Ubuntu example to triton_machine. Cleaned up a copy-and-paste error in the sidebar_current of the Front Matter. * fixed the active resource sidebar highlight * expanded triton firewall ssh example to include authorization for multiple source IPs --- .../source/docs/import/importability.html.md | 5 +- .../docs/providers/triton/index.html.markdown | 4 +- .../triton/r/triton_fabric.html.markdown | 88 +++++++++++++++++++ .../r/triton_firewall_rule.html.markdown | 21 ++++- .../triton/r/triton_key.html.markdown | 2 +- .../triton/r/triton_machine.html.markdown | 27 +++++- .../triton/r/triton_vlan.html.markdown | 37 ++++++++ website/source/layouts/triton.erb | 10 ++- 8 files changed, 180 insertions(+), 14 deletions(-) create mode 100644 website/source/docs/providers/triton/r/triton_fabric.html.markdown create mode 100644 website/source/docs/providers/triton/r/triton_vlan.html.markdown diff --git a/website/source/docs/import/importability.html.md b/website/source/docs/import/importability.html.md index e4c50e2edb..a9bac81e6d 100644 --- a/website/source/docs/import/importability.html.md +++ b/website/source/docs/import/importability.html.md @@ -177,7 +177,8 @@ To make a resource importable, please see the ### Triton -* triton_firewall_rule * triton_key -* triton_machine +* triton_firewall_rule * triton_vlan +* triton_fabric +* triton_machine diff --git a/website/source/docs/providers/triton/index.html.markdown b/website/source/docs/providers/triton/index.html.markdown index 01ab521dbd..4954f1291e 100644 --- a/website/source/docs/providers/triton/index.html.markdown +++ b/website/source/docs/providers/triton/index.html.markdown @@ -20,7 +20,7 @@ provider "triton" { key_material = "${file("~/.ssh/id_rsa")}" key_id = "25:d4:a9:fe:ef:e6:c0:bf:b4:4b:4b:d4:a8:8f:01:0f" - # If using a private installation of Triton, specify the URL + # Set the URL to specify the specific Triton Data Center: url = "https://us-west-1.api.joyentcloud.com" } ``` @@ -32,4 +32,4 @@ The following arguments are supported in the `provider` block: * `account` - (Required) This is the name of the Triton account. It can also be provided via the `SDC_ACCOUNT` environment variable. * `key_material` - (Required) This is the private key of an SSH key associated with the Triton account to be used. * `key_id` - (Required) This is the fingerprint of the public key matching the key specified in `key_path`. It can be obtained via the command `ssh-keygen -l -E md5 -f /path/to/key` -* `url` - (Optional) This is the URL to the Triton API endpoint. It is required if using a private installation of Triton. The default is to use the Joyent public cloud. +* `url` - (Optional) This is the URL to the Triton API endpoint. It is required if using a private installation of Triton. The default is to use the Joyent public cloud us-west-1 endpoint. Valid public cloud endpoints include: `us-east-1`, `us-east-2`, `us-east-3`, `us-sw-1`, `us-west-1`, `eu-ams-1` diff --git a/website/source/docs/providers/triton/r/triton_fabric.html.markdown b/website/source/docs/providers/triton/r/triton_fabric.html.markdown new file mode 100644 index 0000000000..609690cd77 --- /dev/null +++ b/website/source/docs/providers/triton/r/triton_fabric.html.markdown @@ -0,0 +1,88 @@ +--- +layout: "triton" +page_title: "Triton: triton_fabric" +sidebar_current: "docs-triton-resource-fabric" +description: |- + The `triton_fabric` resource represents an SSH fabric for a Triton account. +--- + +# triton\_fabric + +The `triton_fabric` resource represents an fabric for a Triton account. The fabric is a logical set of interconnected switches. + +## Example Usages + +### Create a fabric + + +``` +resource "triton_fabric" "dmz" { + vlan_id = 100 + name = "dmz" + description = "DMZ Network" + subnet = "10.60.1.0/24" + provision_start_ip = "10.60.1.10" + provision_end_ip = "10.60.1.240" + gateway = "10.60.1.1" + resolvers = ["8.8.8.8", "8.8.4.4"] +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - (String, Required, Change forces new resource) + Network name. + +* `description` - (String, Optional, Change forces new resource) + Optional description of network. + +* `subnet` - (String, Required, Change forces new resource) + CIDR formatted string describing network. + +* `provision_start_ip` - (String, Required, Change forces new resource) + First IP on the network that can be assigned. + +* `provision_end_ip` - (String, Required, Change forces new resource) + Last assignable IP on the network. + +* `gateway` - (String, Optional, Change forces new resource) + Optional gateway IP. + +* `resolvers` - (List, Optional) + Array of IP addresses for resolvers. + +* `routes` - (Map, Optional, Change forces new resource) + Map of CIDR block to Gateway IP address. + +* `internet_nat` - (Bool, Optional, Change forces new resource) + If a NAT zone is provisioned at Gateway IP address. + +* `vlan_id` - (Int, Required, Change forces new resource) + VLAN id the network is on. Number between 0-4095 indicating VLAN ID. + + + + +## Attribute Reference + +The following attributes are exported: + +* `name` - (String) - Network name. +* `public` - (Bool) - Whether or not this is an RFC1918 network. +* `fabric` - (Bool) - Whether or not this network is on a fabric. +* `description` - (String) - Optional description of network. +* `subnet` - (String) - CIDR formatted string describing network. +* `provision_start_ip` - (String) - First IP on the network that can be assigned. +* `provision_end_ip` - (String) - Last assignable IP on the network. +* `gateway` - (String) - Optional gateway IP. +* `resolvers` - (List) - Array of IP addresses for resolvers. +* `routes` - (Map) - Map of CIDR block to Gateway IP address. +* `internet_nat` - (Bool) - If a NAT zone is provisioned at Gateway IP address. +* `vlan_id` - (Int) - VLAN id the network is on. Number between 0-4095 indicating VLAN ID. + + + + diff --git a/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown index ef31fd004b..1bb815f335 100644 --- a/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown +++ b/website/source/docs/providers/triton/r/triton_firewall_rule.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_firewall_rule" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-firewall-rule" description: |- The `triton_firewall_rule` resource represents a rule for the Triton cloud firewall. --- @@ -12,7 +12,7 @@ The `triton_firewall_rule` resource represents a rule for the Triton cloud firew ## Example Usages -Allow traffic on ports tcp/80 and tcp/443 to machines with the 'www' tag from any source +### Allow web traffic on ports tcp/80 and tcp/443 to machines with the 'www' tag from any source ``` @@ -21,9 +21,22 @@ resource "triton_firewall_rule" "www" { enabled = true } ``` -Block traffic on port tcp/143 to all machines +### Allow ssh traffic on port tcp/22 to all machines from known remote IPs + + +``` +resource "triton_firewall_rule" "22" { + rule = "FROM IP (IP w.x.y.z OR IP w.x.y.z) TO all vms ALLOW tcp port 22" + enabled = true +} +``` + + + +### Block IMAP traffic on port tcp/143 to all machines + ``` resource "triton_firewall_rule" "imap" { rule = "FROM any TO all vms BLOCK tcp port 143" @@ -31,6 +44,8 @@ resource "triton_firewall_rule" "imap" { } ``` + + ## Argument Reference The following arguments are supported: diff --git a/website/source/docs/providers/triton/r/triton_key.html.markdown b/website/source/docs/providers/triton/r/triton_key.html.markdown index e87ce3cade..4d18fdc64b 100644 --- a/website/source/docs/providers/triton/r/triton_key.html.markdown +++ b/website/source/docs/providers/triton/r/triton_key.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_key" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-key" description: |- The `triton_key` resource represents an SSH key for a Triton account. --- diff --git a/website/source/docs/providers/triton/r/triton_machine.html.markdown b/website/source/docs/providers/triton/r/triton_machine.html.markdown index f7a10f2856..c5f4d851d3 100644 --- a/website/source/docs/providers/triton/r/triton_machine.html.markdown +++ b/website/source/docs/providers/triton/r/triton_machine.html.markdown @@ -1,7 +1,7 @@ --- layout: "triton" page_title: "Triton: triton_machine" -sidebar_current: "docs-triton-firewall" +sidebar_current: "docs-triton-resource-machine" description: |- The `triton_machine` resource represents a virtual machine or infrastructure container running in Triton. --- @@ -12,12 +12,12 @@ The `triton_machine` resource represents a virtual machine or infrastructure con ## Example Usages -Run a SmartOS base-64 machine. +### Run a SmartOS base-64 machine. ``` -resource "triton_machine" "test" { - name = "example-machine" +resource "triton_machine" "test-smartos" { + name = "test-smartos" package = "g3-standard-0.25-smartos" image = "842e6fa6-6e9b-11e5-8402-1b490459e334" @@ -27,6 +27,25 @@ resource "triton_machine" "test" { } ``` +### Run an Ubuntu 14.04 LTS machine. + +``` +resource "triton_machine" "test-ubuntu" { + name = "test-ubuntu" + package = "g4-general-4G" + image = "1996a1d6-c0d9-11e6-8b80-4772e39dc920" + firewall_enabled = true + root_authorized_keys = "Example Key" + user_script = "#!/bin/bash\necho 'testing user-script' >> /tmp/test.out\nhostname $IMAGENAME" + + tags = { + purpose = "testing ubuntu" + } ## tags +} ## resource +``` + + + ## Argument Reference The following arguments are supported: diff --git a/website/source/docs/providers/triton/r/triton_vlan.html.markdown b/website/source/docs/providers/triton/r/triton_vlan.html.markdown new file mode 100644 index 0000000000..838cc43932 --- /dev/null +++ b/website/source/docs/providers/triton/r/triton_vlan.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "triton" +page_title: "Triton: triton_vlan" +sidebar_current: "docs-triton-resource-vlan" +description: |- + The `triton_vlan` resource represents an VLAN for a Triton account. +--- + +# triton\_vlan + +The `triton_vlan` resource represents an Triton VLAN. A VLAN provides a low level way to segregate and subdivide the network. Traffic on one VLAN cannot, _on its own_, reach another VLAN. + +## Example Usages + +### Create a VLAN + + +``` +resource "triton_vlan" "dmz" { + vlan_id = 100 + name = "dmz" + description = "DMZ VLAN" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vlan_id` - (int, Required, Change forces new resource) + Number between 0-4095 indicating VLAN ID + +* `name` - (string, Required) + Unique name to identify VLAN + +* `description` - (string, Optional) + Description of the VLAN diff --git a/website/source/layouts/triton.erb b/website/source/layouts/triton.erb index 1482a8b529..3e048b3ea6 100644 --- a/website/source/layouts/triton.erb +++ b/website/source/layouts/triton.erb @@ -14,11 +14,17 @@ Resources From 39b9e77d8a73a38d54f9b349665f33d46e751775 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 31 Mar 2017 07:31:30 +0100 Subject: [PATCH 066/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 748f22299e..4807a6cc94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] + * **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207] * **New Resource:** `kubernetes_secret` [GH-12960] * **New Data Source:** `aws_iam_role` [GH-13213] From ee0a4c43fc307f61a8c9f8b9a32c0e3f50f247e5 Mon Sep 17 00:00:00 2001 From: Seigo Uchida Date: Fri, 31 Mar 2017 16:32:54 +0900 Subject: [PATCH 067/101] [docs] Fix wrong attributes in lambda_permission doc (#13191) * Fix wrong attributes in lambda_permission doc * Add a missing attribute in lambda_permission doc --- .../docs/providers/aws/r/lambda_permission.html.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/aws/r/lambda_permission.html.markdown b/website/source/docs/providers/aws/r/lambda_permission.html.markdown index 671108cd95..3360aef2de 100644 --- a/website/source/docs/providers/aws/r/lambda_permission.html.markdown +++ b/website/source/docs/providers/aws/r/lambda_permission.html.markdown @@ -27,7 +27,7 @@ resource "aws_lambda_permission" "allow_cloudwatch" { resource "aws_lambda_alias" "test_alias" { name = "testalias" description = "a sample description" - function_name = "${aws_lambda_function.test_lambda.arn}" + function_name = "${aws_lambda_function.test_lambda.function_name}" function_version = "$LATEST" } @@ -36,6 +36,7 @@ resource "aws_lambda_function" "test_lambda" { function_name = "lambda_function_name" role = "${aws_iam_role.iam_for_lambda.arn}" handler = "exports.handler" + runtime = "nodejs6.10" } resource "aws_iam_role" "iam_for_lambda" { @@ -65,7 +66,7 @@ EOF resource "aws_lambda_permission" "with_sns" { statement_id = "AllowExecutionFromSNS" action = "lambda:InvokeFunction" - function_name = "${aws_lambda_function.my-func.arn}" + function_name = "${aws_lambda_function.my-func.function_name}" principal = "sns.amazonaws.com" source_arn = "${aws_sns_topic.default.arn}" } @@ -85,6 +86,7 @@ resource "aws_lambda_function" "func" { function_name = "lambda_called_from_sns" role = "${aws_iam_role.default.arn}" handler = "exports.handler" + runtime = "python2.7" } resource "aws_iam_role" "default" { From 293922e5aef6ebc32f26c302ca06fe27021133e8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:28:56 +0300 Subject: [PATCH 068/101] provider/aws: Refresh aws_alb_target_group stickiness on manual updates (#13199) Fixes: #13167 When changes to the target group were made via CLI or AWS Console, they were not being picked up by terraform. This is because we were not catching an error setting the `stickiness` parameters: ``` Error refreshing state: 1 error(s) occurred: * aws_alb_target_group.test: aws_alb_target_group.test: stickiness.0.enabled: '' expected type 'bool', got unconvertible type 'string' ``` This meant that changes were not picked up in the following plan. The changes mean the following now: ``` ~ aws_alb_target_group.test stickiness.0.cookie_duration: "10440" => "10000" stickiness.0.enabled: "false" => "true" Plan: 0 to add, 1 to change, 0 to destroy. ``` --- .../aws/resource_aws_alb_target_group.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index 96ecd0d429..01f96b3274 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -258,11 +258,19 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err for _, attr := range attrResp.Attributes { switch *attr.Key { case "stickiness.enabled": - stickinessMap["enabled"] = *attr.Value + enabled, err := strconv.ParseBool(*attr.Value) + if err != nil { + return fmt.Errorf("Error converting stickiness.enabled to bool: %s", *attr.Value) + } + stickinessMap["enabled"] = enabled case "stickiness.type": stickinessMap["type"] = *attr.Value case "stickiness.lb_cookie.duration_seconds": - stickinessMap["cookie_duration"] = *attr.Value + duration, err := strconv.Atoi(*attr.Value) + if err != nil { + return fmt.Errorf("Error converting stickiness.lb_cookie.duration_seconds to int: %s", *attr.Value) + } + stickinessMap["cookie_duration"] = duration case "deregistration_delay.timeout_seconds": timeout, err := strconv.Atoi(*attr.Value) if err != nil { @@ -271,7 +279,10 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err d.Set("deregistration_delay", timeout) } } - d.Set("stickiness", []interface{}{stickinessMap}) + + if err := d.Set("stickiness", []interface{}{stickinessMap}); err != nil { + return err + } return nil } From 5a37434bf198a2d53b18bc0556e505594fb1a12a Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:30:21 +0300 Subject: [PATCH 069/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4807a6cc94..5d29e34097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ BUG FIXES: * provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160] * provider/aws: Don't set DBName on `aws_db_instance` from snapshot [GH-13140] * provider/aws: Add DiffSuppression to aws_ecs_service placement_strategies [GH-13220] + * provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From e4e9d1e0730cb8031a58719579cc39985eda14eb Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:34:51 +0300 Subject: [PATCH 070/101] provider/aws: Preserve default retain_on_delete in cloudfront import (#13209) Fixes: #10969 --- .../providers/aws/import_aws_cloudfront_distribution.go | 4 ++++ .../aws/import_aws_cloudfront_distribution_test.go | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/providers/aws/import_aws_cloudfront_distribution.go b/builtin/providers/aws/import_aws_cloudfront_distribution.go index dcb8792a3e..acfc836dc5 100644 --- a/builtin/providers/aws/import_aws_cloudfront_distribution.go +++ b/builtin/providers/aws/import_aws_cloudfront_distribution.go @@ -7,6 +7,10 @@ import ( ) func resourceAwsCloudFrontDistributionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + // This is a non API attribute + // We are merely setting this to the same value as the Default setting in the schema + d.Set("retain_on_delete", false) + conn := meta.(*AWSClient).cloudfrontconn id := d.Id() resp, err := conn.GetDistributionConfig(&cloudfront.GetDistributionConfigInput{ diff --git a/builtin/providers/aws/import_aws_cloudfront_distribution_test.go b/builtin/providers/aws/import_aws_cloudfront_distribution_test.go index 9fc1958198..787d913a59 100644 --- a/builtin/providers/aws/import_aws_cloudfront_distribution_test.go +++ b/builtin/providers/aws/import_aws_cloudfront_distribution_test.go @@ -19,16 +19,13 @@ func TestAccAWSCloudFrontDistribution_importBasic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckCloudFrontDistributionDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testConfig, }, - resource.TestStep{ + { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - // Ignore retain_on_delete since it doesn't come from the AWS - // API. - ImportStateVerifyIgnore: []string{"retain_on_delete"}, }, }, }) From 453325f3246cf3d41042145b68a04b3ed84cf45c Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:35:39 +0300 Subject: [PATCH 071/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d29e34097..f57fcd3b4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ BUG FIXES: * provider/aws: Don't set DBName on `aws_db_instance` from snapshot [GH-13140] * provider/aws: Add DiffSuppression to aws_ecs_service placement_strategies [GH-13220] * provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199] + * provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 46a5cd543c539a37f4770c2dc7040a112f992512 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:36:15 +0300 Subject: [PATCH 072/101] provider/aws: Refresh aws_alb_target_group tags (#13200) Fixes: #8847 We actually didn't get the list of tags from the API, therefore, any manual changes were not actually showing up in subsequent plans ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBTargetGroup_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 15:45:53 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBTargetGroup_basic -timeout 120m === RUN TestAccAWSALBTargetGroup_basic --- PASS: TestAccAWSALBTargetGroup_basic (62.76s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 62.787s ``` --- .../providers/aws/resource_aws_alb_target_group.go | 14 ++++++++++++++ .../aws/resource_aws_alb_target_group_test.go | 2 ++ 2 files changed, 16 insertions(+) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index 01f96b3274..df1a662be1 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -284,6 +284,20 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err return err } + tagsResp, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{ + ResourceArns: []*string{aws.String(d.Id())}, + }) + if err != nil { + return errwrap.Wrapf("Error retrieving Target Group Tags: {{err}}", err) + } + for _, t := range tagsResp.TagDescriptions { + if *t.ResourceArn == d.Id() { + if err := d.Set("tags", tagsToMapELBv2(t.Tags)); err != nil { + return err + } + } + } + return nil } diff --git a/builtin/providers/aws/resource_aws_alb_target_group_test.go b/builtin/providers/aws/resource_aws_alb_target_group_test.go index 67d453e6cb..7a67978a8a 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group_test.go +++ b/builtin/providers/aws/resource_aws_alb_target_group_test.go @@ -77,6 +77,8 @@ func TestAccAWSALBTargetGroup_basic(t *testing.T) { resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "3"), resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "3"), resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200-299"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.%", "1"), + resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.TestName", "TestAccAWSALBTargetGroup_basic"), ), }, }, From d139db87397260d29e791e64e568c0f95f406632 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:36:39 +0300 Subject: [PATCH 073/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57fcd3b4a..ed00095b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ BUG FIXES: * provider/aws: Add DiffSuppression to aws_ecs_service placement_strategies [GH-13220] * provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199] * provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209] + * provider/aws: Refresh aws_alb_target_group tags [GH-13200] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From d06db231971de673efc76ec8c82d711443c77655 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:40:37 +0300 Subject: [PATCH 074/101] provider/aws: Set aws_vpn_connection to recreate when in deleted state (#13204) Fixes: #12440 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSVpnConnection_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 16:16:13 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSVpnConnection_ -timeout 120m === RUN TestAccAWSVpnConnection_importBasic --- PASS: TestAccAWSVpnConnection_importBasic (208.68s) === RUN TestAccAWSVpnConnection_basic --- PASS: TestAccAWSVpnConnection_basic (391.02s) === RUN TestAccAWSVpnConnection_withoutStaticRoutes --- PASS: TestAccAWSVpnConnection_withoutStaticRoutes (316.99s) === RUN TestAccAWSVpnConnection_disappears --- PASS: TestAccAWSVpnConnection_disappears (202.84s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1119.563s ``` --- .../aws/resource_aws_vpn_connection.go | 5 ++ .../aws/resource_aws_vpn_connection_test.go | 82 ++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_vpn_connection.go b/builtin/providers/aws/resource_aws_vpn_connection.go index b38d903cf8..1cdd83efd1 100644 --- a/builtin/providers/aws/resource_aws_vpn_connection.go +++ b/builtin/providers/aws/resource_aws_vpn_connection.go @@ -294,6 +294,11 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro } vpnConnection := resp.VpnConnections[0] + if vpnConnection == nil || *vpnConnection.State == "deleted" { + // Seems we have lost our VPN Connection + d.SetId("") + return nil + } // Set attributes under the user's control. d.Set("vpn_gateway_id", vpnConnection.VpnGatewayId) diff --git a/builtin/providers/aws/resource_aws_vpn_connection_test.go b/builtin/providers/aws/resource_aws_vpn_connection_test.go index a07bdd10ba..e5328ca9a1 100644 --- a/builtin/providers/aws/resource_aws_vpn_connection_test.go +++ b/builtin/providers/aws/resource_aws_vpn_connection_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -13,6 +14,8 @@ import ( ) func TestAccAWSVpnConnection_basic(t *testing.T) { + var vpn ec2.VpnConnection + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_vpn_connection.foo", @@ -27,6 +30,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) { "aws_vpn_gateway.vpn_gateway", "aws_customer_gateway.customer_gateway", "aws_vpn_connection.foo", + &vpn, ), ), }, @@ -38,6 +42,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) { "aws_vpn_gateway.vpn_gateway", "aws_customer_gateway.customer_gateway", "aws_vpn_connection.foo", + &vpn, ), ), }, @@ -46,6 +51,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) { } func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) { + var vpn ec2.VpnConnection resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "aws_vpn_connection.foo", @@ -60,6 +66,7 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) { "aws_vpn_gateway.vpn_gateway", "aws_customer_gateway.customer_gateway", "aws_vpn_connection.foo", + &vpn, ), resource.TestCheckResourceAttr("aws_vpn_connection.foo", "static_routes_only", "false"), ), @@ -68,6 +75,74 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) { }) } +func TestAccAWSVpnConnection_disappears(t *testing.T) { + var vpn ec2.VpnConnection + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccAwsVpnConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsVpnConnectionConfig, + Check: resource.ComposeTestCheckFunc( + testAccAwsVpnConnection( + "aws_vpc.vpc", + "aws_vpn_gateway.vpn_gateway", + "aws_customer_gateway.customer_gateway", + "aws_vpn_connection.foo", + &vpn, + ), + testAccAWSVpnConnectionDisappears(&vpn), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAWSVpnConnectionDisappears(connection *ec2.VpnConnection) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + _, err := conn.DeleteVpnConnection(&ec2.DeleteVpnConnectionInput{ + VpnConnectionId: connection.VpnConnectionId, + }) + if err != nil { + if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnConnectionID.NotFound" { + return nil + } + if err != nil { + return err + } + } + + return resource.Retry(40*time.Minute, func() *resource.RetryError { + opts := &ec2.DescribeVpnConnectionsInput{ + VpnConnectionIds: []*string{connection.VpnConnectionId}, + } + resp, err := conn.DescribeVpnConnections(opts) + if err != nil { + cgw, ok := err.(awserr.Error) + if ok && cgw.Code() == "InvalidVpnConnectionID.NotFound" { + return nil + } + if ok && cgw.Code() == "IncorrectState" { + return resource.RetryableError(fmt.Errorf( + "Waiting for VPN Connection to be in the correct state: %v", connection.VpnConnectionId)) + } + return resource.NonRetryableError( + fmt.Errorf("Error retrieving VPN Connection: %s", err)) + } + if *resp.VpnConnections[0].State == "deleted" { + return nil + } + return resource.RetryableError(fmt.Errorf( + "Waiting for VPN Connection: %v", connection.VpnConnectionId)) + }) + } +} + func testAccAwsVpnConnectionDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { @@ -112,7 +187,8 @@ func testAccAwsVpnConnection( vpcResource string, vpnGatewayResource string, customerGatewayResource string, - vpnConnectionResource string) resource.TestCheckFunc { + vpnConnectionResource string, + vpnConnection *ec2.VpnConnection) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[vpnConnectionResource] if !ok { @@ -129,7 +205,7 @@ func testAccAwsVpnConnection( ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn - _, err := ec2conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ + resp, err := ec2conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ VpnConnectionIds: []*string{aws.String(connection.Primary.ID)}, }) @@ -137,6 +213,8 @@ func testAccAwsVpnConnection( return err } + *vpnConnection = *resp.VpnConnections[0] + return nil } } From b449b80af3333d5ac8b0976156d9ef5401d8fa5c Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:41:08 +0300 Subject: [PATCH 075/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed00095b57..93823fa3d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ BUG FIXES: * provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199] * provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209] * provider/aws: Refresh aws_alb_target_group tags [GH-13200] + * provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 74c0353231d8f31921d28f90b146a838a58359c8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:45:45 +0300 Subject: [PATCH 076/101] provider/aws: Wait for aws_opsworks_instance to be running when it's specified (#13218) Fixes: #9959 When we specify that we want an opsworks_instance state of running, we should wait for that the be the case. This will then allow us to use the Computed values (e.g. private_ip) etc and allow us to use provisioners as part of the terraform config ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSOpsworksInstance' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/30 20:55:21 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSOpsworksInstance -timeout 120m === RUN TestAccAWSOpsworksInstance_importBasic --- PASS: TestAccAWSOpsworksInstance_importBasic (72.28s) === RUN TestAccAWSOpsworksInstance --- PASS: TestAccAWSOpsworksInstance (110.17s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 182.479s ``` --- builtin/providers/aws/resource_aws_opsworks_instance.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_opsworks_instance.go b/builtin/providers/aws/resource_aws_opsworks_instance.go index 2b2b51c492..b195ac9027 100644 --- a/builtin/providers/aws/resource_aws_opsworks_instance.go +++ b/builtin/providers/aws/resource_aws_opsworks_instance.go @@ -781,7 +781,7 @@ func resourceAwsOpsworksInstanceCreate(d *schema.ResourceData, meta interface{}) d.Set("id", instanceId) if v, ok := d.GetOk("state"); ok && v.(string) == "running" { - err := startOpsworksInstance(d, meta, false) + err := startOpsworksInstance(d, meta, true) if err != nil { return err } @@ -860,7 +860,7 @@ func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) } } else { if status != "stopped" && status != "stopping" && status != "shutting_down" { - err := stopOpsworksInstance(d, meta, false) + err := stopOpsworksInstance(d, meta, true) if err != nil { return err } From 835792018a119284573eb787df5c4ff14fc7c9b8 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 14:47:27 +0300 Subject: [PATCH 077/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93823fa3d4..c453749ca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ BUG FIXES: * provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209] * provider/aws: Refresh aws_alb_target_group tags [GH-13200] * provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204] + * provider/aws: Wait for aws_opsworks_instance to be running when it's specified [GH-13218] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 0f2331cf81d9430d92dc11c3a2937d918c594f05 Mon Sep 17 00:00:00 2001 From: Phillip Shipley Date: Fri, 31 Mar 2017 08:29:04 -0400 Subject: [PATCH 078/101] Improved sentence based on feedback --- website/source/docs/state/purpose.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/state/purpose.html.md b/website/source/docs/state/purpose.html.md index 7c2893abfd..a8647006ec 100644 --- a/website/source/docs/state/purpose.html.md +++ b/website/source/docs/state/purpose.html.md @@ -88,7 +88,7 @@ state is treated as the record of truth. ## Syncing -The primary motivation people have to remote state files is in an attempt +The primary motivation people have for using remote state files is in an attempt to improve using Terraform with teams. State files can easily result in conflicts when two people modify infrastructure at the same time. From 6ed873b72d85aaa896356d1c742c72bf3605b45e Mon Sep 17 00:00:00 2001 From: zimbatm Date: Fri, 31 Mar 2017 11:46:48 +0100 Subject: [PATCH 079/101] Make the external test work on more environments GOPATH is actually a list of path and doesn't necessarily have to be set. If unset it will default to $GOPATH/go in go 1.9+. Assume that go install will install to the first path in the list. --- builtin/providers/external/data_source_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/builtin/providers/external/data_source_test.go b/builtin/providers/external/data_source_test.go index dbdf003864..b1ceabddfb 100644 --- a/builtin/providers/external/data_source_test.go +++ b/builtin/providers/external/data_source_test.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "regexp" "testing" @@ -117,8 +118,13 @@ func buildDataSourceTestProgram() (string, error) { return "", fmt.Errorf("failed to build test stub program: %s", err) } + gopath := os.Getenv("GOPATH") + if gopath == "" { + gopath = filepath.Join(os.Getenv("HOME") + "/go") + } + programPath := path.Join( - os.Getenv("GOPATH"), "bin", "tf-acc-external-data-source", + filepath.SplitList(gopath)[0], "bin", "tf-acc-external-data-source", ) return programPath, nil } From c44487caee20051124d69d7e00ba4f08f3605f0c Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Wed, 29 Mar 2017 18:26:54 -0400 Subject: [PATCH 080/101] Handle the case when issue labels already exist This fixes GH-13163 --- .../github/resource_github_issue_label.go | 73 +++++++++++-------- .../github/r/issue_label.html.markdown | 16 +++- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/builtin/providers/github/resource_github_issue_label.go b/builtin/providers/github/resource_github_issue_label.go index 0d89c0343b..5a1f6eea4f 100644 --- a/builtin/providers/github/resource_github_issue_label.go +++ b/builtin/providers/github/resource_github_issue_label.go @@ -10,9 +10,9 @@ import ( func resourceGithubIssueLabel() *schema.Resource { return &schema.Resource{ - Create: resourceGithubIssueLabelCreate, + Create: resourceGithubIssueLabelCreateOrUpdate, Read: resourceGithubIssueLabelRead, - Update: resourceGithubIssueLabelUpdate, + Update: resourceGithubIssueLabelCreateOrUpdate, Delete: resourceGithubIssueLabelDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -40,21 +40,54 @@ func resourceGithubIssueLabel() *schema.Resource { } } -func resourceGithubIssueLabelCreate(d *schema.ResourceData, meta interface{}) error { +// resourceGithubIssueLabelCreateOrUpdate idempotently creates or updates an +// issue label. Issue labels are keyed off of their "name", so pre-existing +// issue labels result in a 422 HTTP error if they exist outside of Terraform. +// Normally this would not be an issue, except new repositories are created with +// a "default" set of labels, and those labels easily conflict with custom ones. +// +// This function will first check if the label exists, and then issue an update, +// otherwise it will create. This is also advantageous in that we get to use the +// same function for two schema funcs. + +func resourceGithubIssueLabelCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*Organization).client + o := meta.(*Organization).name r := d.Get("repository").(string) n := d.Get("name").(string) c := d.Get("color").(string) - label := github.Label{ + + label := &github.Label{ Name: &n, Color: &c, } - log.Printf("[DEBUG] Creating label: %#v", label) - _, resp, err := client.Issues.CreateLabel(context.TODO(), meta.(*Organization).name, r, &label) - log.Printf("[DEBUG] Response from creating label: %s", *resp) - if err != nil { - return err + log.Printf("[DEBUG] Querying label existence %s/%s (%s)", o, r, n) + existing, _, _ := client.Issues.GetLabel(context.TODO(), o, r, n) + + if existing != nil { + log.Printf("[DEBUG] Updating label: %s/%s (%s: %s)", o, r, n, c) + + // Pull out the original name. If we already have a resource, this is the + // parsed ID. If not, it's the value given to the resource. + var oname string + if d.Id() == "" { + oname = n + } else { + _, oname = parseTwoPartID(d.Id()) + } + + _, _, err := client.Issues.EditLabel(context.TODO(), o, r, oname, label) + if err != nil { + return err + } + } else { + log.Printf("[DEBUG] Creating label: %s/%s (%s: %s)", o, r, n, c) + _, resp, err := client.Issues.CreateLabel(context.TODO(), o, r, label) + log.Printf("[DEBUG] Response from creating label: %s", *resp) + if err != nil { + return err + } } d.SetId(buildTwoPartID(&r, &n)) @@ -66,6 +99,7 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro client := meta.(*Organization).client r, n := parseTwoPartID(d.Id()) + log.Printf("[DEBUG] Reading label: %s/%s", r, n) githubLabel, _, err := client.Issues.GetLabel(context.TODO(), meta.(*Organization).name, r, n) if err != nil { d.SetId("") @@ -80,31 +114,12 @@ func resourceGithubIssueLabelRead(d *schema.ResourceData, meta interface{}) erro return nil } -func resourceGithubIssueLabelUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*Organization).client - r := d.Get("repository").(string) - n := d.Get("name").(string) - c := d.Get("color").(string) - - _, originalName := parseTwoPartID(d.Id()) - _, _, err := client.Issues.EditLabel(context.TODO(), meta.(*Organization).name, r, originalName, &github.Label{ - Name: &n, - Color: &c, - }) - if err != nil { - return err - } - - d.SetId(buildTwoPartID(&r, &n)) - - return resourceGithubIssueLabelRead(d, meta) -} - func resourceGithubIssueLabelDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*Organization).client r := d.Get("repository").(string) n := d.Get("name").(string) + log.Printf("[DEBUG] Deleting label: %s/%s", r, n) _, err := client.Issues.DeleteLabel(context.TODO(), meta.(*Organization).name, r, n) return err } diff --git a/website/source/docs/providers/github/r/issue_label.html.markdown b/website/source/docs/providers/github/r/issue_label.html.markdown index bb2a9aa046..216ed2bfe9 100644 --- a/website/source/docs/providers/github/r/issue_label.html.markdown +++ b/website/source/docs/providers/github/r/issue_label.html.markdown @@ -6,16 +6,24 @@ description: |- Provides a GitHub issue label resource. --- -# github\_issue_label +# github_issue_label Provides a GitHub issue label resource. This resource allows you to create and manage issue labels within your Github organization. +Issue labels are keyed off of their "name", so pre-existing issue labels result +in a 422 HTTP error if they exist outside of Terraform. Normally this would not +be an issue, except new repositories are created with a "default" set of labels, +and those labels easily conflict with custom ones. + +This resource will first check if the label exists, and then issue an update, +otherwise it will create. + ## Example Usage -``` +```hcl # Create a new, red colored label resource "github_issue_label" "test_repo" { repository = "test-repo" @@ -29,5 +37,7 @@ resource "github_issue_label" "test_repo" { The following arguments are supported: * `repository` - (Required) The GitHub repository + * `name` - (Required) The name of the label. -* `color` - (Required) A 6 character hex code, without the leading #, identifying the color of the label. + +* `color` - (Required) A 6 character hex code, **without the leading #**, identifying the color of the label. From 33dd50459362a087c88c12eaa2e7b2f4715f7269 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Fri, 31 Mar 2017 11:44:00 -0400 Subject: [PATCH 081/101] Add test --- .../resource_github_issue_label_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/builtin/providers/github/resource_github_issue_label_test.go b/builtin/providers/github/resource_github_issue_label_test.go index d3b3a0597f..66461302de 100644 --- a/builtin/providers/github/resource_github_issue_label_test.go +++ b/builtin/providers/github/resource_github_issue_label_test.go @@ -32,6 +32,13 @@ func TestAccGithubIssueLabel_basic(t *testing.T) { testAccCheckGithubIssueLabelAttributes(&label, "bar", "FFFFFF"), ), }, + { + Config: testAccGitHubIssueLabelExistsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckGithubIssueLabelExists("github_issue_label.test", &label), + testAccCheckGithubIssueLabelAttributes(&label, "enhancement", "FF00FF"), + ), + }, }, }) } @@ -134,3 +141,16 @@ resource "github_issue_label" "test" { color = "FFFFFF" } `, testRepo) + +var testAccGitHubIssueLabelExistsConfig string = fmt.Sprintf(` +// Create a repository which has the default labels +resource "github_repository" "test" { + name = "tf-acc-repo-label-abc1234" +} + +resource "github_issue_label" "test" { + repository = "${github_repository.test.name}" + name = "enhancement" // Important! This is a pre-created label + color = "FF00FF" +} +`) From 42557dae122e69cd69a6d7ab719946881f9059f5 Mon Sep 17 00:00:00 2001 From: Gauthier Wallet Date: Fri, 31 Mar 2017 18:45:06 +0200 Subject: [PATCH 082/101] provider/aws: Added API Gateway integration update (#13249) --- .../resource_aws_api_gateway_integration.go | 154 +++++++++++++-- ...source_aws_api_gateway_integration_test.go | 182 +++++++++++------- .../r/api_gateway_integration.html.markdown | 8 +- 3 files changed, 251 insertions(+), 93 deletions(-) diff --git a/builtin/providers/aws/resource_aws_api_gateway_integration.go b/builtin/providers/aws/resource_aws_api_gateway_integration.go index b069828809..f782e11ea6 100644 --- a/builtin/providers/aws/resource_aws_api_gateway_integration.go +++ b/builtin/providers/aws/resource_aws_api_gateway_integration.go @@ -11,87 +11,94 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "strings" ) func resourceAwsApiGatewayIntegration() *schema.Resource { return &schema.Resource{ Create: resourceAwsApiGatewayIntegrationCreate, Read: resourceAwsApiGatewayIntegrationRead, - Update: resourceAwsApiGatewayIntegrationCreate, + Update: resourceAwsApiGatewayIntegrationUpdate, Delete: resourceAwsApiGatewayIntegrationDelete, Schema: map[string]*schema.Schema{ - "rest_api_id": &schema.Schema{ + "rest_api_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "resource_id": &schema.Schema{ + "resource_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "http_method": &schema.Schema{ + "http_method": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validateHTTPMethod, }, - "type": &schema.Schema{ + "type": { Type: schema.TypeString, Required: true, + ForceNew: true, ValidateFunc: validateApiGatewayIntegrationType, }, - "uri": &schema.Schema{ + "uri": { Type: schema.TypeString, Optional: true, + ForceNew: true, }, - "credentials": &schema.Schema{ + "credentials": { Type: schema.TypeString, Optional: true, + ForceNew: true, }, - "integration_http_method": &schema.Schema{ + "integration_http_method": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: validateHTTPMethod, }, - "request_templates": &schema.Schema{ + "request_templates": { Type: schema.TypeMap, Optional: true, Elem: schema.TypeString, }, - "request_parameters": &schema.Schema{ + "request_parameters": { Type: schema.TypeMap, Elem: schema.TypeString, Optional: true, ConflictsWith: []string{"request_parameters_in_json"}, }, - "request_parameters_in_json": &schema.Schema{ + "request_parameters_in_json": { Type: schema.TypeString, Optional: true, ConflictsWith: []string{"request_parameters"}, Deprecated: "Use field request_parameters instead", }, - "content_handling": &schema.Schema{ + "content_handling": { Type: schema.TypeString, Optional: true, + ForceNew: true, ValidateFunc: validateApiGatewayIntegrationContentHandling, }, - "passthrough_behavior": &schema.Schema{ + "passthrough_behavior": { Type: schema.TypeString, Optional: true, Computed: true, + ForceNew: true, ValidateFunc: validateApiGatewayIntegrationPassthroughBehavior, }, }, @@ -101,6 +108,7 @@ func resourceAwsApiGatewayIntegration() *schema.Resource { func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).apigateway + log.Print("[DEBUG] Creating API Gateway Integration") var integrationHttpMethod *string if v, ok := d.GetOk("integration_http_method"); ok { integrationHttpMethod = aws.String(v.(string)) @@ -163,13 +171,13 @@ func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interfa d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string))) - return nil + return resourceAwsApiGatewayIntegrationRead(d, meta) } func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).apigateway - log.Printf("[DEBUG] Reading API Gateway Integration %s", d.Id()) + log.Printf("[DEBUG] Reading API Gateway Integration: %s", d.Id()) integration, err := conn.GetIntegration(&apigateway.GetIntegrationInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -191,17 +199,127 @@ func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface } d.Set("request_templates", aws.StringValueMap(integration.RequestTemplates)) - d.Set("credentials", integration.Credentials) d.Set("type", integration.Type) - d.Set("uri", integration.Uri) d.Set("request_parameters", aws.StringValueMap(integration.RequestParameters)) d.Set("request_parameters_in_json", aws.StringValueMap(integration.RequestParameters)) d.Set("passthrough_behavior", integration.PassthroughBehavior) - d.Set("content_handling", integration.ContentHandling) + + if integration.Uri != nil { + d.Set("uri", integration.Uri) + } + + if integration.Credentials != nil { + d.Set("credentials", integration.Credentials) + } + + if integration.ContentHandling != nil { + d.Set("content_handling", integration.ContentHandling) + } return nil } +func resourceAwsApiGatewayIntegrationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + + log.Printf("[DEBUG] Updating API Gateway Integration: %s", d.Id()) + operations := make([]*apigateway.PatchOperation, 0) + + // https://docs.aws.amazon.com/apigateway/api-reference/link-relation/integration-update/#remarks + // According to the above documentation, only a few parts are addable / removable. + if d.HasChange("request_templates") { + o, n := d.GetChange("request_templates") + prefix := "requestTemplates" + + os := o.(map[string]interface{}) + ns := n.(map[string]interface{}) + + // Handle Removal + for k := range os { + if _, ok := ns[k]; !ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("remove"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + }) + } + } + + for k, v := range ns { + // Handle replaces + if _, ok := os[k]; ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + Value: aws.String(v.(string)), + }) + } + + // Handle additions + if _, ok := os[k]; !ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("add"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + Value: aws.String(v.(string)), + }) + } + } + } + + if d.HasChange("request_parameters") { + o, n := d.GetChange("request_parameters") + prefix := "requestParameters" + + os := o.(map[string]interface{}) + ns := n.(map[string]interface{}) + + // Handle Removal + for k := range os { + if _, ok := ns[k]; !ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("remove"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + }) + } + } + + for k, v := range ns { + // Handle replaces + if _, ok := os[k]; ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + Value: aws.String(v.(string)), + }) + } + + // Handle additions + if _, ok := os[k]; !ok { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("add"), + Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))), + Value: aws.String(v.(string)), + }) + } + } + } + + params := &apigateway.UpdateIntegrationInput{ + HttpMethod: aws.String(d.Get("http_method").(string)), + ResourceId: aws.String(d.Get("resource_id").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + PatchOperations: operations, + } + + _, err := conn.UpdateIntegration(params) + if err != nil { + return fmt.Errorf("Error updating API Gateway Integration: %s", err) + } + + d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string))) + + return resourceAwsApiGatewayIntegrationRead(d, meta) +} + func resourceAwsApiGatewayIntegrationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).apigateway log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id()) diff --git a/builtin/providers/aws/resource_aws_api_gateway_integration_test.go b/builtin/providers/aws/resource_aws_api_gateway_integration_test.go index 153ed13b41..ff9c233871 100644 --- a/builtin/providers/aws/resource_aws_api_gateway_integration_test.go +++ b/builtin/providers/aws/resource_aws_api_gateway_integration_test.go @@ -19,88 +19,80 @@ func TestAccAWSAPIGatewayIntegration_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSAPIGatewayIntegrationDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSAPIGatewayIntegrationConfig, Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf), - testAccCheckAWSAPIGatewayIntegrationAttributes(&conf), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "type", "HTTP"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "integration_http_method", "GET"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "uri", "https://www.google.de"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "request_templates.application/json", ""), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"), - resource.TestCheckNoResourceAttr( - "aws_api_gateway_integration.test", "content_handling"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"), + resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Foo", "'Bar'"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), ), }, - resource.TestStep{ + { Config: testAccAWSAPIGatewayIntegrationConfigUpdate, Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf), - testAccCheckAWSAPIGatewayMockIntegrationAttributes(&conf), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "type", "MOCK"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "integration_http_method", ""), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "uri", ""), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "passthrough_behavior", "NEVER"), - resource.TestCheckResourceAttr( - "aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_BINARY"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"), + resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'updated'"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-FooBar", "'Baz'"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", "{'foobar': 'bar}"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.text/html", "Foo"), + ), + }, + + { + Config: testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"), + resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "0"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "0"), + ), + }, + + { + Config: testAccAWSAPIGatewayIntegrationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"), + resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""), + resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), ), }, }, }) } -func testAccCheckAWSAPIGatewayMockIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.Type != "MOCK" { - return fmt.Errorf("Wrong Type: %q", *conf.Type) - } - if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'updated'" { - return fmt.Errorf("wrong updated RequestParameters for header.X-Authorization") - } - if *conf.ContentHandling != "CONVERT_TO_BINARY" { - return fmt.Errorf("wrong ContentHandling: %q", *conf.ContentHandling) - } - return nil - } -} - -func testAccCheckAWSAPIGatewayIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.HttpMethod == "" { - return fmt.Errorf("empty HttpMethod") - } - if *conf.Uri != "https://www.google.de" { - return fmt.Errorf("wrong Uri") - } - if *conf.Type != "HTTP" { - return fmt.Errorf("wrong Type") - } - if conf.RequestTemplates["application/json"] != nil { - return fmt.Errorf("wrong RequestTemplate for application/json") - } - if *conf.RequestTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" { - return fmt.Errorf("wrong RequestTemplate for application/xml") - } - if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'static'" { - return fmt.Errorf("wrong RequestParameters for header.X-Authorization") - } - return nil - } -} - func testAccCheckAWSAPIGatewayIntegrationExists(n string, res *apigateway.Integration) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -196,13 +188,15 @@ resource "aws_api_gateway_integration" "test" { } request_parameters = { - "integration.request.header.X-Authorization" = "'static'" + "integration.request.header.X-Authorization" = "'static'" + "integration.request.header.X-Foo" = "'Bar'" } type = "HTTP" uri = "https://www.google.de" integration_http_method = "GET" passthrough_behavior = "WHEN_NO_MATCH" + content_handling = "CONVERT_TO_TEXT" } ` @@ -233,13 +227,55 @@ resource "aws_api_gateway_integration" "test" { resource_id = "${aws_api_gateway_resource.test.id}" http_method = "${aws_api_gateway_method.test.http_method}" - request_parameters = { - "integration.request.header.X-Authorization" = "'updated'" + request_templates = { + "application/json" = "{'foobar': 'bar}" + "text/html" = "Foo" } - type = "MOCK" - passthrough_behavior = "NEVER" - content_handling = "CONVERT_TO_BINARY" + request_parameters = { + "integration.request.header.X-Authorization" = "'updated'" + "integration.request.header.X-FooBar" = "'Baz'" + } + type = "HTTP" + uri = "https://www.google.de" + integration_http_method = "GET" + passthrough_behavior = "WHEN_NO_MATCH" + content_handling = "CONVERT_TO_TEXT" +} +` + +const testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates = ` +resource "aws_api_gateway_rest_api" "test" { + name = "test" +} + +resource "aws_api_gateway_resource" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}" + path_part = "test" +} + +resource "aws_api_gateway_method" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "GET" + authorization = "NONE" + + request_models = { + "application/json" = "Error" + } +} + +resource "aws_api_gateway_integration" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "${aws_api_gateway_method.test.http_method}" + + type = "HTTP" + uri = "https://www.google.de" + integration_http_method = "GET" + passthrough_behavior = "WHEN_NO_MATCH" + content_handling = "CONVERT_TO_TEXT" } ` diff --git a/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown b/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown index 4281260ad3..cbb2102bb4 100644 --- a/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown +++ b/website/source/docs/providers/aws/r/api_gateway_integration.html.markdown @@ -3,12 +3,12 @@ layout: "aws" page_title: "AWS: aws_api_gateway_integration" sidebar_current: "docs-aws-resource-api-gateway-integration" description: |- - Provides an HTTP Method Integration for an API Gateway Resource. + Provides an HTTP Method Integration for an API Gateway Integration. --- # aws\_api\_gateway\_integration -Provides an HTTP Method Integration for an API Gateway Resource. +Provides an HTTP Method Integration for an API Gateway Integration. ## Example Usage @@ -37,6 +37,10 @@ resource "aws_api_gateway_integration" "MyDemoIntegration" { http_method = "${aws_api_gateway_method.MyDemoMethod.http_method}" type = "MOCK" + request_parameters = { + "integration.request.header.X-Authorization" = "'static'" + } + # Transforms the incoming XML request to JSON request_templates { "application/xml" = < Date: Fri, 31 Mar 2017 19:45:32 +0300 Subject: [PATCH 083/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c453749ca6..4a4a87171d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS: * provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147] * provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147] * provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527] + * provider/aws: Added API Gateway integration update [GH-13249] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/triton: Move to joyent/triton-go [GH-13225] From 4501be7e5c3d7f3c3b8af6297511072c243f188e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 19:59:29 +0300 Subject: [PATCH 084/101] backend/remote-state: Add support for assume role extensions to s3 backend (#13236) Fixes: #13234 This now matches the AWS provider for the Assume Role support --- backend/remote-state/s3/backend.go | 64 ++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/backend/remote-state/s3/backend.go b/backend/remote-state/s3/backend.go index 8265d7f255..a1d9c1f9ae 100644 --- a/backend/remote-state/s3/backend.go +++ b/backend/remote-state/s3/backend.go @@ -21,101 +21,122 @@ import ( func New() backend.Backend { s := &schema.Backend{ Schema: map[string]*schema.Schema{ - "bucket": &schema.Schema{ + "bucket": { Type: schema.TypeString, Required: true, Description: "The name of the S3 bucket", }, - "key": &schema.Schema{ + "key": { Type: schema.TypeString, Required: true, Description: "The path to the state file inside the bucket", }, - "region": &schema.Schema{ + "region": { Type: schema.TypeString, Required: true, Description: "The region of the S3 bucket.", DefaultFunc: schema.EnvDefaultFunc("AWS_DEFAULT_REGION", nil), }, - "endpoint": &schema.Schema{ + "endpoint": { Type: schema.TypeString, Optional: true, Description: "A custom endpoint for the S3 API", DefaultFunc: schema.EnvDefaultFunc("AWS_S3_ENDPOINT", ""), }, - "encrypt": &schema.Schema{ + "encrypt": { Type: schema.TypeBool, Optional: true, Description: "Whether to enable server side encryption of the state file", Default: false, }, - "acl": &schema.Schema{ + "acl": { Type: schema.TypeString, Optional: true, Description: "Canned ACL to be applied to the state file", Default: "", }, - "access_key": &schema.Schema{ + "access_key": { Type: schema.TypeString, Optional: true, Description: "AWS access key", Default: "", }, - "secret_key": &schema.Schema{ + "secret_key": { Type: schema.TypeString, Optional: true, Description: "AWS secret key", Default: "", }, - "kms_key_id": &schema.Schema{ + "kms_key_id": { Type: schema.TypeString, Optional: true, Description: "The ARN of a KMS Key to use for encrypting the state", Default: "", }, - "lock_table": &schema.Schema{ + "lock_table": { Type: schema.TypeString, Optional: true, Description: "DynamoDB table for state locking", Default: "", }, - "profile": &schema.Schema{ + "profile": { Type: schema.TypeString, Optional: true, Description: "AWS profile name", Default: "", }, - "shared_credentials_file": &schema.Schema{ + "shared_credentials_file": { Type: schema.TypeString, Optional: true, Description: "Path to a shared credentials file", Default: "", }, - "token": &schema.Schema{ + "token": { Type: schema.TypeString, Optional: true, Description: "MFA token", Default: "", }, - "role_arn": &schema.Schema{ + "role_arn": { Type: schema.TypeString, Optional: true, Description: "The role to be assumed", Default: "", }, + + "session_name": { + Type: schema.TypeString, + Optional: true, + Description: "The session name to use when assuming the role.", + Default: "", + }, + + "external_id": { + Type: schema.TypeString, + Optional: true, + Description: "The external ID to use when assuming the role", + Default: "", + }, + + "assume_role_policy": { + Type: schema.TypeString, + Optional: true, + Description: "The permissions applied when assuming a role.", + Default: "", + }, }, } @@ -156,12 +177,15 @@ func (b *Backend) configure(ctx context.Context) error { var errs []error creds, err := terraformAWS.GetCredentials(&terraformAWS.Config{ - AccessKey: data.Get("access_key").(string), - SecretKey: data.Get("secret_key").(string), - Token: data.Get("token").(string), - Profile: data.Get("profile").(string), - CredsFilename: data.Get("shared_credentials_file").(string), - AssumeRoleARN: data.Get("role_arn").(string), + AccessKey: data.Get("access_key").(string), + SecretKey: data.Get("secret_key").(string), + Token: data.Get("token").(string), + Profile: data.Get("profile").(string), + CredsFilename: data.Get("shared_credentials_file").(string), + AssumeRoleARN: data.Get("role_arn").(string), + AssumeRoleSessionName: data.Get("session_name").(string), + AssumeRoleExternalID: data.Get("external_id").(string), + AssumeRolePolicy: data.Get("assume_role_policy").(string), }) if err != nil { return err From 2f0cbda43559eef9395c28045d758503fdd84dd0 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 19:59:59 +0300 Subject: [PATCH 085/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4a87171d..295ecb0b97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ FEATURES: IMPROVEMENTS: + * backend/remote-state: Add support for assume role extensions to s3 backend [GH-13236] * config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080] * helper/resource: Allow unknown "pending" states [GH-13099] * provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547] From e7c3e8df68b9e3c724d3e4ccd851c6c3f66a821e Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 20:00:47 +0300 Subject: [PATCH 086/101] provider/aws: change kinesis_firehose_delivery_stream to point to correct destination (#13251) Fixes: #13244 --- .../aws/r/kinesis_firehose_delivery_stream.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown b/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown index 1c2cde2d4f..57dca62e13 100644 --- a/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown +++ b/website/source/docs/providers/aws/r/kinesis_firehose_delivery_stream.html.markdown @@ -97,7 +97,7 @@ resource "aws_elasticsearch_domain" "test_cluster" { resource "aws_kinesis_firehose_delivery_stream" "test_stream" { name = "terraform-kinesis-firehose-test-stream" - destination = "redshift" + destination = "elasticsearch" s3_configuration { role_arn = "${aws_iam_role.firehose_role.arn}" From d25c3104681c18044895b47ed44a87dc71d1e34e Mon Sep 17 00:00:00 2001 From: Joshua Spence Date: Sat, 1 Apr 2017 04:22:57 +1100 Subject: [PATCH 087/101] Add `name_prefix` to RDS resources (#13232) Adds `name_prefix` (or, in some cases, `identifier_prefix`) support to all AWS RDS resources. --- .../providers/aws/resource_aws_db_instance.go | 24 +++- .../aws/resource_aws_db_instance_test.go | 79 ++++++++++- .../aws/resource_aws_db_option_group.go | 55 ++++---- .../aws/resource_aws_db_option_group_test.go | 124 ++++++++++++----- .../aws/resource_aws_db_parameter_group.go | 24 +++- .../resource_aws_db_parameter_group_test.go | 52 +++++++ .../aws/resource_aws_db_subnet_group.go | 42 +++--- .../aws/resource_aws_db_subnet_group_test.go | 116 +++++++++++----- .../providers/aws/resource_aws_rds_cluster.go | 26 +++- .../aws/resource_aws_rds_cluster_instance.go | 21 ++- .../resource_aws_rds_cluster_instance_test.go | 119 ++++++++++++++++ ...esource_aws_rds_cluster_parameter_group.go | 24 +++- ...ce_aws_rds_cluster_parameter_group_test.go | 51 +++++++ .../aws/resource_aws_rds_cluster_test.go | 105 ++++++++++++++ builtin/providers/aws/validators.go | 115 +++++++++++++++- builtin/providers/aws/validators_test.go | 128 ++++++++++++++++++ .../providers/aws/r/db_instance.html.markdown | 5 +- .../aws/r/db_option_group.html.markdown | 7 +- .../aws/r/db_parameter_group.html.markdown | 5 +- .../aws/r/db_subnet_group.html.markdown | 5 +- .../providers/aws/r/rds_cluster.html.markdown | 4 +- .../aws/r/rds_cluster_instance.html.markdown | 4 +- .../r/rds_cluster_parameter_group.markdown | 7 +- 23 files changed, 985 insertions(+), 157 deletions(-) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index 192ccab97d..00409230db 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -101,11 +101,19 @@ func resourceAwsDbInstance() *schema.Resource { }, "identifier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"identifier_prefix"}, + ValidateFunc: validateRdsIdentifier, + }, + "identifier_prefix": { Type: schema.TypeString, Optional: true, Computed: true, ForceNew: true, - ValidateFunc: validateRdsId, + ValidateFunc: validateRdsIdentifierPrefix, }, "instance_class": { @@ -336,10 +344,16 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error conn := meta.(*AWSClient).rdsconn tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) - identifier := d.Get("identifier").(string) - // Generate a unique ID for the user - if identifier == "" { - identifier = resource.PrefixedUniqueId("tf-") + var identifier string + if v, ok := d.GetOk("identifier"); ok { + identifier = v.(string) + } else { + if v, ok := d.GetOk("identifier_prefix"); ok { + identifier = resource.PrefixedUniqueId(v.(string)) + } else { + identifier = resource.UniqueId() + } + // SQL Server identifier size is max 15 chars, so truncate if engine := d.Get("engine").(string); engine != "" { if strings.Contains(strings.ToLower(engine), "sqlserver") { diff --git a/builtin/providers/aws/resource_aws_db_instance_test.go b/builtin/providers/aws/resource_aws_db_instance_test.go index 56f8905327..6c8d451ca0 100644 --- a/builtin/providers/aws/resource_aws_db_instance_test.go +++ b/builtin/providers/aws/resource_aws_db_instance_test.go @@ -53,6 +53,46 @@ func TestAccAWSDBInstance_basic(t *testing.T) { }) } +func TestAccAWSDBInstance_namePrefix(t *testing.T) { + var v rds.DBInstance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBInstanceConfig_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v), + testAccCheckAWSDBInstanceAttributes(&v), + resource.TestMatchResourceAttr( + "aws_db_instance.test", "identifier", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSDBInstance_generatedName(t *testing.T) { + var v rds.DBInstance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBInstanceConfig_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBInstanceExists("aws_db_instance.test", &v), + testAccCheckAWSDBInstanceAttributes(&v), + ), + }, + }, + }) +} + func TestAccAWSDBInstance_kmsKey(t *testing.T) { var v rds.DBInstance keyRegex := regexp.MustCompile("^arn:aws:kms:") @@ -613,8 +653,8 @@ resource "aws_db_instance" "bar" { username = "foo" - # Maintenance Window is stored in lower case in the API, though not strictly - # documented. Terraform will downcase this to match (as opposed to throw a + # Maintenance Window is stored in lower case in the API, though not strictly + # documented. Terraform will downcase this to match (as opposed to throw a # validation error). maintenance_window = "Fri:09:00-Fri:09:30" skip_final_snapshot = true @@ -628,6 +668,39 @@ resource "aws_db_instance" "bar" { } }` +const testAccAWSDBInstanceConfig_namePrefix = ` +resource "aws_db_instance" "test" { + allocated_storage = 10 + engine = "MySQL" + identifier_prefix = "tf-test-" + instance_class = "db.t1.micro" + password = "password" + username = "root" + security_group_names = ["default"] + publicly_accessible = true + skip_final_snapshot = true + + timeouts { + create = "30m" + } +}` + +const testAccAWSDBInstanceConfig_generatedName = ` +resource "aws_db_instance" "test" { + allocated_storage = 10 + engine = "MySQL" + instance_class = "db.t1.micro" + password = "password" + username = "root" + security_group_names = ["default"] + publicly_accessible = true + skip_final_snapshot = true + + timeouts { + create = "30m" + } +}` + var testAccAWSDBInstanceConfigKmsKeyId = ` resource "aws_kms_key" "foo" { description = "Terraform acc test %s" @@ -720,7 +793,7 @@ func testAccReplicaInstanceConfig(val int) string { parameter_group_name = "default.mysql5.6" } - + resource "aws_db_instance" "replica" { identifier = "tf-replica-db-%d" backup_retention_period = 0 diff --git a/builtin/providers/aws/resource_aws_db_option_group.go b/builtin/providers/aws/resource_aws_db_option_group.go index 5c68e7bd38..e8ad1ac99f 100644 --- a/builtin/providers/aws/resource_aws_db_option_group.go +++ b/builtin/providers/aws/resource_aws_db_option_group.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "log" - "regexp" "time" "github.com/aws/aws-sdk-go/aws" @@ -31,10 +30,19 @@ func resourceAwsDbOptionGroup() *schema.Resource { Computed: true, }, "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateDbOptionGroupName, + }, + "name_prefix": &schema.Schema{ Type: schema.TypeString, + Optional: true, + Computed: true, ForceNew: true, - Required: true, - ValidateFunc: validateDbOptionGroupName, + ValidateFunc: validateDbOptionGroupNamePrefix, }, "engine_name": &schema.Schema{ Type: schema.TypeString, @@ -48,8 +56,9 @@ func resourceAwsDbOptionGroup() *schema.Resource { }, "option_group_description": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, + Default: "Managed by Terraform", }, "option": &schema.Schema{ @@ -107,11 +116,20 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er rdsconn := meta.(*AWSClient).rdsconn tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) + var groupName string + if v, ok := d.GetOk("name"); ok { + groupName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + groupName = resource.PrefixedUniqueId(v.(string)) + } else { + groupName = resource.UniqueId() + } + createOpts := &rds.CreateOptionGroupInput{ EngineName: aws.String(d.Get("engine_name").(string)), MajorEngineVersion: aws.String(d.Get("major_engine_version").(string)), OptionGroupDescription: aws.String(d.Get("option_group_description").(string)), - OptionGroupName: aws.String(d.Get("name").(string)), + OptionGroupName: aws.String(groupName), Tags: tags, } @@ -121,7 +139,7 @@ func resourceAwsDbOptionGroupCreate(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error creating DB Option Group: %s", err) } - d.SetId(d.Get("name").(string)) + d.SetId(groupName) log.Printf("[INFO] DB Option Group ID: %s", d.Id()) return resourceAwsDbOptionGroupUpdate(d, meta) @@ -343,28 +361,3 @@ func buildRDSOptionGroupARN(identifier, partition, accountid, region string) (st arn := fmt.Sprintf("arn:%s:rds:%s:%s:og:%s", partition, region, accountid, identifier) return arn, nil } - -func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^[a-z]`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "first character of %q must be a letter", k)) - } - if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "only alphanumeric characters and hyphens allowed in %q", k)) - } - if regexp.MustCompile(`--`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q cannot contain two consecutive hyphens", k)) - } - if regexp.MustCompile(`-$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q cannot end with a hyphen", k)) - } - if len(value) > 255 { - errors = append(errors, fmt.Errorf( - "%q cannot be greater than 255 characters", k)) - } - return -} diff --git a/builtin/providers/aws/resource_aws_db_option_group_test.go b/builtin/providers/aws/resource_aws_db_option_group_test.go index 5a3215b042..3ee5f8197f 100644 --- a/builtin/providers/aws/resource_aws_db_option_group_test.go +++ b/builtin/providers/aws/resource_aws_db_option_group_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -34,6 +35,66 @@ func TestAccAWSDBOptionGroup_basic(t *testing.T) { }) } +func TestAccAWSDBOptionGroup_namePrefix(t *testing.T) { + var v rds.OptionGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBOptionGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSDBOptionGroup_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v), + testAccCheckAWSDBOptionGroupAttributes(&v), + resource.TestMatchResourceAttr( + "aws_db_option_group.test", "name", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSDBOptionGroup_generatedName(t *testing.T) { + var v rds.OptionGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBOptionGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSDBOptionGroup_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v), + testAccCheckAWSDBOptionGroupAttributes(&v), + ), + }, + }, + }) +} + +func TestAccAWSDBOptionGroup_defaultDescription(t *testing.T) { + var v rds.OptionGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBOptionGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSDBOptionGroup_defaultDescription(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBOptionGroupExists("aws_db_option_group.test", &v), + resource.TestCheckResourceAttr( + "aws_db_option_group.test", "option_group_description", "Managed by Terraform"), + ), + }, + }, + }) +} + func TestAccAWSDBOptionGroup_basicDestroyWithInstance(t *testing.T) { rName := fmt.Sprintf("option-group-test-terraform-%s", acctest.RandString(5)) @@ -160,42 +221,6 @@ func testAccCheckAWSDBOptionGroupAttributes(v *rds.OptionGroup) resource.TestChe } } -func TestResourceAWSDBOptionGroupName_validation(t *testing.T) { - cases := []struct { - Value string - ErrCount int - }{ - { - Value: "testing123!", - ErrCount: 1, - }, - { - Value: "1testing123", - ErrCount: 1, - }, - { - Value: "testing--123", - ErrCount: 1, - }, - { - Value: "testing123-", - ErrCount: 1, - }, - { - Value: randomString(256), - ErrCount: 1, - }, - } - - for _, tc := range cases { - _, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name") - - if len(errors) != tc.ErrCount { - t.Fatalf("Expected the DB Option Group Name to trigger a validation error") - } - } -} - func testAccCheckAWSDBOptionGroupExists(n string, v *rds.OptionGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -387,3 +412,30 @@ resource "aws_db_option_group" "bar" { } `, r) } + +const testAccAWSDBOptionGroup_namePrefix = ` +resource "aws_db_option_group" "test" { + name_prefix = "tf-test-" + option_group_description = "Test option group for terraform" + engine_name = "mysql" + major_engine_version = "5.6" +} +` + +const testAccAWSDBOptionGroup_generatedName = ` +resource "aws_db_option_group" "test" { + option_group_description = "Test option group for terraform" + engine_name = "mysql" + major_engine_version = "5.6" +} +` + +func testAccAWSDBOptionGroup_defaultDescription(n int) string { + return fmt.Sprintf(` +resource "aws_db_option_group" "test" { + name = "tf-test-%d" + engine_name = "mysql" + major_engine_version = "5.6" +} +`, n) +} diff --git a/builtin/providers/aws/resource_aws_db_parameter_group.go b/builtin/providers/aws/resource_aws_db_parameter_group.go index b182827128..d5e943fd6f 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group.go @@ -32,10 +32,19 @@ func resourceAwsDbParameterGroup() *schema.Resource { Computed: true, }, "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateDbParamGroupName, + }, + "name_prefix": &schema.Schema{ Type: schema.TypeString, + Optional: true, + Computed: true, ForceNew: true, - Required: true, - ValidateFunc: validateDbParamGroupName, + ValidateFunc: validateDbParamGroupNamePrefix, }, "family": &schema.Schema{ Type: schema.TypeString, @@ -81,8 +90,17 @@ func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) rdsconn := meta.(*AWSClient).rdsconn tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) + var groupName string + if v, ok := d.GetOk("name"); ok { + groupName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + groupName = resource.PrefixedUniqueId(v.(string)) + } else { + groupName = resource.UniqueId() + } + createOpts := rds.CreateDBParameterGroupInput{ - DBParameterGroupName: aws.String(d.Get("name").(string)), + DBParameterGroupName: aws.String(groupName), DBParameterGroupFamily: aws.String(d.Get("family").(string)), Description: aws.String(d.Get("description").(string)), Tags: tags, diff --git a/builtin/providers/aws/resource_aws_db_parameter_group_test.go b/builtin/providers/aws/resource_aws_db_parameter_group_test.go index 75db4f77da..b8e4e56c42 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group_test.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "math/rand" + "regexp" "testing" "time" @@ -290,6 +291,44 @@ func TestAccAWSDBParameterGroup_basic(t *testing.T) { }) } +func TestAccAWSDBParameterGroup_namePrefix(t *testing.T) { + var v rds.DBParameterGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBParameterGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBParameterGroupConfig_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v), + resource.TestMatchResourceAttr( + "aws_db_parameter_group.test", "name", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSDBParameterGroup_generatedName(t *testing.T) { + var v rds.DBParameterGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBParameterGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBParameterGroupConfig_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBParameterGroupExists("aws_db_parameter_group.test", &v), + ), + }, + }, + }) +} + func TestAccAWSDBParameterGroup_withApplyMethod(t *testing.T) { var v rds.DBParameterGroup @@ -671,3 +710,16 @@ resource "aws_db_parameter_group" "large" { parameter { name = "tx_isolation" value = "REPEATABLE-READ" } }`, n) } + +const testAccDBParameterGroupConfig_namePrefix = ` +resource "aws_db_parameter_group" "test" { + name_prefix = "tf-test-" + family = "mysql5.6" +} +` + +const testAccDBParameterGroupConfig_generatedName = ` +resource "aws_db_parameter_group" "test" { + family = "mysql5.6" +} +` diff --git a/builtin/providers/aws/resource_aws_db_subnet_group.go b/builtin/providers/aws/resource_aws_db_subnet_group.go index 9c1c56199f..c4e437beeb 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group.go @@ -3,7 +3,6 @@ package aws import ( "fmt" "log" - "regexp" "strings" "time" @@ -31,10 +30,19 @@ func resourceAwsDbSubnetGroup() *schema.Resource { }, "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateDbSubnetGroupName, + }, + "name_prefix": &schema.Schema{ Type: schema.TypeString, + Optional: true, + Computed: true, ForceNew: true, - Required: true, - ValidateFunc: validateSubnetGroupName, + ValidateFunc: validateDbSubnetGroupNamePrefix, }, "description": &schema.Schema{ @@ -65,8 +73,17 @@ func resourceAwsDbSubnetGroupCreate(d *schema.ResourceData, meta interface{}) er subnetIds[i] = aws.String(subnetId.(string)) } + var groupName string + if v, ok := d.GetOk("name"); ok { + groupName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + groupName = resource.PrefixedUniqueId(v.(string)) + } else { + groupName = resource.UniqueId() + } + createOpts := rds.CreateDBSubnetGroupInput{ - DBSubnetGroupName: aws.String(d.Get("name").(string)), + DBSubnetGroupName: aws.String(groupName), DBSubnetGroupDescription: aws.String(d.Get("description").(string)), SubnetIds: subnetIds, Tags: tags, @@ -238,20 +255,3 @@ func buildRDSsubgrpARN(identifier, partition, accountid, region string) (string, return arn, nil } - -func validateSubnetGroupName(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k)) - } - if len(value) > 255 { - errors = append(errors, fmt.Errorf( - "%q cannot be longer than 255 characters", k)) - } - if regexp.MustCompile(`(?i)^default$`).MatchString(value) { - errors = append(errors, fmt.Errorf( - "%q is not allowed as %q", "Default", k)) - } - return -} diff --git a/builtin/providers/aws/resource_aws_db_subnet_group_test.go b/builtin/providers/aws/resource_aws_db_subnet_group_test.go index 434ae1728e..70d27c5dbb 100644 --- a/builtin/providers/aws/resource_aws_db_subnet_group_test.go +++ b/builtin/providers/aws/resource_aws_db_subnet_group_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -43,6 +44,46 @@ func TestAccAWSDBSubnetGroup_basic(t *testing.T) { }) } +func TestAccAWSDBSubnetGroup_namePrefix(t *testing.T) { + var v rds.DBSubnetGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDBSubnetGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBSubnetGroupConfig_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBSubnetGroupExists( + "aws_db_subnet_group.test", &v), + resource.TestMatchResourceAttr( + "aws_db_subnet_group.test", "name", regexp.MustCompile("^tf_test-")), + ), + }, + }, + }) +} + +func TestAccAWSDBSubnetGroup_generatedName(t *testing.T) { + var v rds.DBSubnetGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDBSubnetGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBSubnetGroupConfig_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBSubnetGroupExists( + "aws_db_subnet_group.test", &v), + ), + }, + }, + }) +} + // Regression test for https://github.com/hashicorp/terraform/issues/2603 and // https://github.com/hashicorp/terraform/issues/2664 func TestAccAWSDBSubnetGroup_withUndocumentedCharacters(t *testing.T) { @@ -105,38 +146,6 @@ func TestAccAWSDBSubnetGroup_updateDescription(t *testing.T) { }) } -func TestResourceAWSDBSubnetGroupNameValidation(t *testing.T) { - cases := []struct { - Value string - ErrCount int - }{ - { - Value: "tEsting", - ErrCount: 1, - }, - { - Value: "testing?", - ErrCount: 1, - }, - { - Value: "default", - ErrCount: 1, - }, - { - Value: randomString(300), - ErrCount: 1, - }, - } - - for _, tc := range cases { - _, errors := validateSubnetGroupName(tc.Value, "aws_db_subnet_group") - - if len(errors) != tc.ErrCount { - t.Fatalf("Expected the DB Subnet Group name to trigger a validation error") - } - } -} - func testAccCheckDBSubnetGroupDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).rdsconn @@ -263,6 +272,49 @@ resource "aws_db_subnet_group" "foo" { }`, rName) } +const testAccDBSubnetGroupConfig_namePrefix = ` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.2.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + name_prefix = "tf_test-" + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +}` + +const testAccDBSubnetGroupConfig_generatedName = ` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.2.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +}` + const testAccDBSubnetGroupConfig_withUnderscoresAndPeriodsAndSpaces = ` resource "aws_vpc" "main" { cidr_block = "192.168.0.0/16" diff --git a/builtin/providers/aws/resource_aws_rds_cluster.go b/builtin/providers/aws/resource_aws_rds_cluster.go index 7461b880c9..c331296549 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster.go +++ b/builtin/providers/aws/resource_aws_rds_cluster.go @@ -36,10 +36,19 @@ func resourceAwsRDSCluster() *schema.Resource { }, "cluster_identifier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"cluster_identifier_prefix"}, + ValidateFunc: validateRdsIdentifier, + }, + "cluster_identifier_prefix": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, - ValidateFunc: validateRdsId, + ValidateFunc: validateRdsIdentifierPrefix, }, "cluster_members": { @@ -225,6 +234,19 @@ func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error conn := meta.(*AWSClient).rdsconn tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) + var identifier string + if v, ok := d.GetOk("cluster_identifier"); ok { + identifier = v.(string) + } else { + if v, ok := d.GetOk("cluster_identifier_prefix"); ok { + identifier = resource.PrefixedUniqueId(v.(string)) + } else { + identifier = resource.PrefixedUniqueId("tf-") + } + + d.Set("cluster_identifier", identifier) + } + if _, ok := d.GetOk("snapshot_identifier"); ok { opts := rds.RestoreDBClusterFromSnapshotInput{ DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)), diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance.go b/builtin/providers/aws/resource_aws_rds_cluster_instance.go index 36f1116286..2caca45732 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance.go @@ -24,10 +24,19 @@ func resourceAwsRDSClusterInstance() *schema.Resource { Schema: map[string]*schema.Schema{ "identifier": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"identifier_prefix"}, + ValidateFunc: validateRdsIdentifier, + }, + "identifier_prefix": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, - ValidateFunc: validateRdsId, + ValidateFunc: validateRdsIdentifierPrefix, }, "db_subnet_group_name": { @@ -162,10 +171,14 @@ func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{ createOpts.DBParameterGroupName = aws.String(attr.(string)) } - if v := d.Get("identifier").(string); v != "" { - createOpts.DBInstanceIdentifier = aws.String(v) + if v, ok := d.GetOk("identifier"); ok { + createOpts.DBInstanceIdentifier = aws.String(v.(string)) } else { - createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId()) + if v, ok := d.GetOk("identifier_prefix"); ok { + createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId(v.(string))) + } else { + createOpts.DBInstanceIdentifier = aws.String(resource.PrefixedUniqueId("tf-")) + } } if attr, ok := d.GetOk("db_subnet_group_name"); ok { diff --git a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go index b1e66ea7e2..df1a5d644a 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_instance_test.go @@ -46,6 +46,48 @@ func TestAccAWSRDSClusterInstance_basic(t *testing.T) { }) } +func TestAccAWSRDSClusterInstance_namePrefix(t *testing.T) { + var v rds.DBInstance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSClusterInstanceConfig_namePrefix(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v), + testAccCheckAWSDBClusterInstanceAttributes(&v), + resource.TestMatchResourceAttr( + "aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-cluster-instance-")), + ), + }, + }, + }) +} + +func TestAccAWSRDSClusterInstance_generatedName(t *testing.T) { + var v rds.DBInstance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSClusterInstanceConfig_generatedName(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.test", &v), + testAccCheckAWSDBClusterInstanceAttributes(&v), + resource.TestMatchResourceAttr( + "aws_rds_cluster_instance.test", "identifier", regexp.MustCompile("^tf-")), + ), + }, + }, + }) +} + func TestAccAWSRDSClusterInstance_kmsKey(t *testing.T) { var v rds.DBInstance keyRegex := regexp.MustCompile("^arn:aws:kms:") @@ -256,6 +298,83 @@ resource "aws_db_parameter_group" "bar" { `, n, n, n) } +func testAccAWSClusterInstanceConfig_namePrefix(n int) string { + return fmt.Sprintf(` +resource "aws_rds_cluster_instance" "test" { + identifier_prefix = "tf-cluster-instance-" + cluster_identifier = "${aws_rds_cluster.test.id}" + instance_class = "db.r3.large" +} + +resource "aws_rds_cluster" "test" { + cluster_identifier = "tf-aurora-cluster-%d" + master_username = "root" + master_password = "password" + db_subnet_group_name = "${aws_db_subnet_group.test.name}" + skip_final_snapshot = true +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.0.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + name = "tf-test-%d" + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +} +`, n, n) +} + +func testAccAWSClusterInstanceConfig_generatedName(n int) string { + return fmt.Sprintf(` +resource "aws_rds_cluster_instance" "test" { + cluster_identifier = "${aws_rds_cluster.test.id}" + instance_class = "db.r3.large" +} + +resource "aws_rds_cluster" "test" { + cluster_identifier = "tf-aurora-cluster-%d" + master_username = "root" + master_password = "password" + db_subnet_group_name = "${aws_db_subnet_group.test.name}" + skip_final_snapshot = true +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.0.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + name = "tf-test-%d" + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +} +`, n, n) +} + func testAccAWSClusterInstanceConfigKmsKey(n int) string { return fmt.Sprintf(` diff --git a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go index 62b0d497bf..61cb20f01d 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group.go @@ -29,10 +29,19 @@ func resourceAwsRDSClusterParameterGroup() *schema.Resource { Computed: true, }, "name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateDbParamGroupName, + }, + "name_prefix": &schema.Schema{ Type: schema.TypeString, + Optional: true, + Computed: true, ForceNew: true, - Required: true, - ValidateFunc: validateDbParamGroupName, + ValidateFunc: validateDbParamGroupNamePrefix, }, "family": &schema.Schema{ Type: schema.TypeString, @@ -86,8 +95,17 @@ func resourceAwsRDSClusterParameterGroupCreate(d *schema.ResourceData, meta inte rdsconn := meta.(*AWSClient).rdsconn tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{})) + var groupName string + if v, ok := d.GetOk("name"); ok { + groupName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + groupName = resource.PrefixedUniqueId(v.(string)) + } else { + groupName = resource.UniqueId() + } + createOpts := rds.CreateDBClusterParameterGroupInput{ - DBClusterParameterGroupName: aws.String(d.Get("name").(string)), + DBClusterParameterGroupName: aws.String(groupName), DBParameterGroupFamily: aws.String(d.Get("family").(string)), Description: aws.String(d.Get("description").(string)), Tags: tags, diff --git a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go index f2a526d2e0..231fdf44c2 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_parameter_group_test.go @@ -3,6 +3,7 @@ package aws import ( "errors" "fmt" + "regexp" "testing" "time" @@ -90,6 +91,44 @@ func TestAccAWSDBClusterParameterGroup_basic(t *testing.T) { }) } +func TestAccAWSDBClusterParameterGroup_namePrefix(t *testing.T) { + var v rds.DBClusterParameterGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBClusterParameterGroupConfig_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v), + resource.TestMatchResourceAttr( + "aws_rds_cluster_parameter_group.test", "name", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSDBClusterParameterGroup_generatedName(t *testing.T) { + var v rds.DBClusterParameterGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDBClusterParameterGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSDBClusterParameterGroupConfig_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSDBClusterParameterGroupExists("aws_rds_cluster_parameter_group.test", &v), + ), + }, + }, + }) +} + func TestAccAWSDBClusterParameterGroup_disappears(t *testing.T) { var v rds.DBClusterParameterGroup @@ -365,3 +404,15 @@ func testAccAWSDBClusterParameterGroupOnlyConfig(name string) string { family = "aurora5.6" }`, name) } + +const testAccAWSDBClusterParameterGroupConfig_namePrefix = ` +resource "aws_rds_cluster_parameter_group" "test" { + name_prefix = "tf-test-" + family = "aurora5.6" +} +` +const testAccAWSDBClusterParameterGroupConfig_generatedName = ` +resource "aws_rds_cluster_parameter_group" "test" { + family = "aurora5.6" +} +` diff --git a/builtin/providers/aws/resource_aws_rds_cluster_test.go b/builtin/providers/aws/resource_aws_rds_cluster_test.go index c18a6431aa..3774385a99 100644 --- a/builtin/providers/aws/resource_aws_rds_cluster_test.go +++ b/builtin/providers/aws/resource_aws_rds_cluster_test.go @@ -40,6 +40,46 @@ func TestAccAWSRDSCluster_basic(t *testing.T) { }) } +func TestAccAWSRDSCluster_namePrefix(t *testing.T) { + var v rds.DBCluster + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSClusterConfig_namePrefix(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists("aws_rds_cluster.test", &v), + resource.TestMatchResourceAttr( + "aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSRDSCluster_generatedName(t *testing.T) { + var v rds.DBCluster + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSClusterConfig_generatedName(acctest.RandInt()), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSClusterExists("aws_rds_cluster.test", &v), + resource.TestMatchResourceAttr( + "aws_rds_cluster.test", "cluster_identifier", regexp.MustCompile("^tf-")), + ), + }, + }, + }) +} + func TestAccAWSRDSCluster_takeFinalSnapshot(t *testing.T) { var v rds.DBCluster rInt := acctest.RandInt() @@ -322,6 +362,71 @@ resource "aws_rds_cluster" "default" { }`, n) } +func testAccAWSClusterConfig_namePrefix(n int) string { + return fmt.Sprintf(` +resource "aws_rds_cluster" "test" { + cluster_identifier_prefix = "tf-test-" + master_username = "root" + master_password = "password" + db_subnet_group_name = "${aws_db_subnet_group.test.name}" + skip_final_snapshot = true +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.0.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + name = "tf-test-%d" + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +} +`, n) +} + +func testAccAWSClusterConfig_generatedName(n int) string { + return fmt.Sprintf(` +resource "aws_rds_cluster" "test" { + master_username = "root" + master_password = "password" + db_subnet_group_name = "${aws_db_subnet_group.test.name}" + skip_final_snapshot = true +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "a" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.0.0/24" + availability_zone = "us-west-2a" +} + +resource "aws_subnet" "b" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "us-west-2b" +} + +resource "aws_db_subnet_group" "test" { + name = "tf-test-%d" + subnet_ids = ["${aws_subnet.a.id}", "${aws_subnet.b.id}"] +} +`, n) +} + func testAccAWSClusterConfigWithFinalSnapshot(n int) string { return fmt.Sprintf(` resource "aws_rds_cluster" "default" { diff --git a/builtin/providers/aws/validators.go b/builtin/providers/aws/validators.go index a8f9c66cfc..7ff0e6f389 100644 --- a/builtin/providers/aws/validators.go +++ b/builtin/providers/aws/validators.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -func validateRdsId(v interface{}, k string) (ws []string, errors []error) { +func validateRdsIdentifier(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { errors = append(errors, fmt.Errorf( @@ -33,6 +33,23 @@ func validateRdsId(v interface{}, k string) (ws []string, errors []error) { return } +func validateRdsIdentifierPrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters and hyphens allowed in %q", k)) + } + if !regexp.MustCompile(`^[a-z]`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "first character of %q must be a letter", k)) + } + if regexp.MustCompile(`--`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot contain two consecutive hyphens", k)) + } + return +} + func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if (len(value) < 1) || (len(value) > 20) { @@ -103,7 +120,27 @@ func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []er "%q cannot be greater than 255 characters", k)) } return +} +func validateDbParamGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters and hyphens allowed in %q", k)) + } + if !regexp.MustCompile(`^[a-z]`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "first character of %q must be a letter", k)) + } + if regexp.MustCompile(`--`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot contain two consecutive hyphens", k)) + } + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 226 characters", k)) + } + return } func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) { @@ -1041,3 +1078,79 @@ func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors return } + +func validateDbSubnetGroupName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k)) + } + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 255 characters", k)) + } + if regexp.MustCompile(`(?i)^default$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q is not allowed as %q", "Default", k)) + } + return +} + +func validateDbSubnetGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[ .0-9a-z-_]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only lowercase alphanumeric characters, hyphens, underscores, periods, and spaces allowed in %q", k)) + } + if len(value) > 229 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 229 characters", k)) + } + return +} + +func validateDbOptionGroupName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[a-z]`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "first character of %q must be a letter", k)) + } + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + if regexp.MustCompile(`--`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot contain two consecutive hyphens", k)) + } + if regexp.MustCompile(`-$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot end with a hyphen", k)) + } + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 255 characters", k)) + } + return +} + +func validateDbOptionGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[a-z]`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "first character of %q must be a letter", k)) + } + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + if regexp.MustCompile(`--`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q cannot contain two consecutive hyphens", k)) + } + if len(value) > 229 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 229 characters", k)) + } + return +} diff --git a/builtin/providers/aws/validators_test.go b/builtin/providers/aws/validators_test.go index 0c37308fe3..7fe451a878 100644 --- a/builtin/providers/aws/validators_test.go +++ b/builtin/providers/aws/validators_test.go @@ -1785,3 +1785,131 @@ func TestValidateElbNamePrefix(t *testing.T) { } } } + +func TestValidateDbSubnetGroupName(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "tEsting", + ErrCount: 1, + }, + { + Value: "testing?", + ErrCount: 1, + }, + { + Value: "default", + ErrCount: 1, + }, + { + Value: randomString(300), + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateDbSubnetGroupName(tc.Value, "aws_db_subnet_group") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the DB Subnet Group name to trigger a validation error") + } + } +} + +func TestValidateDbSubnetGroupNamePrefix(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "tEsting", + ErrCount: 1, + }, + { + Value: "testing?", + ErrCount: 1, + }, + { + Value: randomString(230), + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateDbSubnetGroupNamePrefix(tc.Value, "aws_db_subnet_group") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the DB Subnet Group name prefix to trigger a validation error") + } + } +} + +func TestValidateDbOptionGroupName(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "testing123!", + ErrCount: 1, + }, + { + Value: "1testing123", + ErrCount: 1, + }, + { + Value: "testing--123", + ErrCount: 1, + }, + { + Value: "testing123-", + ErrCount: 1, + }, + { + Value: randomString(256), + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateDbOptionGroupName(tc.Value, "aws_db_option_group_name") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the DB Option Group Name to trigger a validation error") + } + } +} + +func TestValidateDbOptionGroupNamePrefix(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + { + Value: "testing123!", + ErrCount: 1, + }, + { + Value: "1testing123", + ErrCount: 1, + }, + { + Value: "testing--123", + ErrCount: 1, + }, + { + Value: randomString(230), + ErrCount: 1, + }, + } + + for _, tc := range cases { + _, errors := validateDbOptionGroupNamePrefix(tc.Value, "aws_db_option_group_name") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the DB Option Group name prefix to trigger a validation error") + } + } +} diff --git a/website/source/docs/providers/aws/r/db_instance.html.markdown b/website/source/docs/providers/aws/r/db_instance.html.markdown index e491e37842..7e60444257 100644 --- a/website/source/docs/providers/aws/r/db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/db_instance.html.markdown @@ -55,7 +55,8 @@ The following arguments are supported: * `allocated_storage` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The allocated storage in gigabytes. * `engine` - (Required unless a `snapshot_identifier` or `replicate_source_db` is provided) The database engine to use. * `engine_version` - (Optional) The engine version to use. -* `identifier` - (Optional) The name of the RDS instance, if omitted, Terraform will assign a random, unique name +* `identifier` - (Optional, Forces new resource) The name of the RDS instance, if omitted, Terraform will assign a random, unique identifier. +* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`. * `instance_class` - (Required) The instance type of the RDS instance. * `storage_type` - (Optional) One of "standard" (magnetic), "gp2" (general purpose SSD), or "io1" (provisioned IOPS SSD). The default is "io1" if @@ -156,7 +157,7 @@ On Oracle instances the following is exported additionally: - `create` - (Default `40 minutes`) Used for Creating Instances, Replicas, and restoring from Snapshots -- `update` - (Default `80 minutes`) Used for Database modifications +- `update` - (Default `80 minutes`) Used for Database modifications - `delete` - (Default `40 minutes`) Used for destroying databases. This includes the time required to take snapshots diff --git a/website/source/docs/providers/aws/r/db_option_group.html.markdown b/website/source/docs/providers/aws/r/db_option_group.html.markdown index faf7b351c0..ad4c4d5d4b 100644 --- a/website/source/docs/providers/aws/r/db_option_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_option_group.html.markdown @@ -38,8 +38,9 @@ resource "aws_db_option_group" "bar" { The following arguments are supported: -* `name` - (Required) The name of the Option group to be created. -* `option_group_description` - (Required) The description of the option group. +* `name` - (Optional, Forces new resource) The name of the option group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. +* `option_group_description` - (Optional) The description of the option group. Defaults to "Managed by Terraform". * `engine_name` - (Required) Specifies the name of the engine that this option group should be associated with.. * `major_engine_version` - (Required) Specifies the major version of the engine that this option group should be associated with. * `option` - (Optional) A list of Options to apply. @@ -70,4 +71,4 @@ DB Option groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_option_group.bar mysql-option-group -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/db_parameter_group.html.markdown b/website/source/docs/providers/aws/r/db_parameter_group.html.markdown index 271325034c..2e4d362d2d 100644 --- a/website/source/docs/providers/aws/r/db_parameter_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_parameter_group.html.markdown @@ -31,7 +31,8 @@ resource "aws_db_parameter_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB parameter group. +* `name` - (Optional, Forces new resource) The name of the DB parameter group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `family` - (Required) The family of the DB parameter group. * `description` - (Optional) The description of the DB parameter group. Defaults to "Managed by Terraform". * `parameter` - (Optional) A list of DB parameters to apply. @@ -58,4 +59,4 @@ DB Parameter groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_parameter_group.rds_pg rds-pg -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/db_subnet_group.html.markdown b/website/source/docs/providers/aws/r/db_subnet_group.html.markdown index a97f22c051..bee5eee527 100644 --- a/website/source/docs/providers/aws/r/db_subnet_group.html.markdown +++ b/website/source/docs/providers/aws/r/db_subnet_group.html.markdown @@ -27,7 +27,8 @@ resource "aws_db_subnet_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB subnet group. +* `name` - (Optional, Forces new resource) The name of the DB subnet group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `description` - (Optional) The description of the DB subnet group. Defaults to "Managed by Terraform". * `subnet_ids` - (Required) A list of VPC subnet IDs. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -46,4 +47,4 @@ DB Subnet groups can be imported using the `name`, e.g. ``` $ terraform import aws_db_subnet_group.default production-subnet-group -``` \ No newline at end of file +``` diff --git a/website/source/docs/providers/aws/r/rds_cluster.html.markdown b/website/source/docs/providers/aws/r/rds_cluster.html.markdown index b40cbab402..6139824f19 100644 --- a/website/source/docs/providers/aws/r/rds_cluster.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster.html.markdown @@ -53,8 +53,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co The following arguments are supported: -* `cluster_identifier` - (Required) The Cluster Identifier. Must be a lower case -string. +* `cluster_identifier` - (Optional, Forces new resources) The cluster identifier. If omitted, Terraform will assign a random, unique identifier. +* `cluster_identifier_prefix` - (Optional, Forces new resource) Creates a unique cluster identifier beginning with the specified prefix. Conflicts with `cluster_identifer`. * `database_name` - (Optional) The name for your database of up to 8 alpha-numeric characters. If you do not provide a name, Amazon RDS will not create a database in the DB cluster you are creating diff --git a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown index d5196d7338..031972d4b1 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_instance.html.markdown @@ -47,8 +47,8 @@ the [AWS official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/Co The following arguments are supported: -* `identifier` - (Optional) The Instance Identifier. Must be a lower case -string. If omitted, a unique identifier will be generated. +* `identifier` - (Optional, Forces new resource) The indentifier for the RDS instance, if omitted, Terraform will assign a random, unique identifier. +* `identifier_prefix` - (Optional, Forces new resource) Creates a unique identifier beginning with the specified prefix. Conflicts with `identifer`. * `cluster_identifier` - (Required) The identifier of the [`aws_rds_cluster`](/docs/providers/aws/r/rds_cluster.html) in which to launch this instance. * `instance_class` - (Required) The instance class to use. For details on CPU and memory, see [Scaling Aurora DB Instances][4]. Aurora currently diff --git a/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown b/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown index 8b465efad5..c2fa63d78c 100644 --- a/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown +++ b/website/source/docs/providers/aws/r/rds_cluster_parameter_group.markdown @@ -32,9 +32,10 @@ resource "aws_rds_cluster_parameter_group" "default" { The following arguments are supported: -* `name` - (Required) The name of the DB cluster parameter group. +* `name` - (Optional, Forces new resource) The name of the DB cluster parameter group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `family` - (Required) The family of the DB cluster parameter group. -* `description` - (Required) The description of the DB cluster parameter group. +* `description` - (Optional) The description of the DB cluster parameter group. Defaults to "Managed by Terraform". * `parameter` - (Optional) A list of DB parameters to apply. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -60,4 +61,4 @@ RDS Cluster Parameter Groups can be imported using the `name`, e.g. ``` $ terraform import aws_rds_cluster_parameter_group.cluster_pg production-pg-1 -``` \ No newline at end of file +``` From d7a339eb4627b9a2f4b82f969bebbf0823a6e3d3 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 31 Mar 2017 20:23:27 +0300 Subject: [PATCH 088/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295ecb0b97..3c672c3d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ IMPROVEMENTS: * provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147] * provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527] * provider/aws: Added API Gateway integration update [GH-13249] + * provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/triton: Move to joyent/triton-go [GH-13225] From 50fb9aecbdf9bc637c1b3b2ae9eeb01ca06fe95e Mon Sep 17 00:00:00 2001 From: James Nugent Date: Fri, 31 Mar 2017 11:33:08 -0700 Subject: [PATCH 089/101] deps: Update github.com/joyent/triton-go/authentication (#13255) This commit allows private key material to be used with the Triton provider, which is necessary for running acceptance tests in the HashiCorp CI environment. --- .../authentication/private_key_signer.go | 15 +++++++++------ vendor/vendor.json | 10 +++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go index aadb6ee5f4..20dc6bfedf 100644 --- a/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go +++ b/vendor/github.com/joyent/triton-go/authentication/private_key_signer.go @@ -7,10 +7,12 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" + "errors" "fmt" + "strings" + "github.com/hashicorp/errwrap" "golang.org/x/crypto/ssh" - "strings" ) type PrivateKeySigner struct { @@ -27,23 +29,23 @@ func NewPrivateKeySigner(keyFingerprint string, privateKeyMaterial []byte, accou block, _ := pem.Decode(privateKeyMaterial) if block == nil { - return nil, fmt.Errorf("Error PEM-decoding private key material: nil block received") + return nil, errors.New("Error PEM-decoding private key material: nil block received") } rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return nil, errwrap.Wrapf("Error parsing private key: %s", err) + return nil, errwrap.Wrapf("Error parsing private key: {{err}}", err) } sshPublicKey, err := ssh.NewPublicKey(rsakey.Public()) if err != nil { - return nil, errwrap.Wrapf("Error parsing SSH key from private key: %s", err) + return nil, errwrap.Wrapf("Error parsing SSH key from private key: {{err}}", err) } matchKeyFingerprint := formatPublicKeyFingerprint(sshPublicKey, false) displayKeyFingerprint := formatPublicKeyFingerprint(sshPublicKey, true) if matchKeyFingerprint != keyFingerprintMD5 { - return nil, fmt.Errorf("Private key file does not match public key fingerprint") + return nil, errors.New("Private key file does not match public key fingerprint") } return &PrivateKeySigner{ @@ -69,5 +71,6 @@ func (s *PrivateKeySigner) Sign(dateHeader string) (string, error) { } signedBase64 := base64.StdEncoding.EncodeToString(signed) - return fmt.Sprintf(authorizationHeaderFormat, s.formattedKeyFingerprint, "rsa-sha1", headerName, signedBase64), nil + keyID := fmt.Sprintf("/%s/keys/%s", s.accountName, s.formattedKeyFingerprint) + return fmt.Sprintf(authorizationHeaderFormat, keyID, "rsa-sha1", headerName, signedBase64), nil } diff --git a/vendor/vendor.json b/vendor/vendor.json index e8993495c6..1d3e40282f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -2296,14 +2296,14 @@ { "checksumSHA1": "fue8Al8kqw/Q6VFPsNzoky7NIgo=", "path": "github.com/joyent/triton-go", - "revision": "ed036af6d128e3c1ef76e92218810d3b298d1407", - "revisionTime": "2017-03-30T22:02:44Z" + "revision": "66b31a94af28a65e902423879a2820ea34b773fb", + "revisionTime": "2017-03-31T18:12:29Z" }, { - "checksumSHA1": "7sIV9LK625xVO9WsV1gaLQgfBeY=", + "checksumSHA1": "QzUqkCSn/ZHyIK346xb9V6EBw9U=", "path": "github.com/joyent/triton-go/authentication", - "revision": "ed036af6d128e3c1ef76e92218810d3b298d1407", - "revisionTime": "2017-03-30T22:02:44Z" + "revision": "66b31a94af28a65e902423879a2820ea34b773fb", + "revisionTime": "2017-03-31T18:12:29Z" }, { "checksumSHA1": "YhQcOsGx8r2S/jkJ0Qt4cZ5BLCU=", From 173bf10e7b2afcb64f127fc2033c1e91a60aea53 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 31 Mar 2017 22:22:12 +0100 Subject: [PATCH 090/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c672c3d35..3717de123e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ IMPROVEMENTS: * provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527] * provider/aws: Added API Gateway integration update [GH-13249] * provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232] + * provider/github: Handle the case when issue labels already exist [GH-13182] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/triton: Move to joyent/triton-go [GH-13225] From 0c4c578552d4f4015da8adff6316b0bd13985245 Mon Sep 17 00:00:00 2001 From: Doug Neal Date: Sat, 1 Apr 2017 06:57:34 +0100 Subject: [PATCH 091/101] provider/aws: Implement aws_ses_domain_identity (#13098) * provider/aws: New resource: aws_ses_domain_identity Provide a resource to manage domain identities in SES. Exports the verification_code attribute which can be used to add the TXT record to the domain to complete the domain verification. * provider/aws: Acceptance tests for aws_ses_domain_identity * Resource aws_ses_domain_identity: Documentation update Provide documentation for the new resource type. --- builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_ses_domain_identity.go | 99 +++++++++++++++++ .../resource_aws_ses_domain_identity_test.go | 100 ++++++++++++++++++ .../source/docs/import/importability.html.md | 1 + .../aws/r/ses_domain_identity.html.markdown | 46 ++++++++ website/source/layouts/aws.erb | 4 + 6 files changed, 251 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_ses_domain_identity.go create mode 100644 builtin/providers/aws/resource_aws_ses_domain_identity_test.go create mode 100644 website/source/docs/providers/aws/r/ses_domain_identity.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 0ad5817858..e1c7d24ac4 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -386,6 +386,7 @@ func Provider() terraform.ResourceProvider { "aws_route_table": resourceAwsRouteTable(), "aws_route_table_association": resourceAwsRouteTableAssociation(), "aws_ses_active_receipt_rule_set": resourceAwsSesActiveReceiptRuleSet(), + "aws_ses_domain_identity": resourceAwsSesDomainIdentity(), "aws_ses_receipt_filter": resourceAwsSesReceiptFilter(), "aws_ses_receipt_rule": resourceAwsSesReceiptRule(), "aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(), diff --git a/builtin/providers/aws/resource_aws_ses_domain_identity.go b/builtin/providers/aws/resource_aws_ses_domain_identity.go new file mode 100644 index 0000000000..7eba7a1873 --- /dev/null +++ b/builtin/providers/aws/resource_aws_ses_domain_identity.go @@ -0,0 +1,99 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ses" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsSesDomainIdentity() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsSesDomainIdentityCreate, + Read: resourceAwsSesDomainIdentityRead, + Delete: resourceAwsSesDomainIdentityDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "domain": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "verification_token": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsSesDomainIdentityCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).sesConn + + domainName := d.Get("domain").(string) + + createOpts := &ses.VerifyDomainIdentityInput{ + Domain: aws.String(domainName), + } + + _, err := conn.VerifyDomainIdentity(createOpts) + if err != nil { + return fmt.Errorf("Error requesting SES domain identity verification: %s", err) + } + + d.SetId(domainName) + + return resourceAwsSesDomainIdentityRead(d, meta) +} + +func resourceAwsSesDomainIdentityRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).sesConn + + domainName := d.Id() + d.Set("domain", domainName) + + readOpts := &ses.GetIdentityVerificationAttributesInput{ + Identities: []*string{ + aws.String(domainName), + }, + } + + response, err := conn.GetIdentityVerificationAttributes(readOpts) + if err != nil { + log.Printf("[WARN] Error fetching identity verification attributes for %s: %s", d.Id(), err) + return err + } + + verificationAttrs, ok := response.VerificationAttributes[domainName] + if !ok { + log.Printf("[WARN] Domain not listed in response when fetching verification attributes for %s", d.Id()) + d.SetId("") + return nil + } + + d.Set("verification_token", verificationAttrs.VerificationToken) + return nil +} + +func resourceAwsSesDomainIdentityDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).sesConn + + domainName := d.Get("domain").(string) + + deleteOpts := &ses.DeleteIdentityInput{ + Identity: aws.String(domainName), + } + + _, err := conn.DeleteIdentity(deleteOpts) + if err != nil { + return fmt.Errorf("Error deleting SES domain identity: %s", err) + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_ses_domain_identity_test.go b/builtin/providers/aws/resource_aws_ses_domain_identity_test.go new file mode 100644 index 0000000000..6119fa1231 --- /dev/null +++ b/builtin/providers/aws/resource_aws_ses_domain_identity_test.go @@ -0,0 +1,100 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ses" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsSESDomainIdentity_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSESDomainIdentityDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: fmt.Sprintf( + testAccAwsSESDomainIdentityConfig, + acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum), + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSESDomainIdentityExists("aws_ses_domain_identity.test"), + ), + }, + }, + }) +} + +func testAccCheckAwsSESDomainIdentityDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).sesConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ses_domain_identity" { + continue + } + + domain := rs.Primary.ID + params := &ses.GetIdentityVerificationAttributesInput{ + Identities: []*string{ + aws.String(domain), + }, + } + + response, err := conn.GetIdentityVerificationAttributes(params) + if err != nil { + return err + } + + if response.VerificationAttributes[domain] != nil { + return fmt.Errorf("SES Domain Identity %s still exists. Failing!", domain) + } + } + + return nil +} + +func testAccCheckAwsSESDomainIdentityExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("SES Domain Identity not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("SES Domain Identity name not set") + } + + domain := rs.Primary.ID + conn := testAccProvider.Meta().(*AWSClient).sesConn + + params := &ses.GetIdentityVerificationAttributesInput{ + Identities: []*string{ + aws.String(domain), + }, + } + + response, err := conn.GetIdentityVerificationAttributes(params) + if err != nil { + return err + } + + if response.VerificationAttributes[domain] == nil { + return fmt.Errorf("SES Domain Identity %s not found in AWS", domain) + } + + return nil + } +} + +const testAccAwsSESDomainIdentityConfig = ` +resource "aws_ses_domain_identity" "test" { + domain = "%s.terraformtesting.com" +} +` diff --git a/website/source/docs/import/importability.html.md b/website/source/docs/import/importability.html.md index a9bac81e6d..933c47a1dc 100644 --- a/website/source/docs/import/importability.html.md +++ b/website/source/docs/import/importability.html.md @@ -92,6 +92,7 @@ To make a resource importable, please see the * aws_route_table * aws_s3_bucket * aws_security_group +* aws_ses_domain_identity * aws_ses_receipt_filter * aws_ses_receipt_rule_set * aws_simpledb_domain diff --git a/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown b/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown new file mode 100644 index 0000000000..e005160bf1 --- /dev/null +++ b/website/source/docs/providers/aws/r/ses_domain_identity.html.markdown @@ -0,0 +1,46 @@ +--- +layout: "aws" +page_title: "AWS: ses_domain_identity" +sidebar_current: "docs-aws-resource-ses-domain-identity" +description: |- + Provides an SES domain identity resource +--- + +# aws\_ses\_domain_identity + +Provides an SES domain identity resource + +## Argument Reference + +The following arguments are supported: + +* `domain` - (Required) The domain name to assign to SES + +## Attributes Reference + +The following attributes are exported: + +* `verification_token` - A code which when added to the domain as a TXT record + will signal to SES that the owner of the domain has authorised SES to act on + their behalf. The domain identity will be in state "verification pending" + until this is done. See below for an example of how this might be achieved + when the domain is hosted in Route 53 and managed by Terraform. Find out + more about verifying domains in Amazon SES in the [AWS SES + docs](http://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domains.html). + +## Example Usage + +``` +resource "aws_ses_domain_identity" "example" { + domain = "example.com" +} + +resource "aws_route53_record" "example_amazonses_verification_record" { + zone_id = "ABCDEFGHIJ123" + name = "_amazonses.example.com" + type = "TXT" + ttl = "600" + records = ["${aws_ses_domain_identity.example.verification_token}"] +} +``` + diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index d3e39f97d5..75e5d546e9 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -1127,6 +1127,10 @@ aws_ses_active_receipt_rule_set + > + aws_ses_domain_identity + + > aws_ses_receipt_filter From 6667a85cd9d1b764cd3b48a6e1c415fb4a45b02f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 06:58:27 +0100 Subject: [PATCH 092/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3717de123e..0c1e81f63e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ FEATURES: * **New Resource:** `aws_lightsail_static_ip` [GH-13175] * **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207] + * **New Resource:** `aws_ses_domain_identity` [GH-13098] * **New Resource:** `kubernetes_secret` [GH-12960] * **New Data Source:** `aws_iam_role` [GH-13213] From b8f6e2a70ae0e741ddf8a3ef69334121bd62ae93 Mon Sep 17 00:00:00 2001 From: Jonathan Camp Date: Sat, 1 Apr 2017 16:39:46 +0200 Subject: [PATCH 093/101] provider/aws: handle aws_lambda_function missing s3 key error (#10960) * ensure NoSuchKey is not a retryable error * Simplify err handling + lower log msg severity --- .../providers/aws/resource_aws_lambda_function.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_lambda_function.go b/builtin/providers/aws/resource_aws_lambda_function.go index 211da109d7..4eedd77a35 100644 --- a/builtin/providers/aws/resource_aws_lambda_function.go +++ b/builtin/providers/aws/resource_aws_lambda_function.go @@ -297,14 +297,13 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e err := resource.Retry(10*time.Minute, func() *resource.RetryError { _, err := conn.CreateFunction(params) if err != nil { - log.Printf("[ERROR] Received %q, retrying CreateFunction", err) - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "InvalidParameterValueException" { - log.Printf("[DEBUG] InvalidParameterValueException creating Lambda Function: %s", awserr) - return resource.RetryableError(awserr) - } - } log.Printf("[DEBUG] Error creating Lambda Function: %s", err) + + if isAWSErr(err, "InvalidParameterValueException", "The role defined for the function cannot be assumed by Lambda") { + log.Printf("[DEBUG] Received %s, retrying CreateFunction", err) + return resource.RetryableError(err) + } + return resource.NonRetryableError(err) } return nil From 26c544547710eed3882d53d6d11b7d4c0625c47f Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 15:42:17 +0100 Subject: [PATCH 094/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1e81f63e..6e1a57e878 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ BUG FIXES: * provider/aws: Refresh aws_alb_target_group tags [GH-13200] * provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204] * provider/aws: Wait for aws_opsworks_instance to be running when it's specified [GH-13218] + * provider/aws: Handle `aws_lambda_function` missing s3 key error [GH-10960] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From 4b9ec9c9200d4fe77101a62f94958dea678da3b5 Mon Sep 17 00:00:00 2001 From: Masayuki Morita Date: Sun, 2 Apr 2017 01:31:32 +0900 Subject: [PATCH 095/101] provider/aws: Validate aws_ecs_task_definition.container_definitions (#12161) --- .../aws/resource_aws_ecs_task_definition.go | 10 ++++ .../resource_aws_ecs_task_definition_test.go | 48 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition.go b/builtin/providers/aws/resource_aws_ecs_task_definition.go index 2734afba96..fa082472b6 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition.go @@ -45,6 +45,7 @@ func resourceAwsEcsTaskDefinition() *schema.Resource { hash := sha1.Sum([]byte(v.(string))) return hex.EncodeToString(hash[:]) }, + ValidateFunc: validateAwsEcsTaskDefinitionContainerDefinitions, }, "task_role_arn": { @@ -121,6 +122,15 @@ func validateAwsEcsTaskDefinitionNetworkMode(v interface{}, k string) (ws []stri return } +func validateAwsEcsTaskDefinitionContainerDefinitions(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + _, err := expandEcsContainerDefinitions(value) + if err != nil { + errors = append(errors, fmt.Errorf("ECS Task Definition container_definitions is invalid: %s", err)) + } + return +} + func resourceAwsEcsTaskDefinitionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ecsconn diff --git a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go index a414130cdf..afbf2955f8 100644 --- a/builtin/providers/aws/resource_aws_ecs_task_definition_test.go +++ b/builtin/providers/aws/resource_aws_ecs_task_definition_test.go @@ -203,6 +203,28 @@ func TestValidateAwsEcsTaskDefinitionNetworkMode(t *testing.T) { } } +func TestValidateAwsEcsTaskDefinitionContainerDefinitions(t *testing.T) { + validDefinitions := []string{ + testValidateAwsEcsTaskDefinitionValidContainerDefinitions, + } + for _, v := range validDefinitions { + _, errors := validateAwsEcsTaskDefinitionContainerDefinitions(v, "container_definitions") + if len(errors) != 0 { + t.Fatalf("%q should be a valid AWS ECS Task Definition Container Definitions: %q", v, errors) + } + } + + invalidDefinitions := []string{ + testValidateAwsEcsTaskDefinitionInvalidCommandContainerDefinitions, + } + for _, v := range invalidDefinitions { + _, errors := validateAwsEcsTaskDefinitionContainerDefinitions(v, "container_definitions") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid AWS ECS Task Definition Container Definitions", v) + } + } +} + func testAccCheckAWSEcsTaskDefinitionDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ecsconn @@ -666,3 +688,29 @@ TASK_DEFINITION } } ` + +var testValidateAwsEcsTaskDefinitionValidContainerDefinitions = ` +[ + { + "name": "sleep", + "image": "busybox", + "cpu": 10, + "command": ["sleep","360"], + "memory": 10, + "essential": true + } +] +` + +var testValidateAwsEcsTaskDefinitionInvalidCommandContainerDefinitions = ` +[ + { + "name": "sleep", + "image": "busybox", + "cpu": 10, + "command": "sleep 360", + "memory": 10, + "essential": true + } +] +` From 9b55ce5385fe0f7a5c0895c41843bb19432e0886 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sat, 1 Apr 2017 17:32:18 +0100 Subject: [PATCH 096/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1a57e878..5261d3ae3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ IMPROVEMENTS: * provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527] * provider/aws: Added API Gateway integration update [GH-13249] * provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232] + * provider/aws: Validate `aws_ecs_task_definition.container_definitions` [GH-12161] * provider/github: Handle the case when issue labels already exist [GH-13182] * provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148] * provider/triton: Move to joyent/triton-go [GH-13225] From d209dc1a32bb75cb072601c5d96f1e9080f485e7 Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 3 Apr 2017 01:48:45 +0300 Subject: [PATCH 097/101] provider/aws: Fixup AWS db instance acceptance tests with default security group --- builtin/providers/aws/resource_aws_db_instance_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_db_instance_test.go b/builtin/providers/aws/resource_aws_db_instance_test.go index 6c8d451ca0..17d3bf6b88 100644 --- a/builtin/providers/aws/resource_aws_db_instance_test.go +++ b/builtin/providers/aws/resource_aws_db_instance_test.go @@ -676,7 +676,6 @@ resource "aws_db_instance" "test" { instance_class = "db.t1.micro" password = "password" username = "root" - security_group_names = ["default"] publicly_accessible = true skip_final_snapshot = true @@ -692,7 +691,6 @@ resource "aws_db_instance" "test" { instance_class = "db.t1.micro" password = "password" username = "root" - security_group_names = ["default"] publicly_accessible = true skip_final_snapshot = true From 2d8f3f257afbe301a5e5af12c7c1f30fc45f0e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20Schmidt?= Date: Mon, 3 Apr 2017 10:38:35 +0200 Subject: [PATCH 098/101] Fix small typo --- website/source/docs/configuration/interpolation.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/configuration/interpolation.html.md b/website/source/docs/configuration/interpolation.html.md index b101730a39..7092615850 100644 --- a/website/source/docs/configuration/interpolation.html.md +++ b/website/source/docs/configuration/interpolation.html.md @@ -322,7 +322,7 @@ The supported built-in functions are: `a_resource_param = ["${split(",", var.CSV_STRING)}"]`. Example: `split(",", module.amod.server_ids)` - * `substr(string, offset, length)` - Extracts a substring from the input string. A negative offset is interpreted as being equivalent to a positive offset measured backwards from the end of the string. A length of `-1` is interpretted as meaning "until the end of the string". + * `substr(string, offset, length)` - Extracts a substring from the input string. A negative offset is interpreted as being equivalent to a positive offset measured backwards from the end of the string. A length of `-1` is interpreted as meaning "until the end of the string". * `timestamp()` - Returns a UTC timestamp string in RFC 3339 format. This string will change with every invocation of the function, so in order to prevent diffs on every plan & apply, it must be used with the From b80d3e5701f24e468c004363b7f5d3a9c97a6c82 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 3 Apr 2017 12:11:45 +0300 Subject: [PATCH 099/101] provider/aws: Set stickiness to computed in alb_target_group (#13278) The Default values set by AWS were breaking the AWS ALB Listener Rule tests. The stickiness parameter needed to be set to be Computed: true to remove this discrepancy ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBListenerRule_basic' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/04/03 01:23:47 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBListenerRule_basic -timeout 120m === RUN TestAccAWSALBListenerRule_basic --- PASS: TestAccAWSALBListenerRule_basic (235.36s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 235.397s ``` --- builtin/providers/aws/resource_aws_alb_target_group.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builtin/providers/aws/resource_aws_alb_target_group.go b/builtin/providers/aws/resource_aws_alb_target_group.go index df1a662be1..9412198d49 100644 --- a/builtin/providers/aws/resource_aws_alb_target_group.go +++ b/builtin/providers/aws/resource_aws_alb_target_group.go @@ -73,6 +73,7 @@ func resourceAwsAlbTargetGroup() *schema.Resource { "stickiness": { Type: schema.TypeList, Optional: true, + Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ From ef2d2646a8a9e0c8ea02096d51016e64f080c817 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 3 Apr 2017 12:13:58 +0300 Subject: [PATCH 100/101] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5261d3ae3c..74e0105820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ BUG FIXES: * provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204] * provider/aws: Wait for aws_opsworks_instance to be running when it's specified [GH-13218] * provider/aws: Handle `aws_lambda_function` missing s3 key error [GH-10960] + * provider/aws: Set stickiness to computed in alb_target_group [GH-13278] * provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153] ## 0.9.2 (March 28, 2017) From fbbfe67bb5fb6584cd28338632d71b9e7174033b Mon Sep 17 00:00:00 2001 From: Adam Hoka Date: Mon, 3 Apr 2017 14:11:25 +0200 Subject: [PATCH 101/101] Update documentation to reflect reality From the code: "records": &schema.Schema{ Type: schema.TypeString, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, Removed: "Use `record` instead. This attribute will be removed in a future version", }, "record": &schema.Schema{ Type: schema.TypeString, Required: true, --- .../docs/providers/azurerm/r/dns_cname_record.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown b/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown index 38931897a8..10832dff2a 100644 --- a/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown +++ b/website/source/docs/providers/azurerm/r/dns_cname_record.html.markdown @@ -28,7 +28,7 @@ resource "azurerm_dns_cname_record" "test" { zone_name = "${azurerm_dns_zone.test.name}" resource_group_name = "${azurerm_resource_group.test.name}" ttl = "300" - records = ["contoso.com"] + record = "contoso.com" } ``` ## Argument Reference @@ -43,7 +43,7 @@ The following arguments are supported: * `TTL` - (Required) The Time To Live (TTL) of the DNS record. -* `records` - (Required) The target of the CNAME. Must be a single value. +* `record` - (Required) The target of the CNAME. * `tags` - (Optional) A mapping of tags to assign to the resource.