From fc04f4b5a09c00118543a83a31db25d1dc72da4f Mon Sep 17 00:00:00 2001 From: Naveen Nalam Date: Fri, 24 Mar 2017 03:29:27 -0700 Subject: [PATCH 01/97] Fix zone value in example (#13023) As experienced during testing, the value of the zone needs to be the zone name, and not the NS1 internal ID. --- website/source/docs/providers/ns1/r/record.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/ns1/r/record.html.markdown b/website/source/docs/providers/ns1/r/record.html.markdown index 88f84cf40..05b2bde50 100644 --- a/website/source/docs/providers/ns1/r/record.html.markdown +++ b/website/source/docs/providers/ns1/r/record.html.markdown @@ -18,7 +18,7 @@ resource "ns1_zone" "tld" { } resource "ns1_record" "www" { - zone = "${ns1_zone.tld.id}" + zone = "${ns1_zone.tld.zone}" domain = "www.${ns1_zone.tld.zone}" type = "CNAME" ttl = 60 From 4fe7ee16e6a92b51d00abe38a260c99d7b619ce5 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 24 Mar 2017 13:02:11 +0200 Subject: [PATCH 02/97] provider/aws: Migrate the state for AWS VPC after IPv6 changes (#13041) Fixes: #13035 It was pointed out in the issue that the addition of a new parameter with a default value AND a ForceNew: true is causing Terraform to try and recreate the VPC This PR migrates the state to add the default value of false for `assign_generated_ipv6_cidr_block` ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAWSVpcMigrateState' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/24 12:51:41 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAWSVpcMigrateState -timeout 120m === RUN TestAWSVpcMigrateState 2017/03/24 12:52:26 [INFO] Found AWS VPC State v0; migrating to v1 2017/03/24 12:52:26 [DEBUG] Attributes before migration: map[string]string{"assign_generated_ipv6_cidr_block":"true"} 2017/03/24 12:52:26 [DEBUG] Attributes after migration: map[string]string{"assign_generated_ipv6_cidr_block":"false"} 2017/03/24 12:52:26 [INFO] Found AWS VPC State v0; migrating to v1 2017/03/24 12:52:26 [DEBUG] Attributes before migration: map[string]string{} 2017/03/24 12:52:26 [DEBUG] Attributes after migration: map[string]string{"assign_generated_ipv6_cidr_block":"false"} --- PASS: TestAWSVpcMigrateState (0.00s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 0.024s ``` --- builtin/providers/aws/resource_aws_vpc.go | 3 ++ .../providers/aws/resource_aws_vpc_migrate.go | 33 +++++++++++++ .../aws/resource_aws_vpc_migrate_test.go | 49 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_vpc_migrate.go create mode 100644 builtin/providers/aws/resource_aws_vpc_migrate_test.go diff --git a/builtin/providers/aws/resource_aws_vpc.go b/builtin/providers/aws/resource_aws_vpc.go index e3bcee3a6..6807706b6 100644 --- a/builtin/providers/aws/resource_aws_vpc.go +++ b/builtin/providers/aws/resource_aws_vpc.go @@ -22,6 +22,9 @@ func resourceAwsVpc() *schema.Resource { State: resourceAwsVpcInstanceImport, }, + SchemaVersion: 1, + MigrateState: resourceAwsVpcMigrateState, + Schema: map[string]*schema.Schema{ "cidr_block": { Type: schema.TypeString, diff --git a/builtin/providers/aws/resource_aws_vpc_migrate.go b/builtin/providers/aws/resource_aws_vpc_migrate.go new file mode 100644 index 000000000..90738d1f2 --- /dev/null +++ b/builtin/providers/aws/resource_aws_vpc_migrate.go @@ -0,0 +1,33 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/terraform" +) + +func resourceAwsVpcMigrateState( + v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { + switch v { + case 0: + log.Println("[INFO] Found AWS VPC State v0; migrating to v1") + return migrateVpcStateV0toV1(is) + default: + return is, fmt.Errorf("Unexpected schema version: %d", v) + } +} + +func migrateVpcStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) { + if is.Empty() || is.Attributes == nil { + log.Println("[DEBUG] Empty VPC State; nothing to migrate.") + return is, nil + } + + log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes) + + is.Attributes["assign_generated_ipv6_cidr_block"] = "false" + + log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) + return is, nil +} diff --git a/builtin/providers/aws/resource_aws_vpc_migrate_test.go b/builtin/providers/aws/resource_aws_vpc_migrate_test.go new file mode 100644 index 000000000..55be6d75b --- /dev/null +++ b/builtin/providers/aws/resource_aws_vpc_migrate_test.go @@ -0,0 +1,49 @@ +package aws + +import ( + "testing" + + "github.com/hashicorp/terraform/terraform" +) + +func TestAWSVpcMigrateState(t *testing.T) { + cases := map[string]struct { + StateVersion int + ID string + Attributes map[string]string + Expected string + Meta interface{} + }{ + "v0_1": { + StateVersion: 0, + ID: "some_id", + Attributes: map[string]string{ + "assign_generated_ipv6_cidr_block": "true", + }, + Expected: "false", + }, + "v0_1_without_value": { + StateVersion: 0, + ID: "some_id", + Attributes: map[string]string{}, + Expected: "false", + }, + } + + for tn, tc := range cases { + is := &terraform.InstanceState{ + ID: tc.ID, + Attributes: tc.Attributes, + } + is, err := resourceAwsVpcMigrateState( + tc.StateVersion, is, tc.Meta) + + if err != nil { + t.Fatalf("bad: %s, err: %#v", tn, err) + } + + if is.Attributes["assign_generated_ipv6_cidr_block"] != tc.Expected { + t.Fatalf("bad VPC Migrate: %s\n\n expected: %s", is.Attributes["assign_generated_ipv6_cidr_block"], tc.Expected) + } + } +} From 11768bcf5b9d99bb1fcc3c6b41d67584a467c115 Mon Sep 17 00:00:00 2001 From: demonwy Date: Fri, 24 Mar 2017 19:04:56 +0800 Subject: [PATCH 03/97] provider alicloud:add new rds resource and some bugs fix (#12913) * add new rds resource and some bugs fix * add docs * fix validator conflix fix validator conflix --- builtin/providers/alicloud/common.go | 28 + builtin/providers/alicloud/config.go | 56 +- .../alicloud/data_source_alicloud_images.go | 25 +- .../data_source_alicloud_images_test.go | 25 + ...ata_source_alicloud_instance_types_test.go | 2 - .../data_source_alicloud_regions_test.go | 5 - .../data_source_alicloud_zones_test.go | 78 +- builtin/providers/alicloud/errors.go | 1 + builtin/providers/alicloud/extension_ecs.go | 5 + builtin/providers/alicloud/extension_slb.go | 97 +- builtin/providers/alicloud/provider.go | 3 +- .../alicloud/resource_alicloud_db_instance.go | 545 +++++++ .../resource_alicloud_db_instance_test.go | 765 ++++++++++ .../resource_alicloud_disk_attachment_test.go | 1 + .../alicloud/resource_alicloud_disk_test.go | 15 +- .../resource_alicloud_eip_association_test.go | 22 +- .../alicloud/resource_alicloud_instance.go | 170 ++- .../resource_alicloud_instance_test.go | 298 ++-- .../alicloud/resource_alicloud_nat_gateway.go | 38 +- .../resource_alicloud_nat_gateway_test.go | 37 +- .../resource_alicloud_security_group.go | 2 +- .../resource_alicloud_security_group_rule.go | 82 +- ...ource_alicloud_security_group_rule_test.go | 169 ++- .../alicloud/resource_alicloud_slb.go | 282 +++- .../resource_alicloud_slb_attachment_test.go | 23 +- .../alicloud/resource_alicloud_slb_test.go | 36 +- .../resource_alicloud_vroute_entry_test.go | 7 +- .../resource_alicloud_vswitch_test.go | 6 +- .../alicloud/service_alicloud_ecs.go | 59 +- .../alicloud/service_alicloud_rds.go | 278 ++++ .../alicloud/service_alicloud_vpc.go | 26 +- builtin/providers/alicloud/validators.go | 456 +++++- builtin/providers/alicloud/validators_test.go | 73 + examples/alicloud-ecs-image/main.tf | 33 + examples/alicloud-ecs-nat/README.md | 33 + examples/alicloud-ecs-nat/main.tf | 98 ++ examples/alicloud-ecs-nat/outputs.tf | 19 + examples/alicloud-ecs-nat/userdata.sh | 9 + examples/alicloud-ecs-nat/variables.tf | 27 + examples/alicloud-ecs-slb/README.md | 2 +- examples/alicloud-ecs-slb/main.tf | 42 +- examples/alicloud-ecs-userdata/main.tf | 45 +- examples/alicloud-ecs-userdata/outputs.tf | 7 +- examples/alicloud-ecs-userdata/variables.tf | 4 + examples/alicloud-ecs-vpc-cluster/main.tf | 9 - .../alicloud-ecs-vpc-cluster/variables.tf | 4 - examples/alicloud-ecs-vpc/variables.tf | 1 + examples/alicloud-ecs-zone-type/main.tf | 35 +- examples/alicloud-ecs/main.tf | 35 +- examples/alicloud-ecs/variables.tf | 13 +- examples/alicloud-rds/README.md | 17 + examples/alicloud-rds/main.tf | 17 + examples/alicloud-rds/outputs.tf | 11 + examples/alicloud-rds/variables.tf | 29 + examples/alicloud-security-group-rule/main.tf | 17 +- examples/alicloud-slb/README.md | Bin 470567 -> 2186 bytes examples/alicloud-slb/main.tf | 57 +- examples/alicloud-vpc-route-entry/main.tf | 26 +- .../denverdino/aliyungo/common/client.go | 59 +- .../denverdino/aliyungo/common/endpoint.go | 118 ++ .../denverdino/aliyungo/common/endpoints.xml | 1351 +++++++++++++++++ .../denverdino/aliyungo/common/regions.go | 27 +- .../denverdino/aliyungo/common/types.go | 74 + .../denverdino/aliyungo/ecs/client.go | 39 +- .../denverdino/aliyungo/ecs/images.go | 41 +- .../denverdino/aliyungo/ecs/instances.go | 74 +- .../denverdino/aliyungo/ecs/nat_gateway.go | 42 +- .../aliyungo/ecs/security_groups.go | 1 + .../denverdino/aliyungo/ecs/snat_entry.go | 95 ++ .../denverdino/aliyungo/rds/client.go | 48 + .../denverdino/aliyungo/rds/instances.go | 843 ++++++++++ .../denverdino/aliyungo/rds/monitoring.go | 50 + .../denverdino/aliyungo/slb/client.go | 20 +- .../denverdino/aliyungo/slb/listeners.go | 56 +- .../denverdino/aliyungo/slb/rules.go | 126 ++ .../denverdino/aliyungo/slb/servers.go | 2 - vendor/vendor.json | 28 +- .../alicloud/r/db_instance.html.markdown | 106 ++ .../alicloud/r/instance.html.markdown | 17 +- 79 files changed, 6976 insertions(+), 546 deletions(-) create mode 100644 builtin/providers/alicloud/resource_alicloud_db_instance.go create mode 100644 builtin/providers/alicloud/resource_alicloud_db_instance_test.go create mode 100644 builtin/providers/alicloud/service_alicloud_rds.go create mode 100644 examples/alicloud-ecs-nat/README.md create mode 100644 examples/alicloud-ecs-nat/main.tf create mode 100644 examples/alicloud-ecs-nat/outputs.tf create mode 100644 examples/alicloud-ecs-nat/userdata.sh create mode 100644 examples/alicloud-ecs-nat/variables.tf create mode 100644 examples/alicloud-rds/README.md create mode 100644 examples/alicloud-rds/main.tf create mode 100644 examples/alicloud-rds/outputs.tf create mode 100644 examples/alicloud-rds/variables.tf create mode 100644 vendor/github.com/denverdino/aliyungo/common/endpoint.go create mode 100644 vendor/github.com/denverdino/aliyungo/common/endpoints.xml rename builtin/providers/alicloud/extension_nat_gateway.go => vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go (80%) create mode 100644 vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go create mode 100644 vendor/github.com/denverdino/aliyungo/rds/client.go create mode 100644 vendor/github.com/denverdino/aliyungo/rds/instances.go create mode 100644 vendor/github.com/denverdino/aliyungo/rds/monitoring.go create mode 100644 vendor/github.com/denverdino/aliyungo/slb/rules.go create mode 100644 website/source/docs/providers/alicloud/r/db_instance.html.markdown diff --git a/builtin/providers/alicloud/common.go b/builtin/providers/alicloud/common.go index 24c3647db..c2af2a683 100644 --- a/builtin/providers/alicloud/common.go +++ b/builtin/providers/alicloud/common.go @@ -2,6 +2,7 @@ package alicloud import ( "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/schema" ) @@ -12,8 +13,12 @@ const ( VpcNet = InstanceNetWork("vpc") ) +// timeout for common product, ecs e.g. const defaultTimeout = 120 +// timeout for long time progerss product, rds e.g. +const defaultLongTimeout = 800 + func getRegion(d *schema.ResourceData, meta interface{}) common.Region { return meta.(*AliyunClient).Region } @@ -50,3 +55,26 @@ func isProtocalValid(value string) bool { } return res } + +var DefaultBusinessInfo = ecs.BusinessInfo{ + Pack: "terraform", +} + +// default region for all resource +const DEFAULT_REGION = "cn-beijing" + +// default security ip for db +const DEFAULT_DB_SECURITY_IP = "127.0.0.1" + +// we the count of create instance is only one +const DEFAULT_INSTANCE_COUNT = 1 + +// symbol of multiIZ +const MULTI_IZ_SYMBOL = "MAZ" + +// default connect port of db +const DB_DEFAULT_CONNECT_PORT = "3306" + +const COMMA_SEPARATED = "," + +const LOCAL_HOST_IP = "127.0.0.1" diff --git a/builtin/providers/alicloud/config.go b/builtin/providers/alicloud/config.go index 352e2e21c..e17003bb2 100644 --- a/builtin/providers/alicloud/config.go +++ b/builtin/providers/alicloud/config.go @@ -5,6 +5,7 @@ import ( "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" + "github.com/denverdino/aliyungo/rds" "github.com/denverdino/aliyungo/slb" ) @@ -19,8 +20,11 @@ type Config struct { type AliyunClient struct { Region common.Region ecsconn *ecs.Client - vpcconn *ecs.Client - slbconn *slb.Client + rdsconn *rds.Client + // use new version + ecsNewconn *ecs.Client + vpcconn *ecs.Client + slbconn *slb.Client } // Client for AliyunClient @@ -35,6 +39,17 @@ func (c *Config) Client() (*AliyunClient, error) { return nil, err } + ecsNewconn, err := c.ecsConn() + if err != nil { + return nil, err + } + ecsNewconn.SetVersion(EcsApiVersion20160314) + + rdsconn, err := c.rdsConn() + if err != nil { + return nil, err + } + slbconn, err := c.slbConn() if err != nil { return nil, err @@ -46,13 +61,17 @@ func (c *Config) Client() (*AliyunClient, error) { } return &AliyunClient{ - Region: c.Region, - ecsconn: ecsconn, - vpcconn: vpcconn, - slbconn: slbconn, + Region: c.Region, + ecsconn: ecsconn, + ecsNewconn: ecsNewconn, + vpcconn: vpcconn, + slbconn: slbconn, + rdsconn: rdsconn, }, nil } +const BusinessInfoKey = "Terraform" + func (c *Config) loadAndValidate() error { err := c.validateRegion() if err != nil { @@ -74,7 +93,9 @@ func (c *Config) validateRegion() error { } func (c *Config) ecsConn() (*ecs.Client, error) { - client := ecs.NewClient(c.AccessKey, c.SecretKey) + client := ecs.NewECSClient(c.AccessKey, c.SecretKey, c.Region) + client.SetBusinessInfo(BusinessInfoKey) + _, err := client.DescribeRegions() if err != nil { @@ -84,20 +105,21 @@ func (c *Config) ecsConn() (*ecs.Client, error) { return client, nil } -func (c *Config) slbConn() (*slb.Client, error) { - client := slb.NewClient(c.AccessKey, c.SecretKey) +func (c *Config) rdsConn() (*rds.Client, error) { + client := rds.NewRDSClient(c.AccessKey, c.SecretKey, c.Region) + client.SetBusinessInfo(BusinessInfoKey) + return client, nil +} +func (c *Config) slbConn() (*slb.Client, error) { + client := slb.NewSLBClient(c.AccessKey, c.SecretKey, c.Region) + client.SetBusinessInfo(BusinessInfoKey) return client, nil } func (c *Config) vpcConn() (*ecs.Client, error) { - _, err := c.ecsConn() - - if err != nil { - return nil, err - } - - client := &ecs.Client{} - client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey) + client := ecs.NewVPCClient(c.AccessKey, c.SecretKey, c.Region) + client.SetBusinessInfo(BusinessInfoKey) return client, nil + } diff --git a/builtin/providers/alicloud/data_source_alicloud_images.go b/builtin/providers/alicloud/data_source_alicloud_images.go index ae5b66069..d9a873782 100644 --- a/builtin/providers/alicloud/data_source_alicloud_images.go +++ b/builtin/providers/alicloud/data_source_alicloud_images.go @@ -5,10 +5,10 @@ import ( "log" "regexp" "sort" + "time" "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/schema" - "time" ) func dataSourceAlicloudImages() *schema.Resource { @@ -175,15 +175,28 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string)) } - resp, _, err := conn.DescribeImages(params) - if err != nil { - return err + var allImages []ecs.ImageType + + for { + images, paginationResult, err := conn.DescribeImages(params) + if err != nil { + break + } + + allImages = append(allImages, images...) + + pagination := paginationResult.NextPage() + if pagination == nil { + break + } + + params.Pagination = *pagination } var filteredImages []ecs.ImageType if nameRegexOk { r := regexp.MustCompile(nameRegex.(string)) - for _, image := range resp { + for _, image := range allImages { // Check for a very rare case where the response would include no // image name. No name means nothing to attempt a match against, // therefore we are skipping such image. @@ -198,7 +211,7 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro } } } else { - filteredImages = resp[:] + filteredImages = allImages[:] } var images []ecs.ImageType diff --git a/builtin/providers/alicloud/data_source_alicloud_images_test.go b/builtin/providers/alicloud/data_source_alicloud_images_test.go index 7512c6e91..9c6e225e4 100644 --- a/builtin/providers/alicloud/data_source_alicloud_images_test.go +++ b/builtin/providers/alicloud/data_source_alicloud_images_test.go @@ -97,6 +97,22 @@ func TestAccAlicloudImagesDataSource_nameRegexFilter(t *testing.T) { }) } +func TestAccAlicloudImagesDataSource_imageNotInFirstPage(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_images.name_regex_filtered_image"), + resource.TestMatchResourceAttr("data.alicloud_images.name_regex_filtered_image", "images.0.image_id", regexp.MustCompile("^ubuntu_14")), + ), + }, + }, + }) +} + // Instance store test - using centos images const testAccCheckAlicloudImagesDataSourceImagesConfig = ` data "alicloud_images" "multi_image" { @@ -128,3 +144,12 @@ data "alicloud_images" "name_regex_filtered_image" { name_regex = "^centos_6\\w{1,5}[64]{1}.*" } ` + +// Testing image not in first page response +const testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig = ` +data "alicloud_images" "name_regex_filtered_image" { + most_recent = true + owners = "system" + name_regex = "^ubuntu_14.*_64" +} +` diff --git a/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go index 43a180def..335da3fbd 100644 --- a/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go +++ b/builtin/providers/alicloud/data_source_alicloud_instance_types_test.go @@ -17,8 +17,6 @@ func TestAccAlicloudInstanceTypesDataSource_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"), - resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "4"), - resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"), resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"), resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"), diff --git a/builtin/providers/alicloud/data_source_alicloud_regions_test.go b/builtin/providers/alicloud/data_source_alicloud_regions_test.go index f2aff1bbe..9dafaba1e 100644 --- a/builtin/providers/alicloud/data_source_alicloud_regions_test.go +++ b/builtin/providers/alicloud/data_source_alicloud_regions_test.go @@ -71,11 +71,6 @@ func TestAccAlicloudRegionsDataSource_empty(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"), - resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "name", ""), - resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "current", ""), - - resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.#", "13"), - resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.id", "cn-shenzhen"), resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.region_id", "cn-shenzhen"), resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"), diff --git a/builtin/providers/alicloud/data_source_alicloud_zones_test.go b/builtin/providers/alicloud/data_source_alicloud_zones_test.go index 4b07c9671..4757f495c 100644 --- a/builtin/providers/alicloud/data_source_alicloud_zones_test.go +++ b/builtin/providers/alicloud/data_source_alicloud_zones_test.go @@ -1,7 +1,10 @@ package alicloud import ( + "fmt" "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "strconv" "testing" ) @@ -23,6 +26,7 @@ func TestAccAlicloudZonesDataSource_basic(t *testing.T) { } func TestAccAlicloudZonesDataSource_filter(t *testing.T) { + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -33,7 +37,7 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) { Config: testAccCheckAlicloudZonesDataSourceFilter, Check: resource.ComposeTestCheckFunc( testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), - resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "2"), + testCheckZoneLength("data.alicloud_zones.foo"), ), }, @@ -41,13 +45,59 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) { Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized, Check: resource.ComposeTestCheckFunc( testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), - resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "1"), + testCheckZoneLength("data.alicloud_zones.foo"), ), }, }, }) } +func TestAccAlicloudZonesDataSource_unitRegion(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckAlicloudZonesDataSource_unitRegion, + Check: resource.ComposeTestCheckFunc( + testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"), + ), + }, + }, + }) +} + +// the zone length changed occasionally +// check by range to avoid test case failure +func testCheckZoneLength(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + ms := s.RootModule() + rs, ok := ms.Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + is := rs.Primary + if is == nil { + return fmt.Errorf("No primary instance: %s", name) + } + + i, err := strconv.Atoi(is.Attributes["zones.#"]) + + if err != nil { + return fmt.Errorf("convert zone length err: %#v", err) + } + + if i <= 0 { + return fmt.Errorf("zone length expected greater than 0 got err: %d", i) + } + + return nil + } +} + const testAccCheckAlicloudZonesDataSourceBasicConfig = ` data "alicloud_zones" "foo" { } @@ -55,16 +105,28 @@ data "alicloud_zones" "foo" { const testAccCheckAlicloudZonesDataSourceFilter = ` data "alicloud_zones" "foo" { - "available_instance_type"= "ecs.c2.xlarge" - "available_resource_creation"= "VSwitch" - "available_disk_category"= "cloud_efficiency" + available_instance_type= "ecs.c2.xlarge" + available_resource_creation= "VSwitch" + available_disk_category= "cloud_efficiency" } ` const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = ` data "alicloud_zones" "foo" { - "available_instance_type"= "ecs.c2.xlarge" - "available_resource_creation"= "IoOptimized" - "available_disk_category"= "cloud" + available_instance_type= "ecs.c2.xlarge" + available_resource_creation= "IoOptimized" + available_disk_category= "cloud" +} +` + +const testAccCheckAlicloudZonesDataSource_unitRegion = ` +provider "alicloud" { + alias = "northeast" + region = "ap-northeast-1" +} + +data "alicloud_zones" "foo" { + provider = "alicloud.northeast" + available_resource_creation= "VSwitch" } ` diff --git a/builtin/providers/alicloud/errors.go b/builtin/providers/alicloud/errors.go index f285bf968..338525330 100644 --- a/builtin/providers/alicloud/errors.go +++ b/builtin/providers/alicloud/errors.go @@ -9,6 +9,7 @@ const ( DiskIncorrectStatus = "IncorrectDiskStatus" DiskCreatingSnapshot = "DiskCreatingSnapshot" InstanceLockedForSecurity = "InstanceLockedForSecurity" + SystemDiskNotFound = "SystemDiskNotFound" // eip EipIncorrectStatus = "IncorrectEipStatus" InstanceIncorrectStatus = "IncorrectInstanceStatus" diff --git a/builtin/providers/alicloud/extension_ecs.go b/builtin/providers/alicloud/extension_ecs.go index 091bd9708..df21138bf 100644 --- a/builtin/providers/alicloud/extension_ecs.go +++ b/builtin/providers/alicloud/extension_ecs.go @@ -30,3 +30,8 @@ const ( GroupRulePolicyAccept = GroupRulePolicy("accept") GroupRulePolicyDrop = GroupRulePolicy("drop") ) + +const ( + EcsApiVersion20160314 = "2016-03-14" + EcsApiVersion20140526 = "2014-05-26" +) diff --git a/builtin/providers/alicloud/extension_slb.go b/builtin/providers/alicloud/extension_slb.go index 9213f4797..2c4cf787b 100644 --- a/builtin/providers/alicloud/extension_slb.go +++ b/builtin/providers/alicloud/extension_slb.go @@ -8,13 +8,41 @@ import ( ) type Listener struct { + slb.HTTPListenerType + InstancePort int LoadBalancerPort int Protocol string + //tcp & udp + PersistenceTimeout int + + //https SSLCertificateId string - Bandwidth int + + //tcp + HealthCheckType slb.HealthCheckType + + //api interface: http & https is HealthCheckTimeout, tcp & udp is HealthCheckConnectTimeout + HealthCheckConnectTimeout int } +type ListenerErr struct { + ErrType string + Err error +} + +func (e *ListenerErr) Error() string { + return e.ErrType + " " + e.Err.Error() + +} + +const ( + HealthCheckErrType = "healthCheckErrType" + StickySessionErrType = "stickySessionErrType" + CookieTimeOutErrType = "cookieTimeoutErrType" + CookieErrType = "cookieErrType" +) + // Takes the result of flatmap.Expand for an array of listeners and // returns ELB API compatible objects func expandListeners(configured []interface{}) ([]*Listener, error) { @@ -31,13 +59,78 @@ func expandListeners(configured []interface{}) ([]*Listener, error) { InstancePort: ip, LoadBalancerPort: lp, Protocol: data["lb_protocol"].(string), - Bandwidth: data["bandwidth"].(int), + } + + l.Bandwidth = data["bandwidth"].(int) + + if v, ok := data["scheduler"]; ok { + l.Scheduler = slb.SchedulerType(v.(string)) } if v, ok := data["ssl_certificate_id"]; ok { l.SSLCertificateId = v.(string) } + if v, ok := data["sticky_session"]; ok { + l.StickySession = slb.FlagType(v.(string)) + } + + if v, ok := data["sticky_session_type"]; ok { + l.StickySessionType = slb.StickySessionType(v.(string)) + } + + if v, ok := data["cookie_timeout"]; ok { + l.CookieTimeout = v.(int) + } + + if v, ok := data["cookie"]; ok { + l.Cookie = v.(string) + } + + if v, ok := data["persistence_timeout"]; ok { + l.PersistenceTimeout = v.(int) + } + + if v, ok := data["health_check"]; ok { + l.HealthCheck = slb.FlagType(v.(string)) + } + + if v, ok := data["health_check_type"]; ok { + l.HealthCheckType = slb.HealthCheckType(v.(string)) + } + + if v, ok := data["health_check_domain"]; ok { + l.HealthCheckDomain = v.(string) + } + + if v, ok := data["health_check_uri"]; ok { + l.HealthCheckURI = v.(string) + } + + if v, ok := data["health_check_connect_port"]; ok { + l.HealthCheckConnectPort = v.(int) + } + + if v, ok := data["healthy_threshold"]; ok { + l.HealthyThreshold = v.(int) + } + + if v, ok := data["unhealthy_threshold"]; ok { + l.UnhealthyThreshold = v.(int) + } + + if v, ok := data["health_check_timeout"]; ok { + l.HealthCheckTimeout = v.(int) + } + + if v, ok := data["health_check_interval"]; ok { + l.HealthCheckInterval = v.(int) + } + + if v, ok := data["health_check_http_code"]; ok { + l.HealthCheckHttpCode = slb.HealthCheckHttpCodeType(v.(string)) + } + var valid bool if l.SSLCertificateId != "" { // validate the protocol is correct diff --git a/builtin/providers/alicloud/provider.go b/builtin/providers/alicloud/provider.go index 907f3271d..677c1c70d 100644 --- a/builtin/providers/alicloud/provider.go +++ b/builtin/providers/alicloud/provider.go @@ -26,7 +26,7 @@ func Provider() terraform.ResourceProvider { "region": &schema.Schema{ Type: schema.TypeString, Required: true, - DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"), + DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", DEFAULT_REGION), Description: descriptions["region"], }, }, @@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider { "alicloud_disk_attachment": resourceAliyunDiskAttachment(), "alicloud_security_group": resourceAliyunSecurityGroup(), "alicloud_security_group_rule": resourceAliyunSecurityGroupRule(), + "alicloud_db_instance": resourceAlicloudDBInstance(), "alicloud_vpc": resourceAliyunVpc(), "alicloud_nat_gateway": resourceAliyunNatGateway(), //both subnet and vswith exists,cause compatible old version, and compatible aws habit. diff --git a/builtin/providers/alicloud/resource_alicloud_db_instance.go b/builtin/providers/alicloud/resource_alicloud_db_instance.go new file mode 100644 index 000000000..c19aef165 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_db_instance.go @@ -0,0 +1,545 @@ +package alicloud + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/rds" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strconv" + "strings" + "time" +) + +func resourceAlicloudDBInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceAlicloudDBInstanceCreate, + Read: resourceAlicloudDBInstanceRead, + Update: resourceAlicloudDBInstanceUpdate, + Delete: resourceAlicloudDBInstanceDelete, + + Schema: map[string]*schema.Schema{ + "engine": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{"MySQL", "SQLServer", "PostgreSQL", "PPAS"}), + ForceNew: true, + Required: true, + }, + "engine_version": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{"5.5", "5.6", "5.7", "2008r2", "2012", "9.4", "9.3"}), + ForceNew: true, + Required: true, + }, + "db_instance_class": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "db_instance_storage": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + + "instance_charge_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{string(rds.Postpaid), string(rds.Prepaid)}), + Optional: true, + ForceNew: true, + Default: rds.Postpaid, + }, + "period": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateAllowedIntValue([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 24, 36}), + Optional: true, + ForceNew: true, + Default: 1, + }, + + "zone_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "multi_az": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "db_instance_net_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{string(common.Internet), string(common.Intranet)}), + Optional: true, + }, + "allocate_public_connection": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "instance_network_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{string(common.VPC), string(common.Classic)}), + Optional: true, + Computed: true, + }, + "vswitch_id": &schema.Schema{ + Type: schema.TypeString, + ForceNew: true, + Optional: true, + }, + + "master_user_name": &schema.Schema{ + Type: schema.TypeString, + ForceNew: true, + Optional: true, + }, + "master_user_password": &schema.Schema{ + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Sensitive: true, + }, + + "preferred_backup_period": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + // terraform does not support ValidateFunc of TypeList attr + // ValidateFunc: validateAllowedStringValue([]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}), + Optional: true, + }, + "preferred_backup_time": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue(rds.BACKUP_TIME), + Optional: true, + }, + "backup_retention_period": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateIntegerInRange(7, 730), + Optional: true, + }, + + "security_ips": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Optional: true, + }, + + "port": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "connections": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "connection_string": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "ip_type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "ip_address": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Computed: true, + }, + + "db_mappings": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "db_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "character_set_name": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue(rds.CHARACTER_SET_NAME), + Required: true, + }, + "db_description": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Optional: true, + Set: resourceAlicloudDatabaseHash, + }, + }, + } +} + +func resourceAlicloudDatabaseHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%s-", m["db_name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["character_set_name"].(string))) + buf.WriteString(fmt.Sprintf("%s-", m["db_description"].(string))) + + return hashcode.String(buf.String()) +} + +func resourceAlicloudDBInstanceCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.rdsconn + + args, err := buildDBCreateOrderArgs(d, meta) + if err != nil { + return err + } + + resp, err := conn.CreateOrder(args) + + if err != nil { + return fmt.Errorf("Error creating Alicloud db instance: %#v", err) + } + + instanceId := resp.DBInstanceId + if instanceId == "" { + return fmt.Errorf("Error get Alicloud db instance id") + } + + d.SetId(instanceId) + d.Set("instance_charge_type", d.Get("instance_charge_type")) + d.Set("period", d.Get("period")) + d.Set("period_type", d.Get("period_type")) + + // wait instance status change from Creating to running + if err := conn.WaitForInstance(d.Id(), rds.Running, defaultLongTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", rds.Running, err) + } + + if err := modifySecurityIps(d.Id(), d.Get("security_ips"), meta); err != nil { + return err + } + + masterUserName := d.Get("master_user_name").(string) + masterUserPwd := d.Get("master_user_password").(string) + if masterUserName != "" && masterUserPwd != "" { + if err := client.CreateAccountByInfo(d.Id(), masterUserName, masterUserPwd); err != nil { + return fmt.Errorf("Create db account %s error: %v", masterUserName, err) + } + } + + if d.Get("allocate_public_connection").(bool) { + if err := client.AllocateDBPublicConnection(d.Id(), DB_DEFAULT_CONNECT_PORT); err != nil { + return fmt.Errorf("Allocate public connection error: %v", err) + } + } + + return resourceAlicloudDBInstanceUpdate(d, meta) +} + +func modifySecurityIps(id string, ips interface{}, meta interface{}) error { + client := meta.(*AliyunClient) + ipList := expandStringList(ips.([]interface{})) + + ipstr := strings.Join(ipList[:], COMMA_SEPARATED) + // default disable connect from outside + if ipstr == "" { + ipstr = LOCAL_HOST_IP + } + + if err := client.ModifyDBSecurityIps(id, ipstr); err != nil { + return fmt.Errorf("Error modify security ips %s: %#v", ipstr, err) + } + return nil +} + +func resourceAlicloudDBInstanceUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.rdsconn + d.Partial(true) + + if d.HasChange("db_mappings") { + o, n := d.GetChange("db_mappings") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + var allDbs []string + remove := os.Difference(ns).List() + add := ns.Difference(os).List() + + if len(remove) > 0 && len(add) > 0 { + return fmt.Errorf("Failure modify database, we neither support create and delete database simultaneous nor modify database attributes.") + } + + if len(remove) > 0 { + for _, db := range remove { + dbm, _ := db.(map[string]interface{}) + if err := conn.DeleteDatabase(d.Id(), dbm["db_name"].(string)); err != nil { + return fmt.Errorf("Failure delete database %s: %#v", dbm["db_name"].(string), err) + } + } + } + + if len(add) > 0 { + for _, db := range add { + dbm, _ := db.(map[string]interface{}) + dbName := dbm["db_name"].(string) + allDbs = append(allDbs, dbName) + + if err := client.CreateDatabaseByInfo(d.Id(), dbName, dbm["character_set_name"].(string), dbm["db_description"].(string)); err != nil { + return fmt.Errorf("Failure create database %s: %#v", dbName, err) + } + + } + } + + if err := conn.WaitForAllDatabase(d.Id(), allDbs, rds.Running, 600); err != nil { + return fmt.Errorf("Failure create database %#v", err) + } + + if user := d.Get("master_user_name").(string); user != "" { + for _, dbName := range allDbs { + if err := client.GrantDBPrivilege2Account(d.Id(), user, dbName); err != nil { + return fmt.Errorf("Failed to grant database %s readwrite privilege to account %s: %#v", dbName, user, err) + } + } + } + + d.SetPartial("db_mappings") + } + + if d.HasChange("preferred_backup_period") || d.HasChange("preferred_backup_time") || d.HasChange("backup_retention_period") { + period := d.Get("preferred_backup_period").([]interface{}) + periodList := expandStringList(period) + time := d.Get("preferred_backup_time").(string) + retention := d.Get("backup_retention_period").(int) + + if time == "" || retention == 0 || len(periodList) < 1 { + return fmt.Errorf("Both backup_time, backup_period and retention_period are required to set backup policy.") + } + + ps := strings.Join(periodList[:], COMMA_SEPARATED) + + if err := client.ConfigDBBackup(d.Id(), time, ps, retention); err != nil { + return fmt.Errorf("Error set backup policy: %#v", err) + } + d.SetPartial("preferred_backup_period") + d.SetPartial("preferred_backup_time") + d.SetPartial("backup_retention_period") + } + + if d.HasChange("security_ips") { + if err := modifySecurityIps(d.Id(), d.Get("security_ips"), meta); err != nil { + return err + } + d.SetPartial("security_ips") + } + + if d.HasChange("db_instance_class") || d.HasChange("db_instance_storage") { + co, cn := d.GetChange("db_instance_class") + so, sn := d.GetChange("db_instance_storage") + classOld := co.(string) + classNew := cn.(string) + storageOld := so.(int) + storageNew := sn.(int) + + // update except the first time, because we will do it in create function + if classOld != "" && storageOld != 0 { + chargeType := d.Get("instance_charge_type").(string) + if chargeType == string(rds.Prepaid) { + return fmt.Errorf("Prepaid db instance does not support modify db_instance_class or db_instance_storage") + } + + if err := client.ModifyDBClassStorage(d.Id(), classNew, strconv.Itoa(storageNew)); err != nil { + return fmt.Errorf("Error modify db instance class or storage error: %#v", err) + } + } + } + + d.Partial(false) + return resourceAlicloudDBInstanceRead(d, meta) +} + +func resourceAlicloudDBInstanceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*AliyunClient) + conn := client.rdsconn + + instance, err := client.DescribeDBInstanceById(d.Id()) + if err != nil { + if notFoundError(err) { + d.SetId("") + return nil + } + return fmt.Errorf("Error Describe DB InstanceAttribute: %#v", err) + } + + args := rds.DescribeDatabasesArgs{ + DBInstanceId: d.Id(), + } + + resp, err := conn.DescribeDatabases(&args) + if err != nil { + return err + } + d.Set("db_mappings", flattenDatabaseMappings(resp.Databases.Database)) + + argn := rds.DescribeDBInstanceNetInfoArgs{ + DBInstanceId: d.Id(), + } + + resn, err := conn.DescribeDBInstanceNetInfo(&argn) + if err != nil { + return err + } + d.Set("connections", flattenDBConnections(resn.DBInstanceNetInfos.DBInstanceNetInfo)) + + ips, err := client.GetSecurityIps(d.Id()) + if err != nil { + log.Printf("Describe DB security ips error: %#v", err) + } + d.Set("security_ips", ips) + + d.Set("engine", instance.Engine) + d.Set("engine_version", instance.EngineVersion) + d.Set("db_instance_class", instance.DBInstanceClass) + d.Set("port", instance.Port) + d.Set("db_instance_storage", instance.DBInstanceStorage) + d.Set("zone_id", instance.ZoneId) + d.Set("db_instance_net_type", instance.DBInstanceNetType) + d.Set("instance_network_type", instance.InstanceNetworkType) + + return nil +} + +func resourceAlicloudDBInstanceDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).rdsconn + + return resource.Retry(5*time.Minute, func() *resource.RetryError { + err := conn.DeleteInstance(d.Id()) + + if err != nil { + return resource.RetryableError(fmt.Errorf("DB Instance in use - trying again while it is deleted.")) + } + + args := &rds.DescribeDBInstancesArgs{ + DBInstanceId: d.Id(), + } + resp, err := conn.DescribeDBInstanceAttribute(args) + if err != nil { + return resource.NonRetryableError(err) + } else if len(resp.Items.DBInstanceAttribute) < 1 { + return nil + } + + return resource.RetryableError(fmt.Errorf("DB in use - trying again while it is deleted.")) + }) +} + +func buildDBCreateOrderArgs(d *schema.ResourceData, meta interface{}) (*rds.CreateOrderArgs, error) { + client := meta.(*AliyunClient) + args := &rds.CreateOrderArgs{ + RegionId: getRegion(d, meta), + // we does not expose this param to user, + // because create prepaid instance progress will be stopped when set auto_pay to false, + // then could not get instance info, cause timeout error + AutoPay: "true", + EngineVersion: d.Get("engine_version").(string), + Engine: rds.Engine(d.Get("engine").(string)), + DBInstanceStorage: d.Get("db_instance_storage").(int), + DBInstanceClass: d.Get("db_instance_class").(string), + Quantity: DEFAULT_INSTANCE_COUNT, + Resource: rds.DefaultResource, + } + + bussStr, err := json.Marshal(DefaultBusinessInfo) + if err != nil { + return nil, fmt.Errorf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo) + } + + args.BusinessInfo = string(bussStr) + + zoneId := d.Get("zone_id").(string) + args.ZoneId = zoneId + + multiAZ := d.Get("multi_az").(bool) + if multiAZ { + if zoneId != "" { + return nil, fmt.Errorf("You cannot set the ZoneId parameter when the MultiAZ parameter is set to true") + } + izs, err := client.DescribeMultiIZByRegion() + if err != nil { + return nil, fmt.Errorf("Get multiAZ id error") + } + + if len(izs) < 1 { + return nil, fmt.Errorf("Current region does not support MultiAZ.") + } + + args.ZoneId = izs[0] + } + + vswitchId := d.Get("vswitch_id").(string) + + networkType := d.Get("instance_network_type").(string) + args.InstanceNetworkType = common.NetworkType(networkType) + + if vswitchId != "" { + args.VSwitchId = vswitchId + + // check InstanceNetworkType with vswitchId + if networkType == string(common.Classic) { + return nil, fmt.Errorf("When fill vswitchId, you shold set instance_network_type to VPC") + } else if networkType == "" { + args.InstanceNetworkType = common.VPC + } + + // get vpcId + vpcId, err := client.GetVpcIdByVSwitchId(vswitchId) + + if err != nil { + return nil, fmt.Errorf("VswitchId %s is not valid of current region", vswitchId) + } + // fill vpcId by vswitchId + args.VPCId = vpcId + + // check vswitchId in zone + vsw, err := client.QueryVswitchById(vpcId, vswitchId) + if err != nil { + return nil, fmt.Errorf("VswitchId %s is not valid of current region", vswitchId) + } + + if zoneId == "" { + args.ZoneId = vsw.ZoneId + } else if vsw.ZoneId != zoneId { + return nil, fmt.Errorf("VswitchId %s is not belong to the zone %s", vswitchId, zoneId) + } + } + + if v := d.Get("db_instance_net_type").(string); v != "" { + args.DBInstanceNetType = common.NetType(v) + } + + chargeType := d.Get("instance_charge_type").(string) + if chargeType != "" { + args.PayType = rds.DBPayType(chargeType) + } else { + args.PayType = rds.Postpaid + } + + // if charge type is postpaid, the commodity code must set to bards + if chargeType == string(rds.Postpaid) { + args.CommodityCode = rds.Bards + } else { + args.CommodityCode = rds.Rds + } + + period := d.Get("period").(int) + args.UsedTime, args.TimeType = TransformPeriod2Time(period, chargeType) + + return args, nil +} diff --git a/builtin/providers/alicloud/resource_alicloud_db_instance_test.go b/builtin/providers/alicloud/resource_alicloud_db_instance_test.go new file mode 100644 index 000000000..8348e5089 --- /dev/null +++ b/builtin/providers/alicloud/resource_alicloud_db_instance_test.go @@ -0,0 +1,765 @@ +package alicloud + +import ( + "fmt" + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/rds" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "log" + "strings" + "testing" +) + +func TestAccAlicloudDBInstance_basic(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstanceConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "port", + "3306"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_storage", + "10"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "instance_network_type", + "Classic"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_net_type", + "Intranet"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine_version", + "5.6"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine", + "MySQL"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_vpc(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_vpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "port", + "3306"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_storage", + "10"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "instance_network_type", + "VPC"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_net_type", + "Intranet"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine_version", + "5.6"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine", + "MySQL"), + ), + }, + }, + }) + +} + +func TestC2CAlicloudDBInstance_prepaid_order(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_prepaid_order, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "port", + "3306"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_storage", + "10"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "instance_network_type", + "VPC"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "db_instance_net_type", + "Intranet"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine_version", + "5.6"), + resource.TestCheckResourceAttr( + "alicloud_db_instance.foo", + "engine", + "MySQL"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_multiIZ(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_multiIZ, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + testAccCheckDBInstanceMultiIZ(&instance), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_database(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_database, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"), + ), + }, + + resource.TestStep{ + Config: testAccDBInstance_database_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "3"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_account(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_grantDatabasePrivilege2Account, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"), + testAccCheckAccountHasPrivilege2Database("alicloud_db_instance.foo", "tester", "foo", "ReadWrite"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_allocatePublicConnection(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_allocatePublicConnection, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "connections.#", "2"), + testAccCheckHasPublicConnection("alicloud_db_instance.foo"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_backupPolicy(t *testing.T) { + var policies []map[string]interface{} + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_backup, + Check: resource.ComposeTestCheckFunc( + testAccCheckBackupPolicyExists( + "alicloud_db_instance.foo", policies), + testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_period", "Wednesday,Thursday"), + testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_time", "00:00Z-01:00Z"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_securityIps(t *testing.T) { + var ips []map[string]interface{} + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_securityIps, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityIpExists( + "alicloud_db_instance.foo", ips), + testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "127.0.0.1"), + ), + }, + + resource.TestStep{ + Config: testAccDBInstance_securityIpsConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityIpExists( + "alicloud_db_instance.foo", ips), + testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "10.168.1.12,100.69.7.112"), + ), + }, + }, + }) + +} + +func TestAccAlicloudDBInstance_upgradeClass(t *testing.T) { + var instance rds.DBInstanceAttribute + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_db_instance.foo", + + Providers: testAccProviders, + CheckDestroy: testAccCheckDBInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccDBInstance_class, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.t1.small"), + ), + }, + + resource.TestStep{ + Config: testAccDBInstance_classUpgrade, + Check: resource.ComposeTestCheckFunc( + testAccCheckDBInstanceExists( + "alicloud_db_instance.foo", &instance), + resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.s1.small"), + ), + }, + }, + }) + +} + +func testAccCheckSecurityIpExists(n string, ips []map[string]interface{}) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB Instance ID is set") + } + + conn := testAccProvider.Meta().(*AliyunClient).rdsconn + args := rds.DescribeDBInstanceIPsArgs{ + DBInstanceId: rs.Primary.ID, + } + + resp, err := conn.DescribeDBInstanceIPs(&args) + log.Printf("[DEBUG] check instance %s security ip %#v", rs.Primary.ID, resp) + + if err != nil { + return err + } + + p := resp.Items.DBInstanceIPArray + + if len(p) < 1 { + return fmt.Errorf("DB security ip not found") + } + + ips = flattenDBSecurityIPs(p) + return nil + } +} + +func testAccCheckDBInstanceMultiIZ(i *rds.DBInstanceAttribute) resource.TestCheckFunc { + return func(s *terraform.State) error { + if !strings.Contains(i.ZoneId, MULTI_IZ_SYMBOL) { + return fmt.Errorf("Current region does not support multiIZ.") + } + return nil + } +} + +func testAccCheckAccountHasPrivilege2Database(n, accountName, dbName, privilege string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB instance ID is set") + } + + conn := testAccProvider.Meta().(*AliyunClient).rdsconn + if err := conn.WaitForAccountPrivilege(rs.Primary.ID, accountName, dbName, rds.AccountPrivilege(privilege), 50); err != nil { + return fmt.Errorf("Failed to grant database %s privilege to account %s: %v", dbName, accountName, err) + } + return nil + } +} + +func testAccCheckHasPublicConnection(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB instance ID is set") + } + + conn := testAccProvider.Meta().(*AliyunClient).rdsconn + if err := conn.WaitForPublicConnection(rs.Primary.ID, 50); err != nil { + return fmt.Errorf("Failed to allocate public connection: %v", err) + } + return nil + } +} + +func testAccCheckDBInstanceExists(n string, d *rds.DBInstanceAttribute) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB Instance ID is set") + } + + client := testAccProvider.Meta().(*AliyunClient) + attr, err := client.DescribeDBInstanceById(rs.Primary.ID) + log.Printf("[DEBUG] check instance %s attribute %#v", rs.Primary.ID, attr) + + if err != nil { + return err + } + + if attr == nil { + return fmt.Errorf("DB Instance not found") + } + + *d = *attr + return nil + } +} + +func testAccCheckBackupPolicyExists(n string, ps []map[string]interface{}) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Backup policy not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No DB Instance ID is set") + } + + conn := testAccProvider.Meta().(*AliyunClient).rdsconn + + args := rds.DescribeBackupPolicyArgs{ + DBInstanceId: rs.Primary.ID, + } + resp, err := conn.DescribeBackupPolicy(&args) + log.Printf("[DEBUG] check instance %s backup policy %#v", rs.Primary.ID, resp) + + if err != nil { + return err + } + + var bs []rds.BackupPolicy + bs = append(bs, resp.BackupPolicy) + ps = flattenDBBackup(bs) + + return nil + } +} + +func testAccCheckKeyValueInMaps(ps []map[string]interface{}, propName, key, value string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, policy := range ps { + if policy[key].(string) != value { + return fmt.Errorf("DB %s attribute '%s' expected %#v, got %#v", propName, key, value, policy[key]) + } + } + return nil + } +} + +func testAccCheckDBInstanceDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*AliyunClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "alicloud_db_instance.foo" { + continue + } + + ins, err := client.DescribeDBInstanceById(rs.Primary.ID) + + if ins != nil { + return fmt.Errorf("Error DB Instance still exist") + } + + // Verify the error is what we want + if err != nil { + // Verify the error is what we want + e, _ := err.(*common.Error) + if e.ErrorResponse.Code == InstanceNotfound { + continue + } + return err + } + } + + return nil +} + +const testAccDBInstanceConfig = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" +} +` + +const testAccDBInstance_vpc = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + +resource "alicloud_vpc" "foo" { + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" +} + +resource "alicloud_vswitch" "foo" { + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" +} + +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + vswitch_id = "${alicloud_vswitch.foo.id}" +} +` +const testAccDBInstance_multiIZ = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + db_instance_net_type = "Intranet" + multi_az = true +} +` + +const testAccDBInstance_prepaid_order = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Prepaid" + db_instance_net_type = "Intranet" +} +` + +const testAccDBInstance_database = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + db_mappings = [ + { + "db_name" = "foo" + "character_set_name" = "utf8" + "db_description" = "tf" + },{ + "db_name" = "bar" + "character_set_name" = "utf8" + "db_description" = "tf" + }] +} +` +const testAccDBInstance_database_update = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + db_mappings = [ + { + "db_name" = "foo" + "character_set_name" = "utf8" + "db_description" = "tf" + },{ + "db_name" = "bar" + "character_set_name" = "utf8" + "db_description" = "tf" + },{ + "db_name" = "zzz" + "character_set_name" = "utf8" + "db_description" = "tf" + }] +} +` + +const testAccDBInstance_grantDatabasePrivilege2Account = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + master_user_name = "tester" + master_user_password = "Test12345" + + db_mappings = [ + { + "db_name" = "foo" + "character_set_name" = "utf8" + "db_description" = "tf" + },{ + "db_name" = "bar" + "character_set_name" = "utf8" + "db_description" = "tf" + }] +} +` + +const testAccDBInstance_allocatePublicConnection = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + master_user_name = "tester" + master_user_password = "Test12345" + + allocate_public_connection = true +} +` + +const testAccDBInstance_backup = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + preferred_backup_period = ["Wednesday","Thursday"] + preferred_backup_time = "00:00Z-01:00Z" + backup_retention_period = 9 +} +` + +const testAccDBInstance_securityIps = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" +} +` +const testAccDBInstance_securityIpsConfig = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + instance_charge_type = "Postpaid" + db_instance_net_type = "Intranet" + + security_ips = ["10.168.1.12", "100.69.7.112"] +} +` + +const testAccDBInstance_class = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + db_instance_net_type = "Intranet" +} +` +const testAccDBInstance_classUpgrade = ` +resource "alicloud_db_instance" "foo" { + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.s1.small" + db_instance_storage = "10" + db_instance_net_type = "Intranet" +} +` diff --git a/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go index a0fe32a0a..00239f5c5 100644 --- a/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go +++ b/builtin/providers/alicloud/resource_alicloud_disk_attachment_test.go @@ -151,4 +151,5 @@ resource "alicloud_security_group" "group" { name = "terraform-test-group" description = "New security group" } + ` diff --git a/builtin/providers/alicloud/resource_alicloud_disk_test.go b/builtin/providers/alicloud/resource_alicloud_disk_test.go index 6cb55bd8e..b8d73a662 100644 --- a/builtin/providers/alicloud/resource_alicloud_disk_test.go +++ b/builtin/providers/alicloud/resource_alicloud_disk_test.go @@ -136,9 +136,13 @@ func testAccCheckDiskDestroy(s *terraform.State) error { } const testAccDiskConfig = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" +} + resource "alicloud_disk" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" name = "New-disk" description = "Hello ecs disk." category = "cloud_efficiency" @@ -146,10 +150,15 @@ resource "alicloud_disk" "foo" { } ` const testAccDiskConfigWithTags = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" +} + resource "alicloud_disk" "bar" { # cn-beijing - availability_zone = "cn-beijing-b" - size = "10" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" + category = "cloud_efficiency" + size = "20" tags { Name = "TerraformTest" } diff --git a/builtin/providers/alicloud/resource_alicloud_eip_association_test.go b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go index b039248af..37c79f005 100644 --- a/builtin/providers/alicloud/resource_alicloud_eip_association_test.go +++ b/builtin/providers/alicloud/resource_alicloud_eip_association_test.go @@ -108,6 +108,10 @@ func testAccCheckEIPAssociationDestroy(s *terraform.State) error { } const testAccEIPAssociationConfig = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "main" { cidr_block = "10.1.0.0/21" } @@ -115,19 +119,23 @@ resource "alicloud_vpc" "main" { resource "alicloud_vswitch" "main" { vpc_id = "${alicloud_vpc.main.id}" cidr_block = "10.1.1.0/24" - availability_zone = "cn-beijing-a" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" depends_on = [ "alicloud_vpc.main"] } resource "alicloud_instance" "instance" { - image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" - instance_type = "ecs.s1.small" - availability_zone = "cn-beijing-a" - security_groups = ["${alicloud_security_group.group.id}"] + # cn-beijing vswitch_id = "${alicloud_vswitch.main.id}" - instance_name = "hello" - io_optimized = "none" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + + # series II + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + + security_groups = ["${alicloud_security_group.group.id}"] + instance_name = "test_foo" tags { Name = "TerraformTest-instance" diff --git a/builtin/providers/alicloud/resource_alicloud_instance.go b/builtin/providers/alicloud/resource_alicloud_instance.go index aeac4b3af..fe221f17b 100644 --- a/builtin/providers/alicloud/resource_alicloud_instance.go +++ b/builtin/providers/alicloud/resource_alicloud_instance.go @@ -5,6 +5,7 @@ import ( "log" "encoding/base64" + "encoding/json" "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/schema" @@ -21,8 +22,9 @@ func resourceAliyunInstance() *schema.Resource { Schema: map[string]*schema.Schema{ "availability_zone": &schema.Schema{ Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, + Computed: true, }, "image_id": &schema.Schema{ @@ -60,11 +62,6 @@ func resourceAliyunInstance() *schema.Resource { ValidateFunc: validateInstanceDescription, }, - "instance_network_type": &schema.Schema{ - Type: schema.TypeString, - Computed: true, - }, - "internet_charge_type": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -104,11 +101,19 @@ func resourceAliyunInstance() *schema.Resource { Default: "cloud", Optional: true, ForceNew: true, + ValidateFunc: validateAllowedStringValue([]string{ + string(ecs.DiskCategoryCloud), + string(ecs.DiskCategoryCloudSSD), + string(ecs.DiskCategoryCloudEfficiency), + string(ecs.DiskCategoryEphemeralSSD), + }), }, "system_disk_size": &schema.Schema{ - Type: schema.TypeInt, - Optional: true, - Computed: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateIntegerInRange(40, 500), }, //subnet_id and vswitch_id both exists, cause compatible old version, and aws habit. @@ -145,7 +150,6 @@ func resourceAliyunInstance() *schema.Resource { "private_ip": &schema.Schema{ Type: schema.TypeString, - Optional: true, Computed: true, }, @@ -168,6 +172,11 @@ func resourceAliyunInstance() *schema.Resource { func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AliyunClient).ecsconn + // create postpaid instance by runInstances API + if v := d.Get("instance_charge_type").(string); v != string(common.PrePaid) { + return resourceAliyunRunInstance(d, meta) + } + args, err := buildAliyunInstanceArgs(d, meta) if err != nil { return err @@ -181,7 +190,8 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(instanceID) d.Set("password", d.Get("password")) - d.Set("system_disk_category", d.Get("system_disk_category")) + //d.Set("system_disk_category", d.Get("system_disk_category")) + //d.Set("system_disk_size", d.Get("system_disk_size")) if d.Get("allocate_public_ip").(bool) { _, err := conn.AllocatePublicIpAddress(d.Id()) @@ -207,11 +217,56 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro return resourceAliyunInstanceUpdate(d, meta) } +func resourceAliyunRunInstance(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AliyunClient).ecsconn + newConn := meta.(*AliyunClient).ecsNewconn + + args, err := buildAliyunInstanceArgs(d, meta) + if err != nil { + return err + } + + runArgs, err := buildAliyunRunInstancesArgs(d, meta) + if err != nil { + return err + } + + runArgs.CreateInstanceArgs = *args + + // runInstances is support in version 2016-03-14 + instanceIds, err := newConn.RunInstances(runArgs) + + if err != nil { + return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err) + } + + d.SetId(instanceIds[0]) + + d.Set("password", d.Get("password")) + d.Set("system_disk_category", d.Get("system_disk_category")) + d.Set("system_disk_size", d.Get("system_disk_size")) + + if d.Get("allocate_public_ip").(bool) { + _, err := conn.AllocatePublicIpAddress(d.Id()) + if err != nil { + log.Printf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err) + } + } + + // after instance created, its status change from pending, starting to running + if err := conn.WaitForInstanceAsyn(d.Id(), ecs.Running, defaultTimeout); err != nil { + log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err) + } + + return resourceAliyunInstanceUpdate(d, meta) +} + func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) conn := client.ecsconn instance, err := client.QueryInstancesById(d.Id()) + if err != nil { if notFoundError(err) { d.SetId("") @@ -220,7 +275,15 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err) } - log.Printf("[DEBUG] DescribeInstanceAttribute for instance: %#v", instance) + disk, diskErr := client.QueryInstanceSystemDisk(d.Id()) + + if diskErr != nil { + if notFoundError(diskErr) { + d.SetId("") + return nil + } + return fmt.Errorf("Error DescribeSystemDisk: %#v", err) + } d.Set("instance_name", instance.InstanceName) d.Set("description", instance.Description) @@ -229,6 +292,8 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("host_name", instance.HostName) d.Set("image_id", instance.ImageId) d.Set("instance_type", instance.InstanceType) + d.Set("system_disk_category", disk.Category) + d.Set("system_disk_size", disk.Size) // In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'. // In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'. @@ -244,10 +309,6 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error d.Set("io_optimized", "none") } - log.Printf("instance.InternetChargeType: %#v", instance.InternetChargeType) - - d.Set("instance_network_type", instance.InstanceNetworkType) - if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" { ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] d.Set("private_ip", ipAddress) @@ -414,33 +475,71 @@ func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) erro return nil } +func buildAliyunRunInstancesArgs(d *schema.ResourceData, meta interface{}) (*ecs.RunInstanceArgs, error) { + args := &ecs.RunInstanceArgs{ + MaxAmount: DEFAULT_INSTANCE_COUNT, + MinAmount: DEFAULT_INSTANCE_COUNT, + } + + bussStr, err := json.Marshal(DefaultBusinessInfo) + if err != nil { + log.Printf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo) + } + + args.BusinessInfo = string(bussStr) + + subnetValue := d.Get("subnet_id").(string) + vswitchValue := d.Get("vswitch_id").(string) + //networkValue := d.Get("instance_network_type").(string) + + // because runInstance is not compatible with createInstance, force NetworkType value to classic + if subnetValue == "" && vswitchValue == "" { + args.NetworkType = string(ClassicNet) + } + + return args, nil +} func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) { client := meta.(*AliyunClient) args := &ecs.CreateInstanceArgs{ - RegionId: getRegion(d, meta), - InstanceType: d.Get("instance_type").(string), - PrivateIpAddress: d.Get("private_ip").(string), + RegionId: getRegion(d, meta), + InstanceType: d.Get("instance_type").(string), } imageID := d.Get("image_id").(string) args.ImageId = imageID + systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string)) + systemDiskSize := d.Get("system_disk_size").(int) + zoneID := d.Get("availability_zone").(string) + // check instanceType and systemDiskCategory, when zoneID is not empty + if zoneID != "" { + zone, err := client.DescribeZone(zoneID) + if err != nil { + return nil, err + } + + if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil { + return nil, err + } + + if err := client.DiskAvailable(zone, systemDiskCategory); err != nil { + return nil, err + } + + args.ZoneId = zoneID - zone, err := client.DescribeZone(zoneID) - if err != nil { - return nil, err } - if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil { - return nil, err + args.SystemDisk = ecs.SystemDiskType{ + Category: systemDiskCategory, + Size: systemDiskSize, } - args.ZoneId = zoneID - sgs, ok := d.GetOk("security_groups") if ok { @@ -451,17 +550,6 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre if err == nil { args.SecurityGroupId = sg0 } - - } - - systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string)) - - if err := client.DiskAvailable(zone, systemDiskCategory); err != nil { - return nil, err - } - - args.SystemDisk = ecs.SystemDiskType{ - Category: systemDiskCategory, } if v := d.Get("instance_name").(string); v != "" { @@ -472,7 +560,7 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre args.Description = v } - log.Printf("[DEBUG] internet_charge_type is %s", d.Get("internet_charge_type").(string)) + log.Printf("[DEBUG] SystemDisk is %d", systemDiskSize) if v := d.Get("internet_charge_type").(string); v != "" { args.InternetChargeType = common.InternetChargeType(v) } @@ -490,7 +578,11 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre } if v := d.Get("io_optimized").(string); v != "" { - args.IoOptimized = ecs.IoOptimized(v) + if v == "optimized" { + args.IoOptimized = ecs.IoOptimized("true") + } else { + args.IoOptimized = ecs.IoOptimized("false") + } } vswitchValue := d.Get("subnet_id").(string) diff --git a/builtin/providers/alicloud/resource_alicloud_instance_test.go b/builtin/providers/alicloud/resource_alicloud_instance_test.go index 2fb232f3c..4e8f0c716 100644 --- a/builtin/providers/alicloud/resource_alicloud_instance_test.go +++ b/builtin/providers/alicloud/resource_alicloud_instance_test.go @@ -56,6 +56,7 @@ func TestAccAlicloudInstance_basic(t *testing.T) { "alicloud_instance.foo", "internet_charge_type", "PayByBandwidth"), + testAccCheckSystemDiskSize("alicloud_instance.foo", 80), ), }, @@ -355,10 +356,6 @@ func TestAccAlicloudInstance_tags(t *testing.T) { Config: testAccCheckInstanceConfigTagsUpdate, Check: resource.ComposeTestCheckFunc( testAccCheckInstanceExists("alicloud_instance.foo", &instance), - resource.TestCheckResourceAttr( - "alicloud_instance.foo", - "tags.foo", - ""), resource.TestCheckResourceAttr( "alicloud_instance.foo", "tags.bar", @@ -418,8 +415,8 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) { testCheckPrivateIP := func() resource.TestCheckFunc { return func(*terraform.State) error { privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] - if privateIP != "172.16.0.229" { - return fmt.Errorf("bad private IP: %s", privateIP) + if privateIP == "" { + return fmt.Errorf("can't get private IP") } return nil @@ -445,14 +442,14 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) { }) } -func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) { +func TestAccAlicloudInstance_associatePublicIP(t *testing.T) { var instance ecs.InstanceAttributesType testCheckPrivateIP := func() resource.TestCheckFunc { return func(*terraform.State) error { privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0] - if privateIP != "172.16.0.229" { - return fmt.Errorf("bad private IP: %s", privateIP) + if privateIP == "" { + return fmt.Errorf("can't get private IP") } return nil @@ -468,7 +465,7 @@ func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) { CheckDestroy: testAccCheckInstanceDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP, + Config: testAccInstanceConfigAssociatePublicIP, Check: resource.ComposeTestCheckFunc( testAccCheckInstanceExists("alicloud_instance.foo", &instance), testCheckPrivateIP(), @@ -597,6 +594,36 @@ func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schem return nil } +func testAccCheckSystemDiskSize(n string, size int) resource.TestCheckFunc { + return func(s *terraform.State) error { + providers := []*schema.Provider{testAccProvider} + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + for _, provider := range providers { + if provider.Meta() == nil { + continue + } + client := provider.Meta().(*AliyunClient) + systemDisk, err := client.QueryInstanceSystemDisk(rs.Primary.ID) + if err != nil { + log.Printf("[ERROR]get system disk size error: %#v", err) + return err + } + + if systemDisk.Size != size { + return fmt.Errorf("system disk size not equal %d, the instance system size is %d", + size, systemDisk.Size) + } + } + + return nil + } +} + const testAccInstanceConfig = ` resource "alicloud_security_group" "tf_test_foo" { name = "tf_test_foo" @@ -609,11 +636,10 @@ resource "alicloud_security_group" "tf_test_bar" { } resource "alicloud_instance" "foo" { - # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" system_disk_category = "cloud_ssd" + system_disk_size = 80 instance_type = "ecs.n1.small" internet_charge_type = "PayByBandwidth" @@ -628,15 +654,20 @@ resource "alicloud_instance" "foo" { } ` const testAccInstanceConfigVPC = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "172.16.0.0/12" + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { @@ -647,7 +678,6 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" vswitch_id = "${alicloud_vswitch.foo.id}" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" @@ -666,15 +696,20 @@ resource "alicloud_instance" "foo" { ` const testAccInstanceConfigUserData = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "172.16.0.0/12" + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { @@ -685,7 +720,6 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" vswitch_id = "${alicloud_vswitch.foo.id}" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" # series II @@ -725,24 +759,22 @@ resource "alicloud_security_group" "tf_test_bar" { } resource "alicloud_instance" "foo" { - # cn-beijing - provider = "alicloud.beijing" - availability_zone = "cn-beijing-b" - image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" + # cn-beijing + provider = "alicloud.beijing" + image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" - internet_charge_type = "PayByBandwidth" + internet_charge_type = "PayByBandwidth" - instance_type = "ecs.n1.medium" - io_optimized = "optimized" - system_disk_category = "cloud_efficiency" - security_groups = ["${alicloud_security_group.tf_test_foo.id}"] - instance_name = "test_foo" + instance_type = "ecs.n1.medium" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + instance_name = "test_foo" } resource "alicloud_instance" "bar" { # cn-shanghai provider = "alicloud.shanghai" - availability_zone = "cn-shanghai-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" internet_charge_type = "PayByBandwidth" @@ -768,7 +800,6 @@ resource "alicloud_security_group" "tf_test_bar" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" instance_type = "ecs.s2.large" @@ -776,6 +807,7 @@ resource "alicloud_instance" "foo" { security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"] instance_name = "test_foo" io_optimized = "optimized" + system_disk_category = "cloud_efficiency" }` const testAccInstanceConfig_multiSecurityGroup_add = ` @@ -796,7 +828,6 @@ resource "alicloud_security_group" "tf_test_add_sg" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" instance_type = "ecs.s2.large" @@ -805,6 +836,7 @@ resource "alicloud_instance" "foo" { "${alicloud_security_group.tf_test_add_sg.id}"] instance_name = "test_foo" io_optimized = "optimized" + system_disk_category = "cloud_efficiency" } ` @@ -814,9 +846,30 @@ resource "alicloud_security_group" "tf_test_foo" { description = "foo" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" instance_type = "ecs.s2.large" @@ -824,6 +877,7 @@ resource "alicloud_instance" "foo" { security_groups = ["${alicloud_security_group.tf_test_foo.id}"] instance_name = "test_foo" io_optimized = "optimized" + system_disk_category = "cloud_efficiency" } ` @@ -836,27 +890,32 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" instance_type = "ecs.s2.large" internet_charge_type = "PayByBandwidth" security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"] instance_name = "test_foo" - io_optimized = "none" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" } ` const testAccInstanceNetworkInstanceSecurityGroups = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "172.16.0.0/12" + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/21" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { @@ -867,7 +926,6 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" vswitch_id = "${alicloud_vswitch.foo.id}" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" @@ -892,7 +950,6 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" # series II @@ -918,7 +975,6 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" # series II @@ -941,9 +997,30 @@ resource "alicloud_security_group" "tf_test_foo" { description = "foo" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" # series II @@ -965,9 +1042,30 @@ resource "alicloud_security_group" "tf_test_foo" { description = "foo" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" # series II @@ -984,15 +1082,20 @@ resource "alicloud_instance" "foo" { ` const testAccInstanceConfigPrivateIP = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "172.16.0.0/12" + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "172.16.0.0/24" - availability_zone = "cn-beijing-b" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { @@ -1003,11 +1106,9 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" security_groups = ["${alicloud_security_group.tf_test_foo.id}"] vswitch_id = "${alicloud_vswitch.foo.id}" - private_ip = "172.16.0.229" # series II instance_type = "ecs.n1.medium" @@ -1017,16 +1118,21 @@ resource "alicloud_instance" "foo" { instance_name = "test_foo" } ` -const testAccInstanceConfigAssociatePublicIPAndPrivateIP = ` +const testAccInstanceConfigAssociatePublicIP = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "172.16.0.0/12" + name = "tf_test_foo" + cidr_block = "172.16.0.0/12" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "172.16.0.0/24" - availability_zone = "cn-beijing-b" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "172.16.0.0/24" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { @@ -1037,11 +1143,9 @@ resource "alicloud_security_group" "tf_test_foo" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" security_groups = ["${alicloud_security_group.tf_test_foo.id}"] vswitch_id = "${alicloud_vswitch.foo.id}" - private_ip = "172.16.0.229" allocate_public_ip = "true" internet_max_bandwidth_out = 5 internet_charge_type = "PayByBandwidth" @@ -1055,52 +1159,56 @@ resource "alicloud_instance" "foo" { } ` const testAccVpcInstanceWithSecurityRule = ` +data "alicloud_zones" "default" { + "available_disk_category"= "cloud_efficiency" + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { - name = "tf_test_foo" - cidr_block = "10.1.0.0/21" + name = "tf_test_foo" + cidr_block = "10.1.0.0/21" } resource "alicloud_vswitch" "foo" { - vpc_id = "${alicloud_vpc.foo.id}" - cidr_block = "10.1.1.0/24" - availability_zone = "cn-beijing-c" + vpc_id = "${alicloud_vpc.foo.id}" + cidr_block = "10.1.1.0/24" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_security_group" "tf_test_foo" { - name = "tf_test_foo" - description = "foo" - vpc_id = "${alicloud_vpc.foo.id}" + name = "tf_test_foo" + description = "foo" + vpc_id = "${alicloud_vpc.foo.id}" } resource "alicloud_security_group_rule" "ingress" { - type = "ingress" - ip_protocol = "tcp" - nic_type = "intranet" - policy = "accept" - port_range = "22/22" - priority = 1 - security_group_id = "${alicloud_security_group.tf_test_foo.id}" - cidr_ip = "0.0.0.0/0" + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.tf_test_foo.id}" + cidr_ip = "0.0.0.0/0" } resource "alicloud_instance" "foo" { - # cn-beijing - availability_zone = "cn-beijing-c" - security_groups = ["${alicloud_security_group.tf_test_foo.id}"] + # cn-beijing + security_groups = ["${alicloud_security_group.tf_test_foo.id}"] - vswitch_id = "${alicloud_vswitch.foo.id}" - allocate_public_ip = true + vswitch_id = "${alicloud_vswitch.foo.id}" + allocate_public_ip = true - # series II - instance_charge_type = "PostPaid" - instance_type = "ecs.n1.small" - internet_charge_type = "PayByBandwidth" - internet_max_bandwidth_out = 5 + # series II + instance_charge_type = "PostPaid" + instance_type = "ecs.n1.small" + internet_charge_type = "PayByBandwidth" + internet_max_bandwidth_out = 5 - system_disk_category = "cloud_efficiency" - image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" - instance_name = "test_foo" - io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "test_foo" + io_optimized = "optimized" } ` diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go index 51622d86e..99e71347a 100644 --- a/builtin/providers/alicloud/resource_alicloud_nat_gateway.go +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "log" @@ -71,7 +72,7 @@ func resourceAliyunNatGateway() *schema.Resource { func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AliyunClient).vpcconn - args := &CreateNatGatewayArgs{ + args := &ecs.CreateNatGatewayArgs{ RegionId: getRegion(d, meta), VpcId: d.Get("vpc_id").(string), Spec: d.Get("spec").(string), @@ -79,11 +80,11 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er bandwidthPackages := d.Get("bandwidth_packages").([]interface{}) - bandwidthPackageTypes := []BandwidthPackageType{} + bandwidthPackageTypes := []ecs.BandwidthPackageType{} for _, e := range bandwidthPackages { pack := e.(map[string]interface{}) - bandwidthPackage := BandwidthPackageType{ + bandwidthPackage := ecs.BandwidthPackageType{ IpCount: pack["ip_count"].(int), Bandwidth: pack["bandwidth"].(int), } @@ -106,8 +107,7 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er if v, ok := d.GetOk("description"); ok { args.Description = v.(string) } - - resp, err := CreateNatGateway(conn, args) + resp, err := conn.CreateNatGateway(args) if err != nil { return fmt.Errorf("CreateNatGateway got error: %#v", err) } @@ -142,6 +142,7 @@ func resourceAliyunNatGatewayRead(d *schema.ResourceData, meta interface{}) erro func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) + conn := client.vpcconn natGateway, err := client.DescribeNatGateway(d.Id()) if err != nil { @@ -150,7 +151,7 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er d.Partial(true) attributeUpdate := false - args := &ModifyNatGatewayAttributeArgs{ + args := &ecs.ModifyNatGatewayAttributeArgs{ RegionId: natGateway.RegionId, NatGatewayId: natGateway.NatGatewayId, } @@ -183,28 +184,28 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er } if attributeUpdate { - if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil { + if err := conn.ModifyNatGatewayAttribute(args); err != nil { return err } } if d.HasChange("spec") { d.SetPartial("spec") - var spec NatGatewaySpec + var spec ecs.NatGatewaySpec if v, ok := d.GetOk("spec"); ok { - spec = NatGatewaySpec(v.(string)) + spec = ecs.NatGatewaySpec(v.(string)) } else { // set default to small spec - spec = NatGatewaySmallSpec + spec = ecs.NatGatewaySmallSpec } - args := &ModifyNatGatewaySpecArgs{ + args := &ecs.ModifyNatGatewaySpecArgs{ RegionId: natGateway.RegionId, NatGatewayId: natGateway.NatGatewayId, Spec: spec, } - err := ModifyNatGatewaySpec(client.vpcconn, args) + err := conn.ModifyNatGatewaySpec(args) if err != nil { return fmt.Errorf("%#v %#v", err, *args) } @@ -218,10 +219,11 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) + conn := client.vpcconn return resource.Retry(5*time.Minute, func() *resource.RetryError { - packages, err := DescribeBandwidthPackages(client.vpcconn, &DescribeBandwidthPackagesArgs{ + packages, err := conn.DescribeBandwidthPackages(&ecs.DescribeBandwidthPackagesArgs{ RegionId: getRegion(d, meta), NatGatewayId: d.Id(), }) @@ -232,7 +234,7 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er retry := false for _, pack := range packages { - err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{ + err = conn.DeleteBandwidthPackage(&ecs.DeleteBandwidthPackageArgs{ RegionId: getRegion(d, meta), BandwidthPackageId: pack.BandwidthPackageId, }) @@ -251,12 +253,12 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted.")) } - args := &DeleteNatGatewayArgs{ + args := &ecs.DeleteNatGatewayArgs{ RegionId: client.Region, NatGatewayId: d.Id(), } - err = DeleteNatGateway(client.vpcconn, args) + err = conn.DeleteNatGateway(args) if err != nil { er, _ := err.(*common.Error) if er.ErrorResponse.Code == DependencyViolationBandwidthPackages { @@ -264,11 +266,11 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er } } - describeArgs := &DescribeNatGatewaysArgs{ + describeArgs := &ecs.DescribeNatGatewaysArgs{ RegionId: client.Region, NatGatewayId: d.Id(), } - gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs) + gw, _, gwErr := conn.DescribeNatGateways(describeArgs) if gwErr != nil { log.Printf("[ERROR] Describe NatGateways failed.") diff --git a/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go index ad8fba166..a928c5dc1 100644 --- a/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go +++ b/builtin/providers/alicloud/resource_alicloud_nat_gateway_test.go @@ -3,13 +3,14 @@ package alicloud import ( "fmt" "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "testing" ) func TestAccAlicloudNatGateway_basic(t *testing.T) { - var nat NatGatewaySetType + var nat ecs.NatGatewaySetType testCheck := func(*terraform.State) error { if nat.BusinessStatus != "Normal" { @@ -55,7 +56,7 @@ func TestAccAlicloudNatGateway_basic(t *testing.T) { } func TestAccAlicloudNatGateway_spec(t *testing.T) { - var nat NatGatewaySetType + var nat ecs.NatGatewaySetType resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -95,7 +96,7 @@ func TestAccAlicloudNatGateway_spec(t *testing.T) { } -func testAccCheckNatGatewayExists(n string, nat *NatGatewaySetType) resource.TestCheckFunc { +func testAccCheckNatGatewayExists(n string, nat *ecs.NatGatewaySetType) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -151,6 +152,10 @@ func testAccCheckNatGatewayDestroy(s *terraform.State) error { } const testAccNatGatewayConfig = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "172.16.0.0/12" @@ -159,7 +164,7 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_nat_gateway" "foo" { @@ -169,11 +174,11 @@ resource "alicloud_nat_gateway" "foo" { bandwidth_packages = [{ ip_count = 1 bandwidth = 5 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }, { ip_count = 2 bandwidth = 10 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }] depends_on = [ "alicloud_vswitch.foo"] @@ -181,6 +186,10 @@ resource "alicloud_nat_gateway" "foo" { ` const testAccNatGatewayConfigSpec = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "172.16.0.0/12" @@ -189,7 +198,7 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_nat_gateway" "foo" { @@ -199,11 +208,11 @@ resource "alicloud_nat_gateway" "foo" { bandwidth_packages = [{ ip_count = 1 bandwidth = 5 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }, { ip_count = 2 bandwidth = 10 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }] depends_on = [ "alicloud_vswitch.foo"] @@ -211,6 +220,10 @@ resource "alicloud_nat_gateway" "foo" { ` const testAccNatGatewayConfigSpecUpgrade = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "172.16.0.0/12" @@ -219,7 +232,7 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_nat_gateway" "foo" { @@ -229,11 +242,11 @@ resource "alicloud_nat_gateway" "foo" { bandwidth_packages = [{ ip_count = 1 bandwidth = 5 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }, { ip_count = 2 bandwidth = 10 - zone = "cn-beijing-b" + zone = "${data.alicloud_zones.default.zones.0.id}" }] depends_on = [ "alicloud_vswitch.foo"] diff --git a/builtin/providers/alicloud/resource_alicloud_security_group.go b/builtin/providers/alicloud/resource_alicloud_security_group.go index f21ae4b27..5f85bfd29 100644 --- a/builtin/providers/alicloud/resource_alicloud_security_group.go +++ b/builtin/providers/alicloud/resource_alicloud_security_group.go @@ -6,7 +6,6 @@ import ( "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" "time" ) @@ -145,6 +144,7 @@ func resourceAliyunSecurityGroupDelete(d *schema.ResourceData, meta interface{}) return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted.")) }) + } func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) { diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go index 4627d8e2b..c43db23a8 100644 --- a/builtin/providers/alicloud/resource_alicloud_security_group_rule.go +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule.go @@ -34,6 +34,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + Computed: true, ValidateFunc: validateSecurityRuleNicType, }, @@ -67,7 +68,6 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: "0.0.0.0/0", }, "source_security_group_id": &schema.Schema{ @@ -86,15 +86,17 @@ func resourceAliyunSecurityGroupRule() *schema.Resource { } func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AliyunClient).ecsconn + client := meta.(*AliyunClient) + conn := client.ecsconn - ruleType := d.Get("type").(string) + direction := d.Get("type").(string) sgId := d.Get("security_group_id").(string) ptl := d.Get("ip_protocol").(string) port := d.Get("port_range").(string) + nicType := d.Get("nic_type").(string) var autherr error - switch GroupRuleDirection(ruleType) { + switch GroupRuleDirection(direction) { case GroupRuleIngress: args, err := buildAliyunSecurityIngressArgs(d, meta) if err != nil { @@ -114,10 +116,11 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac if autherr != nil { return fmt.Errorf( "Error authorizing security group rule type %s: %s", - ruleType, autherr) + direction, autherr) } - d.SetId(sgId + ":" + ruleType + ":" + ptl + ":" + port) + d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType) + return resourceAliyunSecurityGroupRuleRead(d, meta) } @@ -125,10 +128,11 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ client := meta.(*AliyunClient) parts := strings.Split(d.Id(), ":") sgId := parts[0] - types := parts[1] + direction := parts[1] ip_protocol := parts[2] port_range := parts[3] - rule, err := client.DescribeSecurityGroupRule(sgId, types, ip_protocol, port_range) + nic_type := parts[4] + rule, err := client.DescribeSecurityGroupRule(sgId, direction, nic_type, ip_protocol, port_range) if err != nil { if notFoundError(err) { @@ -137,7 +141,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ } return fmt.Errorf("Error SecurityGroup rule: %#v", err) } - log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, types, ip_protocol, port_range, rule) + log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, direction, ip_protocol, port_range, rule) d.Set("type", rule.Direction) d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol))) d.Set("nic_type", rule.NicType) @@ -146,7 +150,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ d.Set("priority", rule.Priority) d.Set("security_group_id", sgId) //support source and desc by type - if GroupRuleDirection(types) == GroupRuleIngress { + if GroupRuleDirection(direction) == GroupRuleIngress { d.Set("cidr_ip", rule.SourceCidrIp) d.Set("source_security_group_id", rule.SourceGroupId) d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount) @@ -161,17 +165,41 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{ func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*AliyunClient) - args, err := buildAliyunSecurityIngressArgs(d, meta) + ruleType := d.Get("type").(string) + + if GroupRuleDirection(ruleType) == GroupRuleIngress { + args, err := buildAliyunSecurityIngressArgs(d, meta) + if err != nil { + return err + } + revokeArgs := &ecs.RevokeSecurityGroupArgs{ + AuthorizeSecurityGroupArgs: *args, + } + return client.RevokeSecurityGroup(revokeArgs) + } + + args, err := buildAliyunSecurityEgressArgs(d, meta) if err != nil { return err } - revokeArgs := &ecs.RevokeSecurityGroupArgs{ - AuthorizeSecurityGroupArgs: *args, + revokeArgs := &ecs.RevokeSecurityGroupEgressArgs{ + AuthorizeSecurityGroupEgressArgs: *args, } - return client.RevokeSecurityGroup(revokeArgs) + return client.RevokeSecurityGroupEgress(revokeArgs) + } +func checkCidrAndSourceGroupId(cidrIp, sourceGroupId string) error { + if cidrIp == "" && sourceGroupId == "" { + return fmt.Errorf("Either cidr_ip or source_security_group_id is required.") + } + + if cidrIp != "" && sourceGroupId != "" { + return fmt.Errorf("You should set only one value of cidr_ip or source_security_group_id.") + } + return nil +} func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) { conn := meta.(*AliyunClient).ecsconn @@ -199,12 +227,17 @@ func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (* args.NicType = ecs.NicType(v) } - if v := d.Get("cidr_ip").(string); v != "" { - args.SourceCidrIp = v + cidrIp := d.Get("cidr_ip").(string) + sourceGroupId := d.Get("source_security_group_id").(string) + if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil { + return nil, err + } + if cidrIp != "" { + args.SourceCidrIp = cidrIp } - if v := d.Get("source_security_group_id").(string); v != "" { - args.SourceGroupId = v + if sourceGroupId != "" { + args.SourceGroupId = sourceGroupId } if v := d.Get("source_group_owner_account").(string); v != "" { @@ -255,12 +288,17 @@ func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*e args.NicType = ecs.NicType(v) } - if v := d.Get("cidr_ip").(string); v != "" { - args.DestCidrIp = v + cidrIp := d.Get("cidr_ip").(string) + sourceGroupId := d.Get("source_security_group_id").(string) + if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil { + return nil, err + } + if cidrIp != "" { + args.DestCidrIp = cidrIp } - if v := d.Get("source_security_group_id").(string); v != "" { - args.DestGroupId = v + if sourceGroupId != "" { + args.DestGroupId = sourceGroupId } if v := d.Get("source_group_owner_account").(string); v != "" { diff --git a/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go index 7eb267fcb..0792966f2 100644 --- a/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go +++ b/builtin/providers/alicloud/resource_alicloud_security_group_rule_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "log" + "regexp" "strings" "testing" ) @@ -81,6 +82,39 @@ func TestAccAlicloudSecurityGroupRule_Egress(t *testing.T) { } +func TestAccAlicloudSecurityGroupRule_EgressDefaultNicType(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.egress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleEgress_emptyNicType, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.egress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "port_range", + "80/80"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "nic_type", + "internet"), + ), + }, + }, + }) + +} + func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) { var pt ecs.PermissionType @@ -114,6 +148,80 @@ func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) { } +func TestAccAlicloudSecurityGroupRule_MissParameterSourceCidrIp(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.egress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRule_missingSourceCidrIp, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.egress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "port_range", + "80/80"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "nic_type", + "internet"), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.egress", + "ip_protocol", + "udp"), + ), + }, + }, + }) + +} + +func TestAccAlicloudSecurityGroupRule_SourceSecurityGroup(t *testing.T) { + var pt ecs.PermissionType + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + + // module name + IDRefreshName: "alicloud_security_group_rule.ingress", + Providers: testAccProviders, + CheckDestroy: testAccCheckSecurityGroupRuleDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccSecurityGroupRuleSourceSecurityGroup, + Check: resource.ComposeTestCheckFunc( + testAccCheckSecurityGroupRuleExists( + "alicloud_security_group_rule.ingress", &pt), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "port_range", + "3306/3306"), + resource.TestMatchResourceAttr( + "alicloud_security_group_rule.ingress", + "source_security_group_id", + regexp.MustCompile("^sg-[a-zA-Z0-9_]+")), + resource.TestCheckResourceAttr( + "alicloud_security_group_rule.ingress", + "cidr_ip", + ""), + ), + }, + }, + }) + +} + func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -128,7 +236,8 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour client := testAccProvider.Meta().(*AliyunClient) log.Printf("[WARN]get sg rule %s", rs.Primary.ID) parts := strings.Split(rs.Primary.ID, ":") - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + // securityGroupId, direction, nicType, ipProtocol, portRange + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) if err != nil { return err @@ -152,7 +261,7 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error { } parts := strings.Split(rs.Primary.ID, ":") - rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3]) + rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3]) if rule != nil { return fmt.Errorf("Error SecurityGroup Rule still exist") @@ -210,6 +319,23 @@ resource "alicloud_security_group_rule" "egress" { ` +const testAccSecurityGroupRuleEgress_emptyNicType = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "egress" { + type = "egress" + ip_protocol = "udp" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "10.159.6.18/12" +} + +` + const testAccSecurityGroupRuleVpcIngress = ` resource "alicloud_security_group" "foo" { vpc_id = "${alicloud_vpc.vpc.id}" @@ -231,6 +357,22 @@ resource "alicloud_security_group_rule" "ingress" { cidr_ip = "10.159.6.18/12" } +` +const testAccSecurityGroupRule_missingSourceCidrIp = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group_rule" "egress" { + security_group_id = "${alicloud_security_group.foo.id}" + type = "egress" + cidr_ip= "0.0.0.0/0" + policy = "accept" + ip_protocol= "udp" + port_range= "80/80" + priority= 1 +} + ` const testAccSecurityGroupRuleMultiIngress = ` @@ -260,4 +402,27 @@ resource "alicloud_security_group_rule" "ingress2" { cidr_ip = "127.0.1.18/16" } +` + +const testAccSecurityGroupRuleSourceSecurityGroup = ` +resource "alicloud_security_group" "foo" { + name = "sg_foo" +} + +resource "alicloud_security_group" "bar" { + name = "sg_bar" +} + +resource "alicloud_security_group_rule" "ingress" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "intranet" + policy = "accept" + port_range = "3306/3306" + priority = 50 + security_group_id = "${alicloud_security_group.bar.id}" + source_security_group_id = "${alicloud_security_group.foo.id}" +} + + ` diff --git a/builtin/providers/alicloud/resource_alicloud_slb.go b/builtin/providers/alicloud/resource_alicloud_slb.go index 8bc787b3d..f3d2af9d3 100644 --- a/builtin/providers/alicloud/resource_alicloud_slb.go +++ b/builtin/providers/alicloud/resource_alicloud_slb.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "errors" "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/slb" "github.com/hashicorp/terraform/helper/hashcode" @@ -83,40 +84,124 @@ func resourceAliyunSlb() *schema.Resource { ValidateFunc: validateSlbListenerBandwidth, Required: true, }, - //http "scheduler": &schema.Schema{ Type: schema.TypeString, ValidateFunc: validateSlbListenerScheduler, Optional: true, - Default: "wrr", + Default: slb.WRRScheduler, }, - + //http & https "sticky_session": &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateSlbListenerStickySession, - Optional: true, + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{ + string(slb.OnFlag), + string(slb.OffFlag)}), + Optional: true, + Default: slb.OffFlag, }, + //http & https "sticky_session_type": &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateSlbListenerStickySessionType, + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{ + string(slb.InsertStickySessionType), + string(slb.ServerStickySessionType)}), + Optional: true, + }, + //http & https + "cookie_timeout": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerCookieTimeout, Optional: true, }, + //http & https "cookie": &schema.Schema{ Type: schema.TypeString, ValidateFunc: validateSlbListenerCookie, Optional: true, }, - "PersistenceTimeout": &schema.Schema{ + //tcp & udp + "persistence_timeout": &schema.Schema{ Type: schema.TypeInt, ValidateFunc: validateSlbListenerPersistenceTimeout, Optional: true, Default: 0, }, + //http & https + "health_check": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{ + string(slb.OnFlag), + string(slb.OffFlag)}), + Optional: true, + Default: slb.OffFlag, + }, + //tcp + "health_check_type": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedStringValue([]string{ + string(slb.TCPHealthCheckType), + string(slb.HTTPHealthCheckType)}), + Optional: true, + Default: slb.TCPHealthCheckType, + }, + //http & https & tcp + "health_check_domain": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerHealthCheckDomain, + Optional: true, + }, + //http & https & tcp + "health_check_uri": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateSlbListenerHealthCheckUri, + Optional: true, + }, + "health_check_connect_port": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateSlbListenerHealthCheckConnectPort, + Optional: true, + }, + "healthy_threshold": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateIntegerInRange(1, 10), + Optional: true, + }, + "unhealthy_threshold": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateIntegerInRange(1, 10), + Optional: true, + }, + + "health_check_timeout": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateIntegerInRange(1, 50), + Optional: true, + }, + "health_check_interval": &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validateIntegerInRange(1, 5), + Optional: true, + }, + //http & https & tcp + "health_check_http_code": &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateAllowedSplitStringValue([]string{ + string(slb.HTTP_2XX), + string(slb.HTTP_3XX), + string(slb.HTTP_4XX), + string(slb.HTTP_5XX)}, ","), + Optional: true, + }, //https "ssl_certificate_id": &schema.Schema{ Type: schema.TypeString, Optional: true, }, + //https + //"ca_certificate_id": &schema.Schema{ + // Type: schema.TypeString, + // Optional: true, + //}, }, }, Set: resourceAliyunSlbListenerHash, @@ -349,44 +434,53 @@ func resourceAliyunSlbListenerHash(v interface{}) int { } func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error { + + errTypeJudge := func(err error) error { + if err != nil { + if listenerType, ok := err.(*ListenerErr); ok { + if listenerType.ErrType == HealthCheckErrType { + return fmt.Errorf("When the HealthCheck is %s, then related HealthCheck parameter "+ + "must have.", slb.OnFlag) + } else if listenerType.ErrType == StickySessionErrType { + return fmt.Errorf("When the StickySession is %s, then StickySessionType parameter "+ + "must have.", slb.OnFlag) + } else if listenerType.ErrType == CookieTimeOutErrType { + return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+ + "then CookieTimeout parameter must have.", slb.OnFlag, slb.InsertStickySessionType) + } else if listenerType.ErrType == CookieErrType { + return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+ + "then Cookie parameter must have.", slb.OnFlag, slb.ServerStickySessionType) + } + return fmt.Errorf("slb listener check errtype not found.") + } + } + return nil + } + if listener.Protocol == strings.ToLower("tcp") { - args := &slb.CreateLoadBalancerTCPListenerArgs{ - LoadBalancerId: loadBalancerId, - ListenerPort: listener.LoadBalancerPort, - BackendServerPort: listener.InstancePort, - Bandwidth: listener.Bandwidth, - } - if err := conn.CreateLoadBalancerTCPListener(args); err != nil { + + args := getTcpListenerArgs(loadBalancerId, listener) + + if err := conn.CreateLoadBalancerTCPListener(&args); err != nil { return err } - } - - if listener.Protocol == strings.ToLower("http") { - args := &slb.CreateLoadBalancerHTTPListenerArgs{ - LoadBalancerId: loadBalancerId, - ListenerPort: listener.LoadBalancerPort, - BackendServerPort: listener.InstancePort, - Bandwidth: listener.Bandwidth, - StickySession: slb.OffFlag, - HealthCheck: slb.OffFlag, + } else if listener.Protocol == strings.ToLower("http") { + args, argsErr := getHttpListenerArgs(loadBalancerId, listener) + if paramErr := errTypeJudge(argsErr); paramErr != nil { + return paramErr } - if err := conn.CreateLoadBalancerHTTPListener(args); err != nil { + if err := conn.CreateLoadBalancerHTTPListener(&args); err != nil { return err } - } + } else if listener.Protocol == strings.ToLower("https") { + listenerType, err := getHttpListenerType(loadBalancerId, listener) + if paramErr := errTypeJudge(err); paramErr != nil { + return paramErr + } - if listener.Protocol == strings.ToLower("https") { args := &slb.CreateLoadBalancerHTTPSListenerArgs{ - - HTTPListenerType: slb.HTTPListenerType{ - LoadBalancerId: loadBalancerId, - ListenerPort: listener.LoadBalancerPort, - BackendServerPort: listener.InstancePort, - Bandwidth: listener.Bandwidth, - StickySession: slb.OffFlag, - HealthCheck: slb.OffFlag, - }, + HTTPListenerType: listenerType, } if listener.SSLCertificateId == "" { return fmt.Errorf("Server Certificated Id cann't be null") @@ -397,17 +491,10 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil { return err } - } + } else if listener.Protocol == strings.ToLower("udp") { + args := getUdpListenerArgs(loadBalancerId, listener) - if listener.Protocol == strings.ToLower("udp") { - args := &slb.CreateLoadBalancerUDPListenerArgs{ - LoadBalancerId: loadBalancerId, - ListenerPort: listener.LoadBalancerPort, - BackendServerPort: listener.InstancePort, - Bandwidth: listener.Bandwidth, - } - - if err := conn.CreateLoadBalancerUDPListener(args); err != nil { + if err := conn.CreateLoadBalancerUDPListener(&args); err != nil { return err } } @@ -418,3 +505,102 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) return nil } + +func getTcpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerTCPListenerArgs { + args := slb.CreateLoadBalancerTCPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + Scheduler: listener.Scheduler, + PersistenceTimeout: listener.PersistenceTimeout, + HealthCheckType: listener.HealthCheckType, + HealthCheckDomain: listener.HealthCheckDomain, + HealthCheckURI: listener.HealthCheckURI, + HealthCheckConnectPort: listener.HealthCheckConnectPort, + HealthyThreshold: listener.HealthyThreshold, + UnhealthyThreshold: listener.UnhealthyThreshold, + HealthCheckConnectTimeout: listener.HealthCheckTimeout, + HealthCheckInterval: listener.HealthCheckInterval, + HealthCheckHttpCode: listener.HealthCheckHttpCode, + } + return args +} + +func getUdpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerUDPListenerArgs { + args := slb.CreateLoadBalancerUDPListenerArgs{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + PersistenceTimeout: listener.PersistenceTimeout, + HealthCheckConnectTimeout: listener.HealthCheckTimeout, + HealthCheckInterval: listener.HealthCheckInterval, + } + return args +} + +func getHttpListenerType(loadBalancerId string, listener *Listener) (listenType slb.HTTPListenerType, err error) { + + if listener.HealthCheck == slb.OnFlag { + if listener.HealthCheckURI == "" || listener.HealthCheckDomain == "" || listener.HealthCheckConnectPort == 0 || + listener.HealthyThreshold == 0 || listener.UnhealthyThreshold == 0 || listener.HealthCheckTimeout == 0 || + listener.HealthCheckHttpCode == "" || listener.HealthCheckInterval == 0 { + + errMsg := errors.New("err: HealthCheck empty.") + return listenType, &ListenerErr{HealthCheckErrType, errMsg} + } + } + + if listener.StickySession == slb.OnFlag { + if listener.StickySessionType == "" { + errMsg := errors.New("err: stickySession empty.") + return listenType, &ListenerErr{StickySessionErrType, errMsg} + } + + if listener.StickySessionType == slb.InsertStickySessionType { + if listener.CookieTimeout == 0 { + errMsg := errors.New("err: cookieTimeout empty.") + return listenType, &ListenerErr{CookieTimeOutErrType, errMsg} + } + } else if listener.StickySessionType == slb.ServerStickySessionType { + if listener.Cookie == "" { + errMsg := errors.New("err: cookie empty.") + return listenType, &ListenerErr{CookieErrType, errMsg} + } + } + } + + httpListenertType := slb.HTTPListenerType{ + LoadBalancerId: loadBalancerId, + ListenerPort: listener.LoadBalancerPort, + BackendServerPort: listener.InstancePort, + Bandwidth: listener.Bandwidth, + Scheduler: listener.Scheduler, + HealthCheck: listener.HealthCheck, + StickySession: listener.StickySession, + StickySessionType: listener.StickySessionType, + CookieTimeout: listener.CookieTimeout, + Cookie: listener.Cookie, + HealthCheckDomain: listener.HealthCheckDomain, + HealthCheckURI: listener.HealthCheckURI, + HealthCheckConnectPort: listener.HealthCheckConnectPort, + HealthyThreshold: listener.HealthyThreshold, + UnhealthyThreshold: listener.UnhealthyThreshold, + HealthCheckTimeout: listener.HealthCheckTimeout, + HealthCheckInterval: listener.HealthCheckInterval, + HealthCheckHttpCode: listener.HealthCheckHttpCode, + } + + return httpListenertType, err +} + +func getHttpListenerArgs(loadBalancerId string, listener *Listener) (listenType slb.CreateLoadBalancerHTTPListenerArgs, err error) { + httpListenerType, err := getHttpListenerType(loadBalancerId, listener) + if err != nil { + return listenType, err + } + + httpArgs := slb.CreateLoadBalancerHTTPListenerArgs(httpListenerType) + return httpArgs, err +} diff --git a/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go index 90a70ead8..5caa4a710 100644 --- a/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go +++ b/builtin/providers/alicloud/resource_alicloud_slb_attachment_test.go @@ -79,9 +79,30 @@ resource "alicloud_security_group" "foo" { description = "foo" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.foo.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-b" image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" # series II diff --git a/builtin/providers/alicloud/resource_alicloud_slb_test.go b/builtin/providers/alicloud/resource_alicloud_slb_test.go index 3e68a4c14..42308f187 100644 --- a/builtin/providers/alicloud/resource_alicloud_slb_test.go +++ b/builtin/providers/alicloud/resource_alicloud_slb_test.go @@ -85,7 +85,7 @@ func TestAccAlicloudSlb_listener(t *testing.T) { testListener := func() resource.TestCheckFunc { return func(*terraform.State) error { listenerPorts := slb.ListenerPorts.ListenerPort[0] - if listenerPorts != 161 { + if listenerPorts != 2001 { return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts) } @@ -260,21 +260,49 @@ resource "alicloud_slb" "listener" { "lb_port" = "21" "lb_protocol" = "tcp" "bandwidth" = 1 + "persistence_timeout" = 500 + "health_check_type" = "http" },{ "instance_port" = "8000" "lb_port" = "80" "lb_protocol" = "http" + "sticky_session" = "on" + "sticky_session_type" = "insert" + "cookie_timeout" = 800 "bandwidth" = 1 },{ - "instance_port" = "1611" - "lb_port" = "161" + "instance_port" = "8001" + "lb_port" = "81" + "lb_protocol" = "http" + "sticky_session" = "on" + "sticky_session_type" = "server" + "cookie" = "testslblistenercookie" + "cookie_timeout" = 1800 + "health_check" = "on" + "health_check_domain" = "$_ip" + "health_check_uri" = "/console" + "health_check_connect_port" = 20 + "healthy_threshold" = 8 + "unhealthy_threshold" = 8 + "health_check_timeout" = 8 + "health_check_interval" = 4 + "health_check_http_code" = "http_2xx" + "bandwidth" = 1 + },{ + "instance_port" = "2001" + "lb_port" = "2001" "lb_protocol" = "udp" "bandwidth" = 1 + "persistence_timeout" = 700 }] } ` const testAccSlb4Vpc = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "172.16.0.0/12" @@ -283,7 +311,7 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_slb" "vpc" { diff --git a/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go index cbdb59bef..8726de64c 100644 --- a/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go +++ b/builtin/providers/alicloud/resource_alicloud_vroute_entry_test.go @@ -124,6 +124,10 @@ func testAccCheckRouteEntryDestroy(s *terraform.State) error { } const testAccRouteEntryConfig = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "10.1.0.0/21" @@ -132,7 +136,7 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "10.1.1.0/24" - availability_zone = "cn-beijing-c" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } resource "alicloud_route_entry" "foo" { @@ -162,7 +166,6 @@ resource "alicloud_security_group_rule" "ingress" { resource "alicloud_instance" "foo" { # cn-beijing - availability_zone = "cn-beijing-c" security_groups = ["${alicloud_security_group.tf_test_foo.id}"] vswitch_id = "${alicloud_vswitch.foo.id}" diff --git a/builtin/providers/alicloud/resource_alicloud_vswitch_test.go b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go index bcd70a2cc..1a1a75bd6 100644 --- a/builtin/providers/alicloud/resource_alicloud_vswitch_test.go +++ b/builtin/providers/alicloud/resource_alicloud_vswitch_test.go @@ -92,6 +92,10 @@ func testAccCheckVswitchDestroy(s *terraform.State) error { } const testAccVswitchConfig = ` +data "alicloud_zones" "default" { + "available_resource_creation"= "VSwitch" +} + resource "alicloud_vpc" "foo" { name = "tf_test_foo" cidr_block = "172.16.0.0/12" @@ -100,6 +104,6 @@ resource "alicloud_vpc" "foo" { resource "alicloud_vswitch" "foo" { vpc_id = "${alicloud_vpc.foo.id}" cidr_block = "172.16.0.0/21" - availability_zone = "cn-beijing-b" + availability_zone = "${data.alicloud_zones.default.zones.0.id}" } ` diff --git a/builtin/providers/alicloud/service_alicloud_ecs.go b/builtin/providers/alicloud/service_alicloud_ecs.go index 2c892ce24..4ff0e5f04 100644 --- a/builtin/providers/alicloud/service_alicloud_ecs.go +++ b/builtin/providers/alicloud/service_alicloud_ecs.go @@ -84,6 +84,24 @@ func (client *AliyunClient) DescribeZone(zoneID string) (*ecs.ZoneType, error) { return zone, nil } +// return multiIZ list of current region +func (client *AliyunClient) DescribeMultiIZByRegion() (izs []string, err error) { + resp, err := client.rdsconn.DescribeRegions() + if err != nil { + return nil, fmt.Errorf("error to list regions not found") + } + regions := resp.Regions.RDSRegion + + zoneIds := []string{} + for _, r := range regions { + if r.RegionId == string(client.Region) && strings.Contains(r.ZoneId, MULTI_IZ_SYMBOL) { + zoneIds = append(zoneIds, r.ZoneId) + } + } + + return zoneIds, nil +} + func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) { idsStr, jerr := json.Marshal(ids) if jerr != nil { @@ -119,6 +137,23 @@ func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.Instanc return &instances[0], nil } +func (client *AliyunClient) QueryInstanceSystemDisk(id string) (disk *ecs.DiskItemType, err error) { + args := ecs.DescribeDisksArgs{ + RegionId: client.Region, + InstanceId: string(id), + DiskType: ecs.DiskTypeAllSystem, + } + disks, _, err := client.ecsconn.DescribeDisks(&args) + if err != nil { + return nil, err + } + if len(disks) == 0 { + return nil, common.GetClientErrorFromString(SystemDiskNotFound) + } + + return &disks[0], nil +} + // ResourceAvailable check resource available for zone func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error { available := false @@ -186,15 +221,26 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr return client.ecsconn.DescribeSecurityGroupAttribute(args) } -func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip_protocol, port_range string) (*ecs.PermissionType, error) { +func (client *AliyunClient) DescribeSecurityByAttr(securityGroupId, direction, nicType string) (*ecs.DescribeSecurityGroupAttributeResponse, error) { - sg, err := client.DescribeSecurity(securityGroupId) + args := &ecs.DescribeSecurityGroupAttributeArgs{ + RegionId: client.Region, + SecurityGroupId: securityGroupId, + Direction: direction, + NicType: ecs.NicType(nicType), + } + + return client.ecsconn.DescribeSecurityGroupAttribute(args) +} + +func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, direction, nicType, ipProtocol, portRange string) (*ecs.PermissionType, error) { + sg, err := client.DescribeSecurityByAttr(securityGroupId, direction, nicType) if err != nil { return nil, err } for _, p := range sg.Permissions.Permission { - if strings.ToLower(string(p.IpProtocol)) == ip_protocol && p.PortRange == port_range { + if strings.ToLower(string(p.IpProtocol)) == ipProtocol && p.PortRange == portRange { return &p, nil } } @@ -203,6 +249,11 @@ func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip } func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error { - //todo: handle the specal err + //when the rule is not exist, api will return success(200) return client.ecsconn.RevokeSecurityGroup(args) } + +func (client *AliyunClient) RevokeSecurityGroupEgress(args *ecs.RevokeSecurityGroupEgressArgs) error { + //when the rule is not exist, api will return success(200) + return client.ecsconn.RevokeSecurityGroupEgress(args) +} diff --git a/builtin/providers/alicloud/service_alicloud_rds.go b/builtin/providers/alicloud/service_alicloud_rds.go new file mode 100644 index 000000000..903374fe6 --- /dev/null +++ b/builtin/providers/alicloud/service_alicloud_rds.go @@ -0,0 +1,278 @@ +package alicloud + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/rds" + "strings" +) + +// when getInstance is empty, then throw InstanceNotfound error +func (client *AliyunClient) DescribeDBInstanceById(id string) (instance *rds.DBInstanceAttribute, err error) { + arrtArgs := rds.DescribeDBInstancesArgs{ + DBInstanceId: id, + } + resp, err := client.rdsconn.DescribeDBInstanceAttribute(&arrtArgs) + if err != nil { + return nil, err + } + + attr := resp.Items.DBInstanceAttribute + + if len(attr) <= 0 { + return nil, common.GetClientErrorFromString(InstanceNotfound) + } + + return &attr[0], nil +} + +func (client *AliyunClient) CreateAccountByInfo(instanceId, username, pwd string) error { + conn := client.rdsconn + args := rds.CreateAccountArgs{ + DBInstanceId: instanceId, + AccountName: username, + AccountPassword: pwd, + } + + if _, err := conn.CreateAccount(&args); err != nil { + return err + } + + if err := conn.WaitForAccount(instanceId, username, rds.Available, 200); err != nil { + return err + } + return nil +} + +func (client *AliyunClient) CreateDatabaseByInfo(instanceId, dbName, charset, desp string) error { + conn := client.rdsconn + args := rds.CreateDatabaseArgs{ + DBInstanceId: instanceId, + DBName: dbName, + CharacterSetName: charset, + DBDescription: desp, + } + _, err := conn.CreateDatabase(&args) + return err +} + +func (client *AliyunClient) DescribeDatabaseByName(instanceId, dbName string) (ds []rds.Database, err error) { + conn := client.rdsconn + args := rds.DescribeDatabasesArgs{ + DBInstanceId: instanceId, + DBName: dbName, + } + + resp, err := conn.DescribeDatabases(&args) + if err != nil { + return nil, err + } + + return resp.Databases.Database, nil +} + +func (client *AliyunClient) GrantDBPrivilege2Account(instanceId, username, dbName string) error { + conn := client.rdsconn + pargs := rds.GrantAccountPrivilegeArgs{ + DBInstanceId: instanceId, + AccountName: username, + DBName: dbName, + AccountPrivilege: rds.ReadWrite, + } + if _, err := conn.GrantAccountPrivilege(&pargs); err != nil { + return err + } + + if err := conn.WaitForAccountPrivilege(instanceId, username, dbName, rds.ReadWrite, 200); err != nil { + return err + } + return nil +} + +func (client *AliyunClient) AllocateDBPublicConnection(instanceId, port string) error { + conn := client.rdsconn + args := rds.AllocateInstancePublicConnectionArgs{ + DBInstanceId: instanceId, + ConnectionStringPrefix: instanceId + "o", + Port: port, + } + + if _, err := conn.AllocateInstancePublicConnection(&args); err != nil { + return err + } + + if err := conn.WaitForPublicConnection(instanceId, 600); err != nil { + return err + } + return nil +} + +func (client *AliyunClient) ConfigDBBackup(instanceId, backupTime, backupPeriod string, retentionPeriod int) error { + bargs := rds.BackupPolicy{ + PreferredBackupTime: backupTime, + PreferredBackupPeriod: backupPeriod, + BackupRetentionPeriod: retentionPeriod, + } + args := rds.ModifyBackupPolicyArgs{ + DBInstanceId: instanceId, + BackupPolicy: bargs, + } + + if _, err := client.rdsconn.ModifyBackupPolicy(&args); err != nil { + return err + } + + if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil { + return err + } + return nil +} + +func (client *AliyunClient) ModifyDBSecurityIps(instanceId, ips string) error { + sargs := rds.DBInstanceIPArray{ + SecurityIps: ips, + } + + args := rds.ModifySecurityIpsArgs{ + DBInstanceId: instanceId, + DBInstanceIPArray: sargs, + } + + if _, err := client.rdsconn.ModifySecurityIps(&args); err != nil { + return err + } + + if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil { + return err + } + return nil +} + +func (client *AliyunClient) DescribeDBSecurityIps(instanceId string) (ips []rds.DBInstanceIPList, err error) { + args := rds.DescribeDBInstanceIPsArgs{ + DBInstanceId: instanceId, + } + + resp, err := client.rdsconn.DescribeDBInstanceIPs(&args) + if err != nil { + return nil, err + } + return resp.Items.DBInstanceIPArray, nil +} + +func (client *AliyunClient) GetSecurityIps(instanceId string) ([]string, error) { + arr, err := client.DescribeDBSecurityIps(instanceId) + if err != nil { + return nil, err + } + ips := "" + for i, ip := range arr { + if i == 0 { + ips += ip.SecurityIPList + } else { + ips += COMMA_SEPARATED + ip.SecurityIPList + } + } + return strings.Split(ips, COMMA_SEPARATED), nil +} + +func (client *AliyunClient) ModifyDBClassStorage(instanceId, class, storage string) error { + conn := client.rdsconn + args := rds.ModifyDBInstanceSpecArgs{ + DBInstanceId: instanceId, + PayType: rds.Postpaid, + DBInstanceClass: class, + DBInstanceStorage: storage, + } + + if _, err := conn.ModifyDBInstanceSpec(&args); err != nil { + return err + } + + if err := conn.WaitForInstance(instanceId, rds.Running, 600); err != nil { + return err + } + return nil +} + +// turn period to TimeType +func TransformPeriod2Time(period int, chargeType string) (ut int, tt common.TimeType) { + if chargeType == string(rds.Postpaid) { + return 1, common.Day + } + + if period >= 1 && period <= 9 { + return period, common.Month + } + + if period == 12 { + return 1, common.Year + } + + if period == 24 { + return 2, common.Year + } + return 0, common.Day + +} + +// turn TimeType to Period +func TransformTime2Period(ut int, tt common.TimeType) (period int) { + if tt == common.Year { + return 12 * ut + } + + return ut + +} + +// Flattens an array of databases into a []map[string]interface{} +func flattenDatabaseMappings(list []rds.Database) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(list)) + for _, i := range list { + l := map[string]interface{}{ + "db_name": i.DBName, + "character_set_name": i.CharacterSetName, + "db_description": i.DBDescription, + } + result = append(result, l) + } + return result +} + +func flattenDBBackup(list []rds.BackupPolicy) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(list)) + for _, i := range list { + l := map[string]interface{}{ + "preferred_backup_period": i.PreferredBackupPeriod, + "preferred_backup_time": i.PreferredBackupTime, + "backup_retention_period": i.LogBackupRetentionPeriod, + } + result = append(result, l) + } + return result +} + +func flattenDBSecurityIPs(list []rds.DBInstanceIPList) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(list)) + for _, i := range list { + l := map[string]interface{}{ + "security_ips": i.SecurityIPList, + } + result = append(result, l) + } + return result +} + +// Flattens an array of databases connection into a []map[string]interface{} +func flattenDBConnections(list []rds.DBInstanceNetInfo) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(list)) + for _, i := range list { + l := map[string]interface{}{ + "connection_string": i.ConnectionString, + "ip_type": i.IPType, + "ip_address": i.IPAddress, + } + result = append(result, l) + } + return result +} diff --git a/builtin/providers/alicloud/service_alicloud_vpc.go b/builtin/providers/alicloud/service_alicloud_vpc.go index 102c61165..775fe112c 100644 --- a/builtin/providers/alicloud/service_alicloud_vpc.go +++ b/builtin/providers/alicloud/service_alicloud_vpc.go @@ -24,14 +24,14 @@ func (client *AliyunClient) DescribeEipAddress(allocationId string) (*ecs.EipAdd return &eips[0], nil } -func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*NatGatewaySetType, error) { +func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*ecs.NatGatewaySetType, error) { - args := &DescribeNatGatewaysArgs{ + args := &ecs.DescribeNatGatewaysArgs{ RegionId: client.Region, NatGatewayId: natGatewayId, } - natGateways, _, err := DescribeNatGateways(client.ecsconn, args) + natGateways, _, err := client.vpcconn.DescribeNatGateways(args) if err != nil { return nil, err } @@ -132,3 +132,23 @@ func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType } return nil, nil } + +func (client *AliyunClient) GetVpcIdByVSwitchId(vswitchId string) (vpcId string, err error) { + + vs, _, err := client.ecsconn.DescribeVpcs(&ecs.DescribeVpcsArgs{ + RegionId: client.Region, + }) + if err != nil { + return "", err + } + + for _, v := range vs { + for _, sw := range v.VSwitchIds.VSwitchId { + if sw == vswitchId { + return v.VpcId, nil + } + } + } + + return "", &common.Error{ErrorResponse: common.ErrorResponse{Message: Notfound}} +} diff --git a/builtin/providers/alicloud/validators.go b/builtin/providers/alicloud/validators.go index 9c7fec01a..9687e68e8 100644 --- a/builtin/providers/alicloud/validators.go +++ b/builtin/providers/alicloud/validators.go @@ -2,17 +2,27 @@ package alicloud import ( "fmt" - "regexp" + "net" + "strconv" "strings" "github.com/denverdino/aliyungo/common" "github.com/denverdino/aliyungo/ecs" - "github.com/hashicorp/terraform/helper/validation" + "github.com/denverdino/aliyungo/slb" + "github.com/hashicorp/terraform/helper/schema" + "regexp" ) // common func validateInstancePort(v interface{}, k string) (ws []string, errors []error) { - return validation.IntBetween(1, 65535)(v, k) + value := v.(int) + if value < 1 || value > 65535 { + errors = append(errors, fmt.Errorf( + "%q must be a valid instance port between 1 and 65535", + k)) + return + } + return } func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []error) { @@ -28,11 +38,12 @@ func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []er // ecs func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(ecs.DiskCategoryCloud), - string(ecs.DiskCategoryCloudEfficiency), - string(ecs.DiskCategoryCloudSSD), - }, false)(v, k) + category := ecs.DiskCategory(v.(string)) + if category != ecs.DiskCategoryCloud && category != ecs.DiskCategoryCloudEfficiency && category != ecs.DiskCategoryCloudSSD { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s", k, ecs.DiskCategoryCloud, ecs.DiskCategoryCloudEfficiency, ecs.DiskCategoryCloudSSD)) + } + + return } func validateInstanceName(v interface{}, k string) (ws []string, errors []error) { @@ -49,7 +60,12 @@ func validateInstanceName(v interface{}, k string) (ws []string, errors []error) } func validateInstanceDescription(v interface{}, k string) (ws []string, errors []error) { - return validation.StringLenBetween(2, 256)(v, k) + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return } func validateDiskName(v interface{}, k string) (ws []string, errors []error) { @@ -71,7 +87,12 @@ func validateDiskName(v interface{}, k string) (ws []string, errors []error) { } func validateDiskDescription(v interface{}, k string) (ws []string, errors []error) { - return validation.StringLenBetween(2, 128)(v, k) + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return } //security group @@ -89,114 +110,225 @@ func validateSecurityGroupName(v interface{}, k string) (ws []string, errors []e } func validateSecurityGroupDescription(v interface{}, k string) (ws []string, errors []error) { - return validation.StringLenBetween(2, 256)(v, k) + value := v.(string) + if len(value) < 2 || len(value) > 256 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k)) + + } + return } func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(GroupRuleIngress), - string(GroupRuleEgress), - }, false)(v, k) + rt := GroupRuleDirection(v.(string)) + if rt != GroupRuleIngress && rt != GroupRuleEgress { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress)) + } + + return } func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(GroupRuleTcp), - string(GroupRuleUdp), - string(GroupRuleIcmp), - string(GroupRuleGre), - string(GroupRuleAll), - }, false)(v, k) + pt := GroupRuleIpProtocol(v.(string)) + if pt != GroupRuleTcp && pt != GroupRuleUdp && pt != GroupRuleIcmp && pt != GroupRuleGre && pt != GroupRuleAll { + errors = append(errors, fmt.Errorf("%s must be one of %s %s %s %s %s", k, + GroupRuleTcp, GroupRuleUdp, GroupRuleIcmp, GroupRuleGre, GroupRuleAll)) + } + + return } func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(GroupRuleInternet), - string(GroupRuleIntranet), - }, false)(v, k) + pt := GroupRuleNicType(v.(string)) + if pt != GroupRuleInternet && pt != GroupRuleIntranet { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleInternet, GroupRuleIntranet)) + } + + return } func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(GroupRulePolicyAccept), - string(GroupRulePolicyDrop), - }, false)(v, k) + pt := GroupRulePolicy(v.(string)) + if pt != GroupRulePolicyAccept && pt != GroupRulePolicyDrop { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRulePolicyAccept, GroupRulePolicyDrop)) + } + + return } func validateSecurityPriority(v interface{}, k string) (ws []string, errors []error) { - return validation.IntBetween(1, 100)(v, k) + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid authorization policy priority between 1 and 100", + k)) + return + } + return } // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that // represents a network address - it adds an error otherwise func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { - return validation.CIDRNetwork(0, 32)(v, k) + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + } + + return } func validateRouteEntryNextHopType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - string(ecs.NextHopIntance), - string(ecs.NextHopTunnel), - }, false)(v, k) + nht := ecs.NextHopType(v.(string)) + if nht != ecs.NextHopIntance && nht != ecs.NextHopTunnel { + errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, + ecs.NextHopIntance, ecs.NextHopTunnel)) + } + + return } func validateSwitchCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { - return validation.CIDRNetwork(16, 29)(v, k) + value := v.(string) + _, ipnet, err := net.ParseCIDR(value) + if err != nil { + errors = append(errors, fmt.Errorf( + "%q must contain a valid CIDR, got error parsing: %s", k, err)) + return + } + + if ipnet == nil || value != ipnet.String() { + errors = append(errors, fmt.Errorf( + "%q must contain a valid network CIDR, expected %q, got %q", + k, ipnet, value)) + return + } + + mark, _ := strconv.Atoi(strings.Split(ipnet.String(), "/")[1]) + if mark < 16 || mark > 29 { + errors = append(errors, fmt.Errorf( + "%q must contain a network CIDR which mark between 16 and 29", + k)) + } + + return } // validateIoOptimized ensures that the string value is a valid IoOptimized that // represents a IoOptimized - it adds an error otherwise func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "", - string(ecs.IoOptimizedNone), - string(ecs.IoOptimizedOptimized), - }, false)(v, k) + if value := v.(string); value != "" { + ioOptimized := ecs.IoOptimized(value) + if ioOptimized != ecs.IoOptimizedNone && + ioOptimized != ecs.IoOptimizedOptimized { + errors = append(errors, fmt.Errorf( + "%q must contain a valid IoOptimized, expected %s or %s, got %q", + k, ecs.IoOptimizedNone, ecs.IoOptimizedOptimized, ioOptimized)) + } + } + + return } // validateInstanceNetworkType ensures that the string value is a classic or vpc func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "", - string(ClassicNet), - string(VpcNet), - }, false)(v, k) + if value := v.(string); value != "" { + network := InstanceNetWork(value) + if network != ClassicNet && + network != VpcNet { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceNetworkType, expected %s or %s, go %q", + k, ClassicNet, VpcNet, network)) + } + } + return } func validateInstanceChargeType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "", - string(common.PrePaid), - string(common.PostPaid), - }, false)(v, k) + if value := v.(string); value != "" { + chargeType := common.InstanceChargeType(value) + if chargeType != common.PrePaid && + chargeType != common.PostPaid { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PrePaid, common.PostPaid, chargeType)) + } + } + + return } func validateInternetChargeType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "", - string(common.PayByBandwidth), - string(common.PayByTraffic), - }, false)(v, k) + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + if chargeType != common.PayByBandwidth && + chargeType != common.PayByTraffic { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, common.PayByBandwidth, common.PayByTraffic, chargeType)) + } + } + + return } func validateInternetMaxBandWidthOut(v interface{}, k string) (ws []string, errors []error) { - return validation.IntBetween(1, 100)(v, k) + value := v.(int) + if value < 1 || value > 100 { + errors = append(errors, fmt.Errorf( + "%q must be a valid internet bandwidth out between 1 and 1000", + k)) + return + } + return } // SLB func validateSlbName(v interface{}, k string) (ws []string, errors []error) { - return validation.StringLenBetween(0, 80)(v, k) + if value := v.(string); value != "" { + if len(value) < 1 || len(value) > 80 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer name characters between 1 and 80", + k)) + return + } + } + + return } func validateSlbInternetChargeType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "paybybandwidth", - "paybytraffic", - }, false)(v, k) + if value := v.(string); value != "" { + chargeType := common.InternetChargeType(value) + + if chargeType != "paybybandwidth" && + chargeType != "paybytraffic" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid InstanceChargeType, expected %s or %s, got %q", + k, "paybybandwidth", "paybytraffic", value)) + } + } + + return } func validateSlbBandwidth(v interface{}, k string) (ws []string, errors []error) { - return validation.IntBetween(1, 1000)(v, k) + value := v.(int) + if value < 1 || value > 1000 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer bandwidth between 1 and 1000", + k)) + return + } + return } func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors []error) { @@ -211,23 +343,180 @@ func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors } func validateSlbListenerScheduler(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{"wrr", "wlc"}, false)(v, k) -} + if value := v.(string); value != "" { + scheduler := slb.SchedulerType(value) -func validateSlbListenerStickySession(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{"", "on", "off"}, false)(v, k) -} + if scheduler != "wrr" && scheduler != "wlc" { + errors = append(errors, fmt.Errorf( + "%q must contain a valid SchedulerType, expected %s or %s, got %q", + k, "wrr", "wlc", value)) + } + } -func validateSlbListenerStickySessionType(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{"", "insert", "server"}, false)(v, k) + return } func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{"", "insert", "server"}, false)(v, k) + if value := v.(string); value != "" { + if len(value) < 1 || len(value) > 200 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 200 characters", k)) + } + } + return +} + +func validateSlbListenerCookieTimeout(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 0 || value > 86400 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer cookie timeout between 0 and 86400", + k)) + return + } + return } func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string, errors []error) { - return validation.IntBetween(0, 86400)(v, k) + value := v.(int) + if value < 0 || value > 3600 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer persistence timeout between 0 and 86400", + k)) + return + } + return +} + +func validateSlbListenerHealthCheckDomain(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + //the len add "$_ip",so to max is 84 + if len(value) < 1 || len(value) > 84 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 84 characters", k)) + } + } + return +} + +func validateSlbListenerHealthCheckUri(v interface{}, k string) (ws []string, errors []error) { + if value := v.(string); value != "" { + if len(value) < 1 || len(value) > 80 { + errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) + } + } + return +} + +func validateSlbListenerHealthCheckConnectPort(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < 1 || value > 65535 { + if value != -520 { + errors = append(errors, fmt.Errorf( + "%q must be a valid load balancer health check connect port between 1 and 65535 or -520", + k)) + return + } + + } + return +} + +func validateDBBackupPeriod(v interface{}, k string) (ws []string, errors []error) { + days := []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} + value := v.(string) + exist := false + for _, d := range days { + if value == d { + exist = true + break + } + } + if !exist { + errors = append(errors, fmt.Errorf( + "%q must contain a valid backup period value should in array %#v, got %q", + k, days, value)) + } + + return +} + +func validateAllowedStringValue(ss []string) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + existed := false + for _, s := range ss { + if s == value { + existed = true + break + } + } + if !existed { + errors = append(errors, fmt.Errorf( + "%q must contain a valid string value should in array %#v, got %q", + k, ss, value)) + } + return + + } +} + +func validateAllowedSplitStringValue(ss []string, splitStr string) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + existed := false + tsList := strings.Split(value, splitStr) + + for _, ts := range tsList { + existed = false + for _, s := range ss { + if ts == s { + existed = true + break + } + } + } + if !existed { + errors = append(errors, fmt.Errorf( + "%q must contain a valid string value should in %#v, got %q", + k, ss, value)) + } + return + + } +} + +func validateAllowedIntValue(is []int) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + existed := false + for _, i := range is { + if i == value { + existed = true + break + } + } + if !existed { + errors = append(errors, fmt.Errorf( + "%q must contain a valid int value should in array %#v, got %q", + k, is, value)) + } + return + + } +} + +func validateIntegerInRange(min, max int) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + if value < min { + errors = append(errors, fmt.Errorf( + "%q cannot be lower than %d: %d", k, min, value)) + } + if value > max { + errors = append(errors, fmt.Errorf( + "%q cannot be higher than %d: %d", k, max, value)) + } + return + } } //data source validate func @@ -244,14 +533,19 @@ func validateNameRegex(v interface{}, k string) (ws []string, errors []error) { } func validateImageOwners(v interface{}, k string) (ws []string, errors []error) { - return validation.StringInSlice([]string{ - "", - string(ecs.ImageOwnerSystem), - string(ecs.ImageOwnerSelf), - string(ecs.ImageOwnerOthers), - string(ecs.ImageOwnerMarketplace), - string(ecs.ImageOwnerDefault), - }, false)(v, k) + if value := v.(string); value != "" { + owners := ecs.ImageOwnerAlias(value) + if owners != ecs.ImageOwnerSystem && + owners != ecs.ImageOwnerSelf && + owners != ecs.ImageOwnerOthers && + owners != ecs.ImageOwnerMarketplace && + owners != ecs.ImageOwnerDefault { + errors = append(errors, fmt.Errorf( + "%q must contain a valid Image owner , expected %s, %s, %s, %s or %s, got %q", + k, ecs.ImageOwnerSystem, ecs.ImageOwnerSelf, ecs.ImageOwnerOthers, ecs.ImageOwnerMarketplace, ecs.ImageOwnerDefault, owners)) + } + } + return } func validateRegion(v interface{}, k string) (ws []string, errors []error) { diff --git a/builtin/providers/alicloud/validators_test.go b/builtin/providers/alicloud/validators_test.go index fa5a8aed0..7d40de6b7 100644 --- a/builtin/providers/alicloud/validators_test.go +++ b/builtin/providers/alicloud/validators_test.go @@ -427,3 +427,76 @@ func TestValidateSlbListenerBandwidth(t *testing.T) { } } } + +func TestValidateAllowedStringValue(t *testing.T) { + exceptValues := []string{"aliyun", "alicloud", "alibaba"} + validValues := []string{"aliyun"} + for _, v := range validValues { + _, errors := validateAllowedStringValue(exceptValues)(v, "allowvalue") + if len(errors) != 0 { + t.Fatalf("%q should be a valid value in %#v: %q", v, exceptValues, errors) + } + } + + invalidValues := []string{"ali", "alidata", "terraform"} + for _, v := range invalidValues { + _, errors := validateAllowedStringValue(exceptValues)(v, "allowvalue") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid value", v) + } + } +} + +func TestValidateAllowedStringSplitValue(t *testing.T) { + exceptValues := []string{"aliyun", "alicloud", "alibaba"} + validValues := "aliyun,alicloud" + _, errors := validateAllowedSplitStringValue(exceptValues, ",")(validValues, "allowvalue") + if len(errors) != 0 { + t.Fatalf("%q should be a valid value in %#v: %q", validValues, exceptValues, errors) + } + + invalidValues := "ali,alidata" + _, invalidErr := validateAllowedSplitStringValue(exceptValues, ",")(invalidValues, "allowvalue") + if len(invalidErr) == 0 { + t.Fatalf("%q should be an invalid value", invalidValues) + } +} + +func TestValidateAllowedIntValue(t *testing.T) { + exceptValues := []int{1, 3, 5, 6} + validValues := []int{1, 3, 5, 6} + for _, v := range validValues { + _, errors := validateAllowedIntValue(exceptValues)(v, "allowvalue") + if len(errors) != 0 { + t.Fatalf("%q should be a valid value in %#v: %q", v, exceptValues, errors) + } + } + + invalidValues := []int{0, 7, 10} + for _, v := range invalidValues { + _, errors := validateAllowedIntValue(exceptValues)(v, "allowvalue") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid value", v) + } + } +} + +func TestValidateIntegerInRange(t *testing.T) { + validIntegers := []int{-259, 0, 1, 5, 999} + min := -259 + max := 999 + for _, v := range validIntegers { + _, errors := validateIntegerInRange(min, max)(v, "name") + if len(errors) != 0 { + t.Fatalf("%q should be an integer in range (%d, %d): %q", v, min, max, errors) + } + } + + invalidIntegers := []int{-260, -99999, 1000, 25678} + for _, v := range invalidIntegers { + _, errors := validateIntegerInRange(min, max)(v, "name") + if len(errors) == 0 { + t.Fatalf("%q should be an integer outside range (%d, %d)", v, min, max) + } + } +} diff --git a/examples/alicloud-ecs-image/main.tf b/examples/alicloud-ecs-image/main.tf index 743e6b9e1..04efe08ed 100644 --- a/examples/alicloud-ecs-image/main.tf +++ b/examples/alicloud-ecs-image/main.tf @@ -9,6 +9,39 @@ resource "alicloud_security_group" "group" { description = "New security group" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "https-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "443/443" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_disk" "disk" { availability_zone = "${var.availability_zones}" diff --git a/examples/alicloud-ecs-nat/README.md b/examples/alicloud-ecs-nat/README.md new file mode 100644 index 000000000..123faebd0 --- /dev/null +++ b/examples/alicloud-ecs-nat/README.md @@ -0,0 +1,33 @@ +### Configure NAT instance Example + +In the Virtual Private Cloud(VPC) environment, to enable multiple back-end intranet hosts to provide services externally with a limited number of EIPs, map the ports on the EIP-bound host to the back-end intranet hosts. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + + Get the outputs: + + nat_instance_eip_address = 123.56.19.238 + + nat_instance_private_ip = 10.1.1.57 + + worker_instance_private_ip = 10.1.1.56 + +* Apply phase + + + login the vm: ssh root@123.56.19.238|Test123456 + + Run the "iptables -t nat -nvL" command to check the result + + | prot | in | source | destination | | + | ---- | -- | ----------- | -------------- | ------------------------ | + | tcp | * | 0.0.0.0/0 | 10.1.1.57 | tcp dpt:80 to:10.1.1.56 + | all | * | 10.1.1.0/24 | 0.0.0.0/0 | to:10.1.1.57 + + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-ecs-nat/main.tf b/examples/alicloud-ecs-nat/main.tf new file mode 100644 index 000000000..300f09746 --- /dev/null +++ b/examples/alicloud-ecs-nat/main.tf @@ -0,0 +1,98 @@ +resource "alicloud_vpc" "main" { + cidr_block = "${var.vpc_cidr}" +} + +resource "alicloud_vswitch" "main" { + vpc_id = "${alicloud_vpc.main.id}" + cidr_block = "${var.vswitch_cidr}" + availability_zone = "${var.zone}" + depends_on = ["alicloud_vpc.main"] +} + +resource "alicloud_route_entry" "entry" { + router_id = "${alicloud_vpc.main.router_id}" + route_table_id = "${alicloud_vpc.main.router_table_id}" + destination_cidrblock = "0.0.0.0/0" + nexthop_type = "Instance" + nexthop_id = "${alicloud_instance.nat.id}" +} + +resource "alicloud_instance" "nat" { + image_id = "${var.image}" + instance_type = "${var.instance_nat_type}" + availability_zone = "${var.zone}" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.main.id}" + instance_name = "nat" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + password= "${var.instance_pwd}" + + depends_on = ["alicloud_instance.worker"] + user_data = "${data.template_file.shell.rendered}" + + tags { + Name = "ecs-nat" + } +} + +data "template_file" "shell" { + template = "${file("userdata.sh")}" + + vars { + worker_private_ip = "${alicloud_instance.worker.private_ip}" + vswitch_cidr = "${var.vswitch_cidr}" + } +} + +resource "alicloud_instance" "worker" { + image_id = "${var.image}" + instance_type = "${var.instance_worker_type}" + availability_zone = "${var.zone}" + security_groups = ["${alicloud_security_group.group.id}"] + vswitch_id = "${alicloud_vswitch.main.id}" + instance_name = "worker" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" + password= "${var.instance_pwd}" + + tags { + Name = "ecs-worker" + } +} + +resource "alicloud_eip" "eip" { +} + +resource "alicloud_eip_association" "attach" { + allocation_id = "${alicloud_eip.eip.id}" + instance_id = "${alicloud_instance.nat.id}" +} + +resource "alicloud_security_group" "group" { + name = "terraform-test-group" + description = "New security group" + vpc_id = "${alicloud_vpc.main.id}" +} + +resource "alicloud_security_group_rule" "allow_in" { + security_group_id = "${alicloud_security_group.group.id}" + type = "ingress" + cidr_ip= "0.0.0.0/0" + policy = "accept" + ip_protocol= "all" + nic_type= "intranet" + port_range= "-1/-1" + priority= 1 +} + +resource "alicloud_security_group_rule" "allow_out" { + security_group_id = "${alicloud_security_group.group.id}" + type = "egress" + cidr_ip= "0.0.0.0/0" + policy = "accept" + ip_protocol= "all" + nic_type= "intranet" + port_range= "-1/-1" + priority= 1 +} \ No newline at end of file diff --git a/examples/alicloud-ecs-nat/outputs.tf b/examples/alicloud-ecs-nat/outputs.tf new file mode 100644 index 000000000..46632334d --- /dev/null +++ b/examples/alicloud-ecs-nat/outputs.tf @@ -0,0 +1,19 @@ +output "nat_instance_id" { + value = "${alicloud_instance.nat.id}" +} + +output "nat_instance_private_ip" { + value = "${alicloud_instance.nat.private_ip}" +} + +output "nat_instance_eip_address" { + value = "${alicloud_eip.eip.ip_address}" +} + +output "worker_instance_id" { + value = "${alicloud_instance.worker.id}" +} + +output "worker_instance_private_ip" { + value = "${alicloud_instance.worker.private_ip}" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-nat/userdata.sh b/examples/alicloud-ecs-nat/userdata.sh new file mode 100644 index 000000000..6cf1f4536 --- /dev/null +++ b/examples/alicloud-ecs-nat/userdata.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +PostRouting=${vswitch_cidr} +SourceRouting=`ifconfig eth0|grep inet|awk '{print $2}'|tr -d 'addr:'` +echo ${worker_private_ip}>> /etc/sysctl.conf +echo 'net.ipv4.ip_forward=1'>> /etc/sysctl.conf +sysctl -p +iptables -t nat -I POSTROUTING -s $PostRouting -j SNAT --to-source $SourceRouting +iptables -t nat -I PREROUTING -d $SourceRouting -p tcp --dport 80 -j DNAT --to ${worker_private_ip} \ No newline at end of file diff --git a/examples/alicloud-ecs-nat/variables.tf b/examples/alicloud-ecs-nat/variables.tf new file mode 100644 index 000000000..2ccec3d1a --- /dev/null +++ b/examples/alicloud-ecs-nat/variables.tf @@ -0,0 +1,27 @@ +variable "vpc_cidr" { + default = "10.1.0.0/21" +} + +variable "vswitch_cidr" { + default = "10.1.1.0/24" +} + +variable "zone" { + default = "cn-beijing-c" +} + +variable "image" { + default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" +} + +variable "instance_nat_type" { + default = "ecs.n1.small" +} + +variable "instance_worker_type" { + default = "ecs.s2.large" +} + +variable "instance_pwd" { + default = "Test123456" +} \ No newline at end of file diff --git a/examples/alicloud-ecs-slb/README.md b/examples/alicloud-ecs-slb/README.md index db4b4631e..91c585551 100644 --- a/examples/alicloud-ecs-slb/README.md +++ b/examples/alicloud-ecs-slb/README.md @@ -15,4 +15,4 @@ The example launches ECS, disk, and attached the disk on ECS. It also creates an * Destroy - terraform destroy \ No newline at end of file + terraform destroy diff --git a/examples/alicloud-ecs-slb/main.tf b/examples/alicloud-ecs-slb/main.tf index fad5c7768..8e6b9a659 100644 --- a/examples/alicloud-ecs-slb/main.tf +++ b/examples/alicloud-ecs-slb/main.tf @@ -3,33 +3,59 @@ resource "alicloud_security_group" "group" { description = "New security group" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "https-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "443/443" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "instance" { instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" image_id = "${var.image_id}" instance_type = "${var.ecs_type}" count = "${var.count}" - availability_zone = "${var.availability_zones}" security_groups = ["${alicloud_security_group.group.*.id}"] - internet_charge_type = "${var.internet_charge_type}" internet_max_bandwidth_out = "${var.internet_max_bandwidth_out}" - io_optimized = "${var.io_optimized}" - password = "${var.ecs_password}" - allocate_public_ip = "${var.allocate_public_ip}" - + availability_zone = "" instance_charge_type = "PostPaid" system_disk_category = "cloud_efficiency" - tags { role = "${var.role}" dc = "${var.datacenter}" } - } resource "alicloud_slb" "instance" { diff --git a/examples/alicloud-ecs-userdata/main.tf b/examples/alicloud-ecs-userdata/main.tf index 99376325f..b0eacf57d 100644 --- a/examples/alicloud-ecs-userdata/main.tf +++ b/examples/alicloud-ecs-userdata/main.tf @@ -11,27 +11,38 @@ resource "alicloud_vswitch" "vsw" { } resource "alicloud_security_group" "sg" { - name = "tf-sg" - description = "sg" - vpc_id = "${alicloud_vpc.default.id}" + name = "tf-sg" + description = "sg" + vpc_id = "${alicloud_vpc.default.id}" +} + +resource "alicloud_security_group_rule" "allow_ssh" { + security_group_id = "${alicloud_security_group.sg.id}" + type = "ingress" + cidr_ip= "0.0.0.0/0" + policy = "accept" + ip_protocol= "tcp" + port_range= "22/22" + priority= 1 } resource "alicloud_instance" "website" { - # cn-beijing - availability_zone = "${var.zone}" - vswitch_id = "${alicloud_vswitch.vsw.id}" - image_id = "${var.image}" + # cn-beijing + availability_zone = "${var.zone}" + vswitch_id = "${alicloud_vswitch.vsw.id}" + image_id = "${var.image}" - # series II - instance_type = "${var.ecs_type}" - io_optimized = "optimized" - system_disk_category = "cloud_efficiency" + # series II + instance_type = "${var.ecs_type}" + io_optimized = "optimized" + system_disk_category = "cloud_efficiency" - internet_charge_type = "PayByTraffic" - internet_max_bandwidth_out = 5 - allocate_public_ip = true - security_groups = ["${alicloud_security_group.sg.id}"] - instance_name = "test_foo" + internet_charge_type = "PayByTraffic" + internet_max_bandwidth_out = 5 + allocate_public_ip = true + security_groups = ["${alicloud_security_group.sg.id}"] + instance_name = "tf_website" + password= "${var.password}" - user_data = "${file("userdata.sh")}" + user_data = "${file("userdata.sh")}" } diff --git a/examples/alicloud-ecs-userdata/outputs.tf b/examples/alicloud-ecs-userdata/outputs.tf index 7115e9247..2034a7016 100644 --- a/examples/alicloud-ecs-userdata/outputs.tf +++ b/examples/alicloud-ecs-userdata/outputs.tf @@ -1,7 +1,8 @@ -output "hostname" { - value = "${alicloud_instance.website.instance_name}" -} output "ecs_id" { value = "${alicloud_instance.website.id}" +} + +output "ecs_public_ip" { + value = "${alicloud_instance.website.public_ip}" } \ No newline at end of file diff --git a/examples/alicloud-ecs-userdata/variables.tf b/examples/alicloud-ecs-userdata/variables.tf index d8809ad83..5c5475839 100644 --- a/examples/alicloud-ecs-userdata/variables.tf +++ b/examples/alicloud-ecs-userdata/variables.tf @@ -10,6 +10,10 @@ variable "zone" { default = "cn-beijing-b" } +variable "password" { + default = "Test123456" +} + variable "image" { default = "ubuntu_140405_32_40G_cloudinit_20161115.vhd" } diff --git a/examples/alicloud-ecs-vpc-cluster/main.tf b/examples/alicloud-ecs-vpc-cluster/main.tf index 0ec8bf8a0..5d0ef7b81 100644 --- a/examples/alicloud-ecs-vpc-cluster/main.tf +++ b/examples/alicloud-ecs-vpc-cluster/main.tf @@ -1,6 +1,3 @@ -provider "alicloud" { - region = "${var.region}" -} module "vpc" { availability_zones = "${var.availability_zones}" @@ -21,14 +18,12 @@ module "control-nodes" { role = "control" datacenter = "${var.datacenter}" ecs_type = "${var.control_ecs_type}" - ecs_password = "${var.ecs_password}" disk_size = "${var.control_disk_size}" ssh_username = "${var.ssh_username}" short_name = "${var.short_name}" availability_zones = "${module.vpc.availability_zones}" security_groups = ["${module.security-groups.control_security_group}"] vswitch_id = "${module.vpc.vswitch_ids}" - internet_charge_type = "${var.internet_charge_type}" } module "edge-nodes" { @@ -37,13 +32,11 @@ module "edge-nodes" { role = "edge" datacenter = "${var.datacenter}" ecs_type = "${var.edge_ecs_type}" - ecs_password = "${var.ecs_password}" ssh_username = "${var.ssh_username}" short_name = "${var.short_name}" availability_zones = "${module.vpc.availability_zones}" security_groups = ["${module.security-groups.worker_security_group}"] vswitch_id = "${module.vpc.vswitch_ids}" - internet_charge_type = "${var.internet_charge_type}" } module "worker-nodes" { @@ -52,11 +45,9 @@ module "worker-nodes" { role = "worker" datacenter = "${var.datacenter}" ecs_type = "${var.worker_ecs_type}" - ecs_password = "${var.ecs_password}" ssh_username = "${var.ssh_username}" short_name = "${var.short_name}" availability_zones = "${module.vpc.availability_zones}" security_groups = ["${module.security-groups.worker_security_group}"] vswitch_id = "${module.vpc.vswitch_ids}" - internet_charge_type = "${var.internet_charge_type}" } \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc-cluster/variables.tf b/examples/alicloud-ecs-vpc-cluster/variables.tf index 7af611862..4df8f7b4d 100644 --- a/examples/alicloud-ecs-vpc-cluster/variables.tf +++ b/examples/alicloud-ecs-vpc-cluster/variables.tf @@ -50,10 +50,6 @@ variable "availability_zones" { default = "cn-beijing-c" } -variable "internet_charge_type" { - default = "" -} - variable "datacenter" { default = "beijing" } \ No newline at end of file diff --git a/examples/alicloud-ecs-vpc/variables.tf b/examples/alicloud-ecs-vpc/variables.tf index 67664e425..e3064c15f 100644 --- a/examples/alicloud-ecs-vpc/variables.tf +++ b/examples/alicloud-ecs-vpc/variables.tf @@ -18,6 +18,7 @@ variable "short_name" { variable "ecs_type" { } variable "ecs_password" { + default = "Test12345" } variable "availability_zones" { } diff --git a/examples/alicloud-ecs-zone-type/main.tf b/examples/alicloud-ecs-zone-type/main.tf index c3c21bc9a..1817781bc 100644 --- a/examples/alicloud-ecs-zone-type/main.tf +++ b/examples/alicloud-ecs-zone-type/main.tf @@ -5,7 +5,7 @@ data "alicloud_instance_types" "1c2g" { } data "alicloud_zones" "default" { - "available_instance_type"= "${data.alicloud_instance_types.4c8g.instance_types.0.id}" + "available_instance_type"= "${data.alicloud_instance_types.1c2g.instance_types.0.id}" "available_disk_category"= "${var.disk_category}" } @@ -14,6 +14,39 @@ resource "alicloud_security_group" "group" { description = "New security group" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "https-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "443/443" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "instance" { instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" diff --git a/examples/alicloud-ecs/main.tf b/examples/alicloud-ecs/main.tf index a6d39a059..0ed077940 100644 --- a/examples/alicloud-ecs/main.tf +++ b/examples/alicloud-ecs/main.tf @@ -1,11 +1,39 @@ +data "alicloud_instance_types" "instance_type" { + instance_type_family = "ecs.n1" + cpu_core_count = "1" + memory_size = "2" +} + resource "alicloud_security_group" "group" { name = "${var.short_name}" description = "New security group" } +resource "alicloud_security_group_rule" "allow_http_80" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "${var.nic_type}" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} + + +resource "alicloud_security_group_rule" "allow_https_443" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "${var.nic_type}" + policy = "accept" + port_range = "443/443" + priority = 1 + security_group_id = "${alicloud_security_group.group.id}" + cidr_ip = "0.0.0.0/0" +} resource "alicloud_disk" "disk" { - availability_zone = "${var.availability_zones}" + availability_zone = "${alicloud_instance.instance.0.availability_zone}" category = "${var.disk_category}" size = "${var.disk_size}" count = "${var.count}" @@ -15,7 +43,7 @@ resource "alicloud_instance" "instance" { instance_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" host_name = "${var.short_name}-${var.role}-${format(var.count_format, count.index+1)}" image_id = "${var.image_id}" - instance_type = "${var.ecs_type}" + instance_type = "${data.alicloud_instance_types.instance_type.instance_types.0.id}" count = "${var.count}" availability_zone = "${var.availability_zones}" security_groups = ["${alicloud_security_group.group.*.id}"] @@ -45,5 +73,4 @@ resource "alicloud_disk_attachment" "instance-attachment" { disk_id = "${element(alicloud_disk.disk.*.id, count.index)}" instance_id = "${element(alicloud_instance.instance.*.id, count.index)}" device_name = "${var.device_name}" -} - +} \ No newline at end of file diff --git a/examples/alicloud-ecs/variables.tf b/examples/alicloud-ecs/variables.tf index c663f8dd3..dcf479d30 100644 --- a/examples/alicloud-ecs/variables.tf +++ b/examples/alicloud-ecs/variables.tf @@ -8,6 +8,10 @@ variable "image_id" { default = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" } +variable "availability_zones" { + default = "" +} + variable "role" { default = "work" } @@ -23,9 +27,6 @@ variable "ecs_type" { variable "ecs_password" { default = "Test12345" } -variable "availability_zones" { - default = "cn-beijing-b" -} variable "allocate_public_ip" { default = true } @@ -41,11 +42,15 @@ variable "io_optimized" { } variable "disk_category" { - default = "cloud_ssd" + default = "cloud_efficiency" } variable "disk_size" { default = "40" } variable "device_name" { default = "/dev/xvdb" +} + +variable "nic_type" { + default = "internet" } \ No newline at end of file diff --git a/examples/alicloud-rds/README.md b/examples/alicloud-rds/README.md new file mode 100644 index 000000000..c7b2d09d4 --- /dev/null +++ b/examples/alicloud-rds/README.md @@ -0,0 +1,17 @@ +### RDS Example + +The example launches RDS instance, database, account and grant the database readwrite privilege to the account. + +### Get up and running + +* Planning phase + + terraform plan + +* Apply phase + + terraform apply + +* Destroy + + terraform destroy \ No newline at end of file diff --git a/examples/alicloud-rds/main.tf b/examples/alicloud-rds/main.tf new file mode 100644 index 000000000..582bd9739 --- /dev/null +++ b/examples/alicloud-rds/main.tf @@ -0,0 +1,17 @@ + +resource "alicloud_db_instance" "dc" { + engine = "${var.engine}" + engine_version = "${var.engine_version}" + db_instance_class = "${var.instance_class}" + db_instance_storage = "${var.storage}" + db_instance_net_type = "${var.net_type}" + + master_user_name = "${var.user_name}" + master_user_password = "${var.password}" + + db_mappings = [{ + db_name = "${var.database_name}" + character_set_name = "${var.database_character}" + db_description = "tf" + }] +} \ No newline at end of file diff --git a/examples/alicloud-rds/outputs.tf b/examples/alicloud-rds/outputs.tf new file mode 100644 index 000000000..26c452f92 --- /dev/null +++ b/examples/alicloud-rds/outputs.tf @@ -0,0 +1,11 @@ +output "port" { + value = "${alicloud_db_instance.dc.port}" +} + +output "connections" { + value = "${alicloud_db_instance.dc.connections}" +} + +output "security_ips" { + value = "${alicloud_db_instance.dc.security_ips}" +} \ No newline at end of file diff --git a/examples/alicloud-rds/variables.tf b/examples/alicloud-rds/variables.tf new file mode 100644 index 000000000..81491c456 --- /dev/null +++ b/examples/alicloud-rds/variables.tf @@ -0,0 +1,29 @@ +variable "engine" { + default = "MySQL" +} +variable "engine_version" { + default = "5.6" +} +variable "instance_class" { + default = "rds.mysql.t1.small" +} +variable "storage" { + default = "10" +} +variable "net_type" { + default = "Intranet" +} + +variable "user_name" { + default = "tf_tester" +} +variable "password" { + default = "Test12345" +} + +variable "database_name" { + default = "bookstore" +} +variable "database_character" { + default = "utf8" +} \ No newline at end of file diff --git a/examples/alicloud-security-group-rule/main.tf b/examples/alicloud-security-group-rule/main.tf index 706ee0863..0f4d1bd0e 100644 --- a/examples/alicloud-security-group-rule/main.tf +++ b/examples/alicloud-security-group-rule/main.tf @@ -2,12 +2,23 @@ resource "alicloud_security_group" "default" { name = "${var.security_group_name}" } -resource "alicloud_security_group_rule" "allow_all_tcp" { +resource "alicloud_security_group_rule" "http-in" { type = "ingress" ip_protocol = "tcp" - nic_type = "${var.nic_type}" + nic_type = "internet" policy = "accept" - port_range = "1/65535" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.default.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "ssh-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "22/22" priority = 1 security_group_id = "${alicloud_security_group.default.id}" cidr_ip = "0.0.0.0/0" diff --git a/examples/alicloud-slb/README.md b/examples/alicloud-slb/README.md index 370476f13d377f3c79ea206832b6babe61ab681f..50289d1d634d3ce97ae2d4ef869d79d15e950ee1 100644 GIT binary patch literal 2186 zcmcImOKTf35I#r0V!)+`l8x;o4kfpimR@owwD(2Ujaj>Dr zAZax--#osV-O*?Se_p=B&kb*^LdO2q2$&Bb9P)tap6d#DRmmWY<_eVb0X4c?2qfR1 zz;eeM4Cr8Clrn3nU!NHp?J!<;N&~S9y>N1l?1YI9d~2<70jx8@2&15b6;~~S!X;2kWB&Zj0LBEQ8tB=4knc-mFBMy^6#QjG<*&|%8=t2C7DYZT2= z4`imLf~DjQMdAIL5WR+yWn~&JkJyqskGR;+q-Xe4N;~$ZL8^wM0W-d%iVaO&0NOfv z7^HN`=28c|SEZl{Di>1F8@-f0D?C|B|NNHzg#lWdMxAT5rtV_w3;b3*?ZMXE(em7x`Qb6&04iRCBf4Li!wiuTGnhyerSTe0pint+6d-* literal 470567 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

Latest

-
+
+
+ +

Join us for a live webinar with Mitchell Hashimoto to learn how Terraform provisions infrastructure across different clouds using a consistent workflow.

+

+ Register Now +

+
+
+

Terraform 0.9 Released

From ba0f80275fa0d60b31a6f0093420ee503fa12193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Pinson?= Date: Tue, 28 Mar 2017 11:18:03 +0200 Subject: [PATCH 68/97] Rancher: error when no api_url is provided (#13086) --- builtin/providers/rancher/provider.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/builtin/providers/rancher/provider.go b/builtin/providers/rancher/provider.go index 9c176943f..8df36778d 100644 --- a/builtin/providers/rancher/provider.go +++ b/builtin/providers/rancher/provider.go @@ -2,6 +2,7 @@ package rancher import ( "encoding/json" + "fmt" "io/ioutil" "net/url" "os" @@ -87,7 +88,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { return config, err } - if apiURL == "" { + if apiURL == "" && config.URL != "" { u, err := url.Parse(config.URL) if err != nil { return config, err @@ -104,6 +105,10 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { } } + if apiURL == "" { + return &Config{}, fmt.Errorf("No api_url provided") + } + config := &Config{ APIURL: apiURL + "/v1", AccessKey: accessKey, From b1d6b2e5541aa980c7a7905c2bb39ac6d668856a Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Tue, 28 Mar 2017 12:20:04 +0300 Subject: [PATCH 69/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6fb9926..fbe4a9505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ BUG FIXES: * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool [GH-13069] * provider/openstack: Resolve issues with Port Fixed IPs [GH-13056] + * provider/rancher: error when no api_url is provided [GH-13086] * provider/scaleway: work around parallel request limitation [GH-13045] ## 0.9.1 (March 17, 2017) From dd25334b463b7a764544cfe5dd195686e6e5e2dc Mon Sep 17 00:00:00 2001 From: Clint Date: Tue, 28 Mar 2017 04:29:20 -0500 Subject: [PATCH 70/97] provider/aws: Add failing test for OpsWorks endpoints (#13024) Fix an issue when upgrading from Terraform < 0.9 to 0.9+, when we added support for the regional endpoints in OpsWorks Stacks. OpsWorks Stacks can only be managed via the endpoint with which they were created, not where the stack resides. --- .../aws/resource_aws_opsworks_stack.go | 116 +++++++++- .../aws/resource_aws_opsworks_stack_test.go | 199 ++++++++++++++++++ 2 files changed, 307 insertions(+), 8 deletions(-) diff --git a/builtin/providers/aws/resource_aws_opsworks_stack.go b/builtin/providers/aws/resource_aws_opsworks_stack.go index 6a58583d5..aae83c267 100644 --- a/builtin/providers/aws/resource_aws_opsworks_stack.go +++ b/builtin/providers/aws/resource_aws_opsworks_stack.go @@ -3,14 +3,17 @@ package aws import ( "fmt" "log" + "os" "strings" "time" + "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/opsworks" ) @@ -183,6 +186,11 @@ func resourceAwsOpsworksStack() *schema.Resource { Computed: true, Optional: true, }, + + "stack_endpoint": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -254,6 +262,13 @@ func resourceAwsOpsworksSetStackCustomCookbooksSource(d *schema.ResourceData, v func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*AWSClient).opsworksconn + var conErr error + if v := d.Get("stack_endpoint").(string); v != "" { + client, conErr = opsworksConnForRegion(v, meta) + if conErr != nil { + return conErr + } + } req := &opsworks.DescribeStacksInput{ StackIds: []*string{ @@ -263,16 +278,53 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id()) - resp, err := client.DescribeStacks(req) - if err != nil { - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "ResourceNotFoundException" { - log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id()) - d.SetId("") - return nil + // notFound represents the number of times we've called DescribeStacks looking + // for this Stack. If it's not found in the the default region we're in, we + // check us-east-1 in the event this stack was created with Terraform before + // version 0.9 + // See https://github.com/hashicorp/terraform/issues/12842 + var notFound int + var resp *opsworks.DescribeStacksOutput + var dErr error + + for { + resp, dErr = client.DescribeStacks(req) + if dErr != nil { + if awserr, ok := dErr.(awserr.Error); ok { + if awserr.Code() == "ResourceNotFoundException" { + if notFound < 1 { + // If we haven't already, try us-east-1, legacy connection + notFound++ + var connErr error + client, connErr = opsworksConnForRegion("us-east-1", meta) + if connErr != nil { + return connErr + } + // start again from the top of the FOR loop, but with a client + // configured to talk to us-east-1 + continue + } + + // We've tried both the original and us-east-1 endpoint, and the stack + // is still not found + log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id()) + d.SetId("") + return nil + } + // not ResoureNotFoundException, fall through to returning error + } + return dErr + } + // If the stack was found, set the stack_endpoint + if client.Config.Region != nil && *client.Config.Region != "" { + log.Printf("[DEBUG] Setting stack_endpoint for (%s) to (%s)", d.Id(), *client.Config.Region) + if err := d.Set("stack_endpoint", *client.Config.Region); err != nil { + log.Printf("[WARN] Error setting stack_endpoint: %s", err) } } - return err + log.Printf("[DEBUG] Breaking stack endpoint search, found stack for (%s)", d.Id()) + // Break the FOR loop + break } stack := resp.Stacks[0] @@ -309,6 +361,40 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro return nil } +// opsworksConn will return a connection for the stack_endpoint in the +// configuration. Stacks can only be accessed or managed within the endpoint +// in which they are created, so we allow users to specify an original endpoint +// for Stacks created before multiple endpoints were offered (Terraform v0.9.0). +// See: +// - https://github.com/hashicorp/terraform/pull/12688 +// - https://github.com/hashicorp/terraform/issues/12842 +func opsworksConnForRegion(region string, meta interface{}) (*opsworks.OpsWorks, error) { + originalConn := meta.(*AWSClient).opsworksconn + + // Regions are the same, no need to reconfigure + if originalConn.Config.Region != nil && *originalConn.Config.Region == region { + return originalConn, nil + } + + // Set up base session + sess, err := session.NewSession(&originalConn.Config) + if err != nil { + return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err) + } + + sess.Handlers.Build.PushBackNamed(addTerraformVersionToUserAgent) + + if extraDebug := os.Getenv("TERRAFORM_AWS_AUTHFAILURE_DEBUG"); extraDebug != "" { + sess.Handlers.UnmarshalError.PushFrontNamed(debugAuthFailure) + } + + newSession := sess.Copy(&aws.Config{Region: aws.String(region)}) + newOpsworksconn := opsworks.New(newSession) + + log.Printf("[DEBUG] Returning new OpsWorks client") + return newOpsworksconn, nil +} + func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*AWSClient).opsworksconn @@ -396,6 +482,13 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*AWSClient).opsworksconn + var conErr error + if v := d.Get("stack_endpoint").(string); v != "" { + client, conErr = opsworksConnForRegion(v, meta) + if conErr != nil { + return conErr + } + } err := resourceAwsOpsworksStackValidate(d) if err != nil { @@ -456,6 +549,13 @@ func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) er func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*AWSClient).opsworksconn + var conErr error + if v := d.Get("stack_endpoint").(string); v != "" { + client, conErr = opsworksConnForRegion(v, meta) + if conErr != nil { + return conErr + } + } req := &opsworks.DeleteStackInput{ StackId: aws.String(d.Id()), diff --git a/builtin/providers/aws/resource_aws_opsworks_stack_test.go b/builtin/providers/aws/resource_aws_opsworks_stack_test.go index 532ef5570..eb366e33c 100644 --- a/builtin/providers/aws/resource_aws_opsworks_stack_test.go +++ b/builtin/providers/aws/resource_aws_opsworks_stack_test.go @@ -74,6 +74,205 @@ func TestAccAWSOpsworksStackVpc(t *testing.T) { }) } +// Tests the addition of regional endpoints and supporting the classic link used +// to create Stack's prior to v0.9.0. +// See https://github.com/hashicorp/terraform/issues/12842 +func TestAccAWSOpsWorksStack_classic_endpoints(t *testing.T) { + stackName := fmt.Sprintf("tf-opsworks-acc-%d", acctest.RandInt()) + rInt := acctest.RandInt() + var opsstack opsworks.Stack + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsOpsworksStackDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAwsOpsWorksStack_classic_endpoint(stackName, rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSOpsworksStackExists( + "aws_opsworks_stack.main", false, &opsstack), + ), + }, + // Ensure that changing to us-west-2 region results in no plan + resource.TestStep{ + Config: testAccAwsOpsWorksStack_regional_endpoint(stackName, rInt), + PlanOnly: true, + }, + }, + }) + +} + +func testAccAwsOpsWorksStack_classic_endpoint(rName string, rInt int) string { + return fmt.Sprintf(` +provider "aws" { + region = "us-east-1" +} + +resource "aws_opsworks_stack" "main" { + name = "%s" + region = "us-west-2" + service_role_arn = "${aws_iam_role.opsworks_service.arn}" + default_instance_profile_arn = "${aws_iam_instance_profile.opsworks_instance.arn}" + + configuration_manager_version = "12" + default_availability_zone = "us-west-2b" +} + +resource "aws_iam_role" "opsworks_service" { + name = "tf_opsworks_service_%d" + + assume_role_policy = < Date: Tue, 28 Mar 2017 12:30:03 +0300 Subject: [PATCH 71/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbe4a9505..dde6b7d7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ BUG FIXES: * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] * provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules [GH-12835] * provider/aws: aws_ses_receipt_rule: fix off-by-one errors [GH-12961] + * provider/aws: Fix issue upgrading to Terraform v0.9+ with AWS OpsWorks Stacks [GH-13024] * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From 6365269541c8e3150ebe638a5c555e1424071417 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Tue, 28 Mar 2017 09:56:35 +0000 Subject: [PATCH 72/97] v0.9.2 --- CHANGELOG.md | 130 +++++++++++++++++++++---------------------- terraform/version.go | 2 +- website/config.rb | 2 +- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dde6b7d7d..4297ae3cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.9.2 (unreleased) +## 0.9.2 (March 28, 2017) BACKWARDS IMCOMPATIBILITIES / NOTES: @@ -7,77 +7,77 @@ BACKWARDS IMCOMPATIBILITIES / NOTES: FEATURES: - * **New Resource:** `alicloud_db_instance` [GH-12913] - * **New Resource:** `aws_api_gateway_usage_plan` [GH-12542] - * **New Resource:** `aws_api_gateway_usage_plan_key` [GH-12851] - * **New Resource:** `github_repository_webhook` [GH-12924] - * **New Resource:** `random_pet` [GH-12903] - * **New Interpolation:** `substr` [GH-12870] + * **New Resource:** `alicloud_db_instance` ([#12913](https://github.com/hashicorp/terraform/issues/12913)) + * **New Resource:** `aws_api_gateway_usage_plan` ([#12542](https://github.com/hashicorp/terraform/issues/12542)) + * **New Resource:** `aws_api_gateway_usage_plan_key` ([#12851](https://github.com/hashicorp/terraform/issues/12851)) + * **New Resource:** `github_repository_webhook` ([#12924](https://github.com/hashicorp/terraform/issues/12924)) + * **New Resource:** `random_pet` ([#12903](https://github.com/hashicorp/terraform/issues/12903)) + * **New Interpolation:** `substr` ([#12870](https://github.com/hashicorp/terraform/issues/12870)) * **S3 Environments:** The S3 remote state backend now supports named environments IMPROVEMENTS: - * core: fix interpolation error when referencing computed values from an `aws_instance` `cidr_block` [GH-13046] - * core: fix `ignore_changes` causing fields to be removed during apply [GH-12897] - * core: add `-force-copy` option to `terraform init` to supress prompts for copying state [GH-12939] - * helper/acctest: Add NewSSHKeyPair function [GH-12894] - * provider/alicloud: simplify validators [GH-12982] - * provider/aws: Added support for EMR AutoScalingRole [GH-12823] - * provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources [GH-12629] - * provider/aws: Updated default configuration manager version in `aws_opsworks_stack` [GH-12979] - * provider/aws: Added aws_api_gateway_api_key value attribute [GH-9462] - * provider/aws: Allow aws_alb subnets to change [GH-12850] - * provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups [GH-12855] - * provider/aws: Support Import of iam_server_certificate [GH-13065] - * provider/azurerm: Add support for setting the primary network interface [GH-11290] - * provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource [GH-11306] - * provider/consul: Add support for basic auth to the provider [GH-12679] - * provider/digitalocean: Support disk only resize [GH-13059] - * provider/dnsimple: Allow dnsimple_record.priority attribute to be set [GH-12843] - * provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config [GH-12743] - * provider/google: Add local ssd count support for container clusters [GH-12281] - * provider/ignition: ignition_filesystem, explicit option to create the filesystem [GH-12980] - * provider/kubernetes: Internal K8S annotations are ignored in `config_map` [GH-12945] - * provider/ns1: Ensure provider checks for credentials [GH-12920] - * provider/openstack: Adding Timeouts to Blockstorage Resources [GH-12862] - * provider/openstack: Adding Timeouts to FWaaS v1 Resources [GH-12863] - * provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources [GH-12865] - * provider/openstack: Adding Timeouts to Network Resources [GH-12866] - * provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867] - * provider/openstack: Deprecating Instance Volume attribute [GH-13062] - * provider/openstack: Decprecating Instance Floating IP attribute [GH-13063] - * provider/openstack: Don't log the catalog [GH-13075] - * provider/openstack: Handle 409/500 Response on Pool Create [GH-13074] - * provider/pagerduty: Validate credentials [GH-12854] - * provider/openstack: Adding all_metadata attribute [GH-13061] - * provider/profitbricks: Handling missing resources [GH-13053] + * core: fix interpolation error when referencing computed values from an `aws_instance` `cidr_block` ([#13046](https://github.com/hashicorp/terraform/issues/13046)) + * core: fix `ignore_changes` causing fields to be removed during apply ([#12897](https://github.com/hashicorp/terraform/issues/12897)) + * core: add `-force-copy` option to `terraform init` to supress prompts for copying state ([#12939](https://github.com/hashicorp/terraform/issues/12939)) + * helper/acctest: Add NewSSHKeyPair function ([#12894](https://github.com/hashicorp/terraform/issues/12894)) + * provider/alicloud: simplify validators ([#12982](https://github.com/hashicorp/terraform/issues/12982)) + * provider/aws: Added support for EMR AutoScalingRole ([#12823](https://github.com/hashicorp/terraform/issues/12823)) + * provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources ([#12629](https://github.com/hashicorp/terraform/issues/12629)) + * provider/aws: Updated default configuration manager version in `aws_opsworks_stack` ([#12979](https://github.com/hashicorp/terraform/issues/12979)) + * provider/aws: Added aws_api_gateway_api_key value attribute ([#9462](https://github.com/hashicorp/terraform/issues/9462)) + * provider/aws: Allow aws_alb subnets to change ([#12850](https://github.com/hashicorp/terraform/issues/12850)) + * provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups ([#12855](https://github.com/hashicorp/terraform/issues/12855)) + * provider/aws: Support Import of iam_server_certificate ([#13065](https://github.com/hashicorp/terraform/issues/13065)) + * provider/azurerm: Add support for setting the primary network interface ([#11290](https://github.com/hashicorp/terraform/issues/11290)) + * provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource ([#11306](https://github.com/hashicorp/terraform/issues/11306)) + * provider/consul: Add support for basic auth to the provider ([#12679](https://github.com/hashicorp/terraform/issues/12679)) + * provider/digitalocean: Support disk only resize ([#13059](https://github.com/hashicorp/terraform/issues/13059)) + * provider/dnsimple: Allow dnsimple_record.priority attribute to be set ([#12843](https://github.com/hashicorp/terraform/issues/12843)) + * provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config ([#12743](https://github.com/hashicorp/terraform/issues/12743)) + * provider/google: Add local ssd count support for container clusters ([#12281](https://github.com/hashicorp/terraform/issues/12281)) + * provider/ignition: ignition_filesystem, explicit option to create the filesystem ([#12980](https://github.com/hashicorp/terraform/issues/12980)) + * provider/kubernetes: Internal K8S annotations are ignored in `config_map` ([#12945](https://github.com/hashicorp/terraform/issues/12945)) + * provider/ns1: Ensure provider checks for credentials ([#12920](https://github.com/hashicorp/terraform/issues/12920)) + * provider/openstack: Adding Timeouts to Blockstorage Resources ([#12862](https://github.com/hashicorp/terraform/issues/12862)) + * provider/openstack: Adding Timeouts to FWaaS v1 Resources ([#12863](https://github.com/hashicorp/terraform/issues/12863)) + * provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources ([#12865](https://github.com/hashicorp/terraform/issues/12865)) + * provider/openstack: Adding Timeouts to Network Resources ([#12866](https://github.com/hashicorp/terraform/issues/12866)) + * provider/openstack: Adding Timeouts to LBaaS v1 Resources ([#12867](https://github.com/hashicorp/terraform/issues/12867)) + * provider/openstack: Deprecating Instance Volume attribute ([#13062](https://github.com/hashicorp/terraform/issues/13062)) + * provider/openstack: Decprecating Instance Floating IP attribute ([#13063](https://github.com/hashicorp/terraform/issues/13063)) + * provider/openstack: Don't log the catalog ([#13075](https://github.com/hashicorp/terraform/issues/13075)) + * provider/openstack: Handle 409/500 Response on Pool Create ([#13074](https://github.com/hashicorp/terraform/issues/13074)) + * provider/pagerduty: Validate credentials ([#12854](https://github.com/hashicorp/terraform/issues/12854)) + * provider/openstack: Adding all_metadata attribute ([#13061](https://github.com/hashicorp/terraform/issues/13061)) + * provider/profitbricks: Handling missing resources ([#13053](https://github.com/hashicorp/terraform/issues/13053)) BUG FIXES: - * core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. [GH-12888] - * provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) [GH-12849] - * provider/aws: Fix flattened cloudfront lambda function associations to be a set not a slice [GH-11984] - * provider/aws: Consider ACTIVE as pending state during ECS svc deletion [GH-12986] - * provider/aws: Deprecate the usage of Api Gateway Key Stages in favor of Usage Plans [GH-12883] - * provider/aws: prevent panic in resourceAwsSsmDocumentRead [GH-12891] - * provider/aws: Prevent panic when setting AWS CodeBuild Source to state [GH-12915] - * provider/aws: Only call replace Iam Instance Profile on existing machines [GH-12922] - * provider/aws: Increase AWS AMI Destroy timeout [GH-12943] - * provider/aws: Set aws_vpc ipv6 for associated only [GH-12899] - * provider/aws: Fix AWS ECS placement strategy spread fields [GH-12998] - * provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013] - * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] - * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] - * provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules [GH-12835] - * provider/aws: aws_ses_receipt_rule: fix off-by-one errors [GH-12961] - * provider/aws: Fix issue upgrading to Terraform v0.9+ with AWS OpsWorks Stacks [GH-13024] - * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] - * provider/google: turn compute_instance_group.instances into a set [GH-12790] - * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] - * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool [GH-13069] - * provider/openstack: Resolve issues with Port Fixed IPs [GH-13056] - * provider/rancher: error when no api_url is provided [GH-13086] - * provider/scaleway: work around parallel request limitation [GH-13045] + * core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. ([#12888](https://github.com/hashicorp/terraform/issues/12888)) + * provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) ([#12849](https://github.com/hashicorp/terraform/issues/12849)) + * provider/aws: Fix flattened cloudfront lambda function associations to be a set not a slice ([#11984](https://github.com/hashicorp/terraform/issues/11984)) + * provider/aws: Consider ACTIVE as pending state during ECS svc deletion ([#12986](https://github.com/hashicorp/terraform/issues/12986)) + * provider/aws: Deprecate the usage of Api Gateway Key Stages in favor of Usage Plans ([#12883](https://github.com/hashicorp/terraform/issues/12883)) + * provider/aws: prevent panic in resourceAwsSsmDocumentRead ([#12891](https://github.com/hashicorp/terraform/issues/12891)) + * provider/aws: Prevent panic when setting AWS CodeBuild Source to state ([#12915](https://github.com/hashicorp/terraform/issues/12915)) + * provider/aws: Only call replace Iam Instance Profile on existing machines ([#12922](https://github.com/hashicorp/terraform/issues/12922)) + * provider/aws: Increase AWS AMI Destroy timeout ([#12943](https://github.com/hashicorp/terraform/issues/12943)) + * provider/aws: Set aws_vpc ipv6 for associated only ([#12899](https://github.com/hashicorp/terraform/issues/12899)) + * provider/aws: Fix AWS ECS placement strategy spread fields ([#12998](https://github.com/hashicorp/terraform/issues/12998)) + * provider/aws: Specify that aws_network_acl_rule requires a cidr block ([#13013](https://github.com/hashicorp/terraform/issues/13013)) + * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same ([#13049](https://github.com/hashicorp/terraform/issues/13049)) + * provider/aws: Only allow 1 value in alb_listener_rule condition ([#13051](https://github.com/hashicorp/terraform/issues/13051)) + * provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules ([#12835](https://github.com/hashicorp/terraform/issues/12835)) + * provider/aws: aws_ses_receipt_rule: fix off-by-one errors ([#12961](https://github.com/hashicorp/terraform/issues/12961)) + * provider/aws: Fix issue upgrading to Terraform v0.9+ with AWS OpsWorks Stacks ([#13024](https://github.com/hashicorp/terraform/issues/13024)) + * provider/fastly: Fix issue importing Fastly Services with Backends ([#12538](https://github.com/hashicorp/terraform/issues/12538)) + * provider/google: turn compute_instance_group.instances into a set ([#12790](https://github.com/hashicorp/terraform/issues/12790)) + * provider/mysql: recreate user/grant if user/grant got deleted manually ([#12791](https://github.com/hashicorp/terraform/issues/12791)) + * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool ([#13069](https://github.com/hashicorp/terraform/issues/13069)) + * provider/openstack: Resolve issues with Port Fixed IPs ([#13056](https://github.com/hashicorp/terraform/issues/13056)) + * provider/rancher: error when no api_url is provided ([#13086](https://github.com/hashicorp/terraform/issues/13086)) + * provider/scaleway: work around parallel request limitation ([#13045](https://github.com/hashicorp/terraform/issues/13045)) ## 0.9.1 (March 17, 2017) diff --git a/terraform/version.go b/terraform/version.go index 193e93cd8..d746c6519 100644 --- a/terraform/version.go +++ b/terraform/version.go @@ -12,7 +12,7 @@ const Version = "0.9.2" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "dev" +const VersionPrerelease = "" // SemVersion is an instance of version.Version. This has the secondary // benefit of verifying during tests and init time that our version is a diff --git a/website/config.rb b/website/config.rb index 6810a2abf..6e2d25511 100644 --- a/website/config.rb +++ b/website/config.rb @@ -2,7 +2,7 @@ set :base_url, "https://www.terraform.io/" activate :hashicorp do |h| h.name = "terraform" - h.version = "0.9.1" + h.version = "0.9.2" h.github_slug = "hashicorp/terraform" end From 7479b8d150d8eba258f6b04e8a3d8f239fe92973 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Tue, 28 Mar 2017 10:05:25 +0000 Subject: [PATCH 73/97] release: clean up after v0.9.2 --- CHANGELOG.md | 3 +++ terraform/version.go | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4297ae3cd..093c692af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.9.3 (unreleased) + + ## 0.9.2 (March 28, 2017) BACKWARDS IMCOMPATIBILITIES / NOTES: diff --git a/terraform/version.go b/terraform/version.go index d746c6519..ada5dcc38 100644 --- a/terraform/version.go +++ b/terraform/version.go @@ -7,12 +7,12 @@ import ( ) // The main version number that is being run at the moment. -const Version = "0.9.2" +const Version = "0.9.3" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. -const VersionPrerelease = "" +const VersionPrerelease = "dev" // SemVersion is an instance of version.Version. This has the secondary // benefit of verifying during tests and init time that our version is a From 0fb54957fe52e9600f49651bc9d048c1ff88a6de Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 28 Mar 2017 13:00:09 +0100 Subject: [PATCH 74/97] docs/google: Fix sidebar highlighting for service account (#13116) --- website/source/layouts/google.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/layouts/google.erb b/website/source/layouts/google.erb index a9429acd7..ddb3a7a80 100644 --- a/website/source/layouts/google.erb +++ b/website/source/layouts/google.erb @@ -22,7 +22,7 @@ - > + > Google Cloud Platform Resources

From 8496d0e510a3ff8b2d31e78a9be330a85da6934d Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 28 Mar 2017 20:26:16 +0100 Subject: [PATCH 91/97] docs/state: Fix broken link to remote state --- website/source/docs/state/sensitive-data.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/state/sensitive-data.html.md b/website/source/docs/state/sensitive-data.html.md index 0ddbf3e9a..acd26f8ad 100644 --- a/website/source/docs/state/sensitive-data.html.md +++ b/website/source/docs/state/sensitive-data.html.md @@ -18,7 +18,7 @@ values within the state. This is implemented on a per-resource basis and you should assume the value is plaintext unless otherwise documented. When using local state, state is stored in plain-text JSON files. When -using [remote state](/docs/state/remote.htm), state is only ever held in memory when used by Terraform. +using [remote state](/docs/state/remote.html), state is only ever held in memory when used by Terraform. It may be encrypted at rest but this depends on the specific remote state backend. From 0e3a7e6d0d97b69ed8b3bcf59de71c511b6baa54 Mon Sep 17 00:00:00 2001 From: James Nugent Date: Tue, 28 Mar 2017 15:38:42 -0400 Subject: [PATCH 92/97] helper/resource: Allow unknown pending states (#13099) Sometimes when waiting on a target state, the set of valid states through which a value will transition is unknown. This commit adds support for an empty Pending slice and will treat any states that are not the target as valid provided the timeouts are not exceeded. --- helper/resource/state.go | 2 +- helper/resource/state_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/helper/resource/state.go b/helper/resource/state.go index aafa7f3bc..7473a105e 100644 --- a/helper/resource/state.go +++ b/helper/resource/state.go @@ -141,7 +141,7 @@ func (conf *StateChangeConf) WaitForState() (interface{}, error) { } } - if !found { + if !found && len(conf.Pending) > 0 { result.Error = &UnexpectedStateError{ LastError: err, State: result.State, diff --git a/helper/resource/state_test.go b/helper/resource/state_test.go index dcbb3ce67..4b4731351 100644 --- a/helper/resource/state_test.go +++ b/helper/resource/state_test.go @@ -70,6 +70,23 @@ func InconsistentStateRefreshFunc() StateRefreshFunc { } } +func UnknownPendingStateRefreshFunc() StateRefreshFunc { + sequence := []string{ + "unknown1", "unknown2", "done", + } + + r := NewStateGenerator(sequence) + + return func() (interface{}, string, error) { + idx, s, err := r.NextState() + if err != nil { + return nil, "", err + } + + return idx, s, nil + } +} + func TestWaitForState_inconsistent_positive(t *testing.T) { conf := &StateChangeConf{ Pending: []string{"replicating"}, @@ -154,6 +171,22 @@ func TestWaitForState_success(t *testing.T) { } } +func TestWaitForState_successUnknownPending(t *testing.T) { + conf := &StateChangeConf{ + Target: []string{"done"}, + Refresh: UnknownPendingStateRefreshFunc(), + Timeout: 200 * time.Second, + } + + obj, err := conf.WaitForState() + if err != nil { + t.Fatalf("err: %s", err) + } + if obj == nil { + t.Fatalf("should return obj") + } +} + func TestWaitForState_successEmpty(t *testing.T) { conf := &StateChangeConf{ Pending: []string{"pending", "incomplete"}, From 86f5a45aab4a8611dc0ff3b505de8548cead419b Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Tue, 28 Mar 2017 22:39:08 +0300 Subject: [PATCH 93/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b3d1a0a..50a440d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ IMPROVEMENTS: * 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] * 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] From c54d6119907f72854a080eec1a466f0e81199a4c Mon Sep 17 00:00:00 2001 From: Reed Loden Date: Tue, 28 Mar 2017 12:40:56 -0700 Subject: [PATCH 94/97] Fix typo in `random_pet` documentation (#13133) s/lenth/length/ --- website/source/docs/providers/random/r/pet.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/random/r/pet.html.md b/website/source/docs/providers/random/r/pet.html.md index dd212be3e..86b457a7a 100644 --- a/website/source/docs/providers/random/r/pet.html.md +++ b/website/source/docs/providers/random/r/pet.html.md @@ -53,7 +53,7 @@ The following arguments are supported: trigger a new id to be generated. See [the main provider documentation](../index.html) for more information. -* `length` - (Optional) The lenth (in words) of the pet name. +* `length` - (Optional) The length (in words) of the pet name. * `prefix` - (Optional) A string to prefix the name with. From 52390473e260b6058d07fac3ae270089a5ebb11a Mon Sep 17 00:00:00 2001 From: = Date: Tue, 28 Mar 2017 09:46:22 -0600 Subject: [PATCH 95/97] 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 17e7f85c8..3b6bb0d0e 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 574745f9e..a6da50c9c 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 07ac7f58f..8b20abf86 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 96/97] 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 305ab9c4a..2a3b1e762 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 97/97] 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 37662760d..0c066a33f 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 46e0de459..885ee9ddd 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 1938ce0bd..118c2d71e 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 c404679c2..b242fbf16 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" {

*4pdjbB*5bCd~f5}oH00|%mcrl|Kjt* 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%0AC-AUC7%IxRTLupd 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 diff --git a/examples/alicloud-slb/main.tf b/examples/alicloud-slb/main.tf index 67f5e0cf6..a4c179f69 100644 --- a/examples/alicloud-slb/main.tf +++ b/examples/alicloud-slb/main.tf @@ -5,21 +5,50 @@ resource "alicloud_slb" "instance" { listener = [ { - "instance_port" = "2111" - "lb_port" = "21" - "lb_protocol" = "tcp" - "bandwidth" = "5" + "instance_port" = "22" + "lb_port" = "22" + "lb_protocol" = "tcp" + "bandwidth" = "10" + "health_check_type" = "http" + "persistence_timeout" = 3600 + "healthy_threshold" = 8 + "unhealthy_threshold" = 8 + "health_check_timeout" = 8 + "health_check_interval" = 5 + "health_check_http_code" = "http_2xx,http_3xx" + "health_check_timeout" = 8 }, + { - "instance_port" = "8000" - "lb_port" = "80" - "lb_protocol" = "http" - "bandwidth" = "5" - }, - { - "instance_port" = "1611" - "lb_port" = "161" + "instance_port" = "2001" + "lb_port" = "2001" "lb_protocol" = "udp" - "bandwidth" = "5" - }] + "bandwidth" = "10" + "persistence_timeout" = 3600 + "healthy_threshold" = 8 + "unhealthy_threshold" = 8 + "health_check_timeout" = 8 + "health_check_interval" = 4 + "health_check_timeout" = 8 + }, + + { + "instance_port" = "80" + "lb_port" = "80" + "lb_protocol" = "http" + "sticky_session" = "on" + "sticky_session_type" = "server" + "cookie" = "testslblistenercookie" + "cookie_timeout" = 86400 + "health_check" = "on" + "health_check_domain" = "$_ip" + "health_check_uri" = "/console" + "health_check_connect_port" = 20 + "healthy_threshold" = 8 + "unhealthy_threshold" = 8 + "health_check_timeout" = 8 + "health_check_interval" = 5 + "health_check_http_code" = "http_2xx,http_3xx" + "bandwidth" = 10 + }] } diff --git a/examples/alicloud-vpc-route-entry/main.tf b/examples/alicloud-vpc-route-entry/main.tf index 9f6876b29..00540f88b 100644 --- a/examples/alicloud-vpc-route-entry/main.tf +++ b/examples/alicloud-vpc-route-entry/main.tf @@ -23,9 +23,9 @@ resource "alicloud_security_group" "sg" { vpc_id = "${alicloud_vpc.default.id}" } -resource "alicloud_security_group_rule" "ssh" { +resource "alicloud_security_group_rule" "ssh-in" { type = "ingress" - ip_protocol = "tcp" + ip_protocol = "tcp" nic_type = "intranet" policy = "${var.rule_policy}" port_range = "22/22" @@ -34,6 +34,28 @@ resource "alicloud_security_group_rule" "ssh" { cidr_ip = "0.0.0.0/0" } +resource "alicloud_security_group_rule" "http-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "80/80" + priority = 1 + security_group_id = "${alicloud_security_group.sg.id}" + cidr_ip = "0.0.0.0/0" +} + +resource "alicloud_security_group_rule" "https-in" { + type = "ingress" + ip_protocol = "tcp" + nic_type = "internet" + policy = "accept" + port_range = "443/443" + priority = 1 + security_group_id = "${alicloud_security_group.sg.id}" + cidr_ip = "0.0.0.0/0" +} + resource "alicloud_instance" "snat" { # cn-beijing availability_zone = "${var.zone_id}" diff --git a/vendor/github.com/denverdino/aliyungo/common/client.go b/vendor/github.com/denverdino/aliyungo/common/client.go index 1fa43afae..69a9c3d1e 100755 --- a/vendor/github.com/denverdino/aliyungo/common/client.go +++ b/vendor/github.com/denverdino/aliyungo/common/client.go @@ -7,8 +7,8 @@ import ( "log" "net/http" "net/url" - "time" "strings" + "time" "github.com/denverdino/aliyungo/util" ) @@ -21,6 +21,9 @@ type Client struct { httpClient *http.Client endpoint string version string + serviceCode string + regionID Region + businessInfo string } // NewClient creates a new instance of ECS client @@ -33,6 +36,26 @@ func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret strin client.version = version } +func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) { + client.Init(endpoint, version, accessKeyId, accessKeySecret) + client.serviceCode = serviceCode + client.regionID = regionID + client.setEndpointByLocation(regionID, serviceCode, accessKeyId, accessKeySecret) +} + +//NewClient using location service +func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKeyId, accessKeySecret string) { + locationClient := NewLocationClient(accessKeyId, accessKeySecret) + ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode) + if ep == "" { + ep = loadEndpointFromFile(region, serviceCode) + } + + if ep != "" { + client.endpoint = ep + } +} + // SetEndpoint sets custom endpoint func (client *Client) SetEndpoint(endpoint string) { client.endpoint = endpoint @@ -43,6 +66,15 @@ func (client *Client) SetVersion(version string) { client.version = version } +func (client *Client) SetRegionID(regionID Region) { + client.regionID = regionID +} + +//SetServiceCode sets serviceCode +func (client *Client) SetServiceCode(serviceCode string) { + client.serviceCode = serviceCode +} + // SetAccessKeyId sets new AccessKeyId func (client *Client) SetAccessKeyId(id string) { client.AccessKeyId = id @@ -58,6 +90,15 @@ func (client *Client) SetDebug(debug bool) { client.debug = debug } +// SetBusinessInfo sets business info to log the request/response message +func (client *Client) SetBusinessInfo(businessInfo string) { + if strings.HasPrefix(businessInfo, "/") { + client.businessInfo = businessInfo + } else if businessInfo != "" { + client.businessInfo = "/" + businessInfo + } +} + // Invoke sends the raw HTTP request for ECS services func (client *Client) Invoke(action string, args interface{}, response interface{}) error { @@ -80,7 +121,7 @@ func (client *Client) Invoke(action string, args interface{}, response interface } // TODO move to util and add build val flag - httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version) + httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) @@ -128,7 +169,8 @@ func (client *Client) Invoke(action string, args interface{}, response interface // Invoke sends the raw HTTP request for ECS services //改进了一下上面那个方法,可以使用各种Http方法 -func (client *Client) InvokeByAnyMethod(method, action string, args interface{}, response interface{}) error { +//2017.1.30 增加了一个path参数,用来拓展访问的地址 +func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error { request := Request{} request.init(client.version, action, client.AccessKeyId) @@ -140,17 +182,18 @@ func (client *Client) InvokeByAnyMethod(method, action string, args interface{}, signature := util.CreateSignatureForRequest(method, &data, client.AccessKeySecret) data.Add("Signature", signature) - // Generate the request URL var ( httpReq *http.Request - err error + err error ) if method == http.MethodGet { - requestURL := client.endpoint + "?" + data.Encode() + requestURL := client.endpoint + path + "?" + data.Encode() + //fmt.Println(requestURL) httpReq, err = http.NewRequest(method, requestURL, nil) } else { - httpReq, err = http.NewRequest(method, client.endpoint, strings.NewReader(data.Encode())) + //fmt.Println(client.endpoint + path) + httpReq, err = http.NewRequest(method, client.endpoint+path, strings.NewReader(data.Encode())) httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") } @@ -159,7 +202,7 @@ func (client *Client) InvokeByAnyMethod(method, action string, args interface{}, } // TODO move to util and add build val flag - httpReq.Header.Set("X-SDK-Client", `AliyunGO/` + Version) + httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo) t0 := time.Now() httpResp, err := client.httpClient.Do(httpReq) diff --git a/vendor/github.com/denverdino/aliyungo/common/endpoint.go b/vendor/github.com/denverdino/aliyungo/common/endpoint.go new file mode 100644 index 000000000..16bcbf9d6 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/endpoint.go @@ -0,0 +1,118 @@ +package common + +import ( + "encoding/xml" + "fmt" + "io/ioutil" + "os" + "strings" +) + +const ( + // LocationDefaultEndpoint is the default API endpoint of Location services + locationDefaultEndpoint = "https://location.aliyuncs.com" + locationAPIVersion = "2015-06-12" + HTTP_PROTOCOL = "http" + HTTPS_PROTOCOL = "https" +) + +var ( + endpoints = make(map[Region]map[string]string) +) + +//init endpoints from file +func init() { + +} + +func NewLocationClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("LOCATION_ENDPOINT") + if endpoint == "" { + endpoint = locationDefaultEndpoint + } + + client := &Client{} + client.Init(endpoint, locationAPIVersion, accessKeyId, accessKeySecret) + return client +} + +func (client *Client) DescribeEndpoint(args *DescribeEndpointArgs) (*DescribeEndpointResponse, error) { + response := &DescribeEndpointResponse{} + err := client.Invoke("DescribeEndpoint", args, response) + if err != nil { + return nil, err + } + return response, err +} + +func getProductRegionEndpoint(region Region, serviceCode string) string { + if sp, ok := endpoints[region]; ok { + if endpoint, ok := sp[serviceCode]; ok { + return endpoint + } + } + + return "" +} + +func setProductRegionEndpoint(region Region, serviceCode string, endpoint string) { + endpoints[region] = map[string]string{ + serviceCode: endpoint, + } +} + +func (client *Client) DescribeOpenAPIEndpoint(region Region, serviceCode string) string { + if endpoint := getProductRegionEndpoint(region, serviceCode); endpoint != "" { + return endpoint + } + + defaultProtocols := HTTP_PROTOCOL + + args := &DescribeEndpointArgs{ + Id: region, + ServiceCode: serviceCode, + Type: "openAPI", + } + + endpoint, err := client.DescribeEndpoint(args) + if err != nil || endpoint.Endpoint == "" { + return "" + } + + for _, protocol := range endpoint.Protocols.Protocols { + if strings.ToLower(protocol) == HTTPS_PROTOCOL { + defaultProtocols = HTTPS_PROTOCOL + break + } + } + + ep := fmt.Sprintf("%s://%s", defaultProtocols, endpoint.Endpoint) + + setProductRegionEndpoint(region, serviceCode, ep) + return ep +} + +func loadEndpointFromFile(region Region, serviceCode string) string { + data, err := ioutil.ReadFile("./endpoints.xml") + if err != nil { + return "" + } + + var endpoints Endpoints + err = xml.Unmarshal(data, &endpoints) + if err != nil { + return "" + } + + for _, endpoint := range endpoints.Endpoint { + if endpoint.RegionIds.RegionId == string(region) { + for _, product := range endpoint.Products.Product { + if strings.ToLower(product.ProductName) == serviceCode { + return fmt.Sprintf("%s://%s", HTTPS_PROTOCOL, product.DomainName) + } + } + } + } + + return "" +} diff --git a/vendor/github.com/denverdino/aliyungo/common/endpoints.xml b/vendor/github.com/denverdino/aliyungo/common/endpoints.xml new file mode 100644 index 000000000..8e781ac46 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/common/endpoints.xml @@ -0,0 +1,1351 @@ + + + + jp-fudao-1 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + me-east-1 + + Rdsrds.me-east-1.aliyuncs.com + Ecsecs.me-east-1.aliyuncs.com + Vpcvpc.me-east-1.aliyuncs.com + Kmskms.me-east-1.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.me-east-1.aliyuncs.com + + + + us-east-1 + + CScs.aliyuncs.com + Pushcloudpush.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + HPChpc.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Msgmsg-inner.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Bssbss.aliyuncs.com + Workorderworkorder.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ubsmsubsms.aliyuncs.com + Vpcvpc.aliyuncs.com + Alertalert.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + CFcf.aliyuncs.com + Drdsdrds.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Stssts.aliyuncs.com + Dtsdts.aliyuncs.com + Drcdrc.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Alidnsalidns.aliyuncs.com + Onsons.aliyuncs.com + Cdncdn.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + ap-northeast-1 + + Rdsrds.ap-northeast-1.aliyuncs.com + Kmskms.ap-northeast-1.aliyuncs.com + Vpcvpc.ap-northeast-1.aliyuncs.com + Ecsecs.ap-northeast-1.aliyuncs.com + Cmsmetrics.ap-northeast-1.aliyuncs.com + Kvstorer-kvstore.ap-northeast-1.aliyuncs.com + Slbslb.ap-northeast-1.aliyuncs.com + + + + cn-hangzhou-bj-b01 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-hongkong + + Pushcloudpush.aliyuncs.com + COScos.aliyuncs.com + Onsons.aliyuncs.com + Essess.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + CScs.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Drcdrc.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Dmdm.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + HPChpc.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Vpcvpc.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Bssbss.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Stssts.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Greengreen.aliyuncs.com + Aasaas.aliyuncs.com + Alidnsalidns.aliyuncs.com + Dtsdts.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Rdsrds.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + Ossoss-cn-hongkong.aliyuncs.com + + + + cn-beijing-nu16-b01 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-beijing-am13-c01 + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + in-west-antgroup-1 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-guizhou-gov + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + in-west-antgroup-2 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-qingdao-cm9 + + CScs.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Drdsdrds.aliyuncs.com + HPChpc.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Alertalert.aliyuncs.com + Mscmsc-inner.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + AMSams.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Bssbss.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Stssts.aliyuncs.com + Dtsdts.aliyuncs.com + Emremr.aliyuncs.com + Drcdrc.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + ROSros.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Cdncdn.aliyuncs.com + + + + tw-snowcloud-kaohsiung + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-shanghai-finance-1 + + Kmskms.cn-shanghai-finance-1.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + Rdsrds.aliyuncs.com + + + + cn-guizhou + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + cn-qingdao-finance + + Ossoss-cn-qdjbp-a.aliyuncs.com + + + + cn-beijing-gov-1 + + Ossoss-cn-haidian-a.aliyuncs.com + Rdsrds.aliyuncs.com + + + + cn-shanghai + + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + HPChpc.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Drcdrc.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Drdsdrds.aliyuncs.com + CScs.aliyuncs.com + Kmskms.cn-shanghai.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Alertalert.aliyuncs.com + Mscmsc-inner.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.cn-shanghai.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + Bssbss.aliyuncs.com + Omsoms.aliyuncs.com + Ubsmsubsms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Apigatewayapigateway.cn-shanghai.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Stssts.aliyuncs.com + Vpcvpc.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Ddsmongodb.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Pushcloudpush.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.aliyuncs.com + Dtsdts.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Jaqjaq.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + jaqjaq.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Rdsrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Essess.aliyuncs.com + Ossoss-cn-shanghai.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + cn-shenzhen-inner + + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + Onsons.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + HPChpc.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + jaqjaq.aliyuncs.com + Mscmsc-inner.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Bssbss.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + Alertalert.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + AMSams.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Stssts.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Greengreen.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.aliyuncs.com + CScs.aliyuncs.com + Drcdrc.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + Dtsdts.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + ROSros.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Rdsrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + + + + cn-fujian + + Ecsecs-cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + + + + in-mumbai-alipay + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + us-west-1 + + CScs.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Stssts.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + Pushcloudpush.aliyuncs.com + Alidnsalidns.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Bssbss.aliyuncs.com + Mscmsc-inner.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + Alertalert.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Vpcvpc.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.aliyuncs.com + HPChpc.aliyuncs.com + Drcdrc.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Dtsdts.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + jaqjaq.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Onsons.aliyuncs.com + Ossoss-us-west-1.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + cn-shanghai-inner + + CScs.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.aliyuncs.com + Smssms.aliyuncs.com + Drdsdrds.aliyuncs.com + HPChpc.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Msgmsg-inner.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + jaqjaq.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + Bssbss.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Alertalert.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Stssts.aliyuncs.com + Dtsdts.aliyuncs.com + Drcdrc.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Alidnsalidns.aliyuncs.com + Onsons.aliyuncs.com + Cdncdn.aliyuncs.com + + + + cn-anhui-gov-1 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-hangzhou-finance + + Ossoss-cn-hzjbp-b-console.aliyuncs.com + + + + cn-hangzhou + + CScs.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Stssts.aliyuncs.com + Smssms.aliyuncs.com + Msgmsg-inner.aliyuncs.com + Jaqjaq.aliyuncs.com + Pushcloudpush.aliyuncs.com + Livelive.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Hpchpc.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Drcdrc.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.cn-hangzhou.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Vpcvpc.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + Domaindomain.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Ubsmsubsms.aliyuncs.com + Apigatewayapigateway.cn-hangzhou.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Oascn-hangzhou.oas.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Alidnsalidns.aliyuncs.com + HPChpc.aliyuncs.com + Emremr.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + Dtsdts.aliyuncs.com + Bssbss.aliyuncs.com + Otsots-pop.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Rdsrds.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Onsons.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + cn-beijing-inner + + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + HPChpc.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.aliyuncs.com + Smssms.aliyuncs.com + Drdsdrds.aliyuncs.com + CScs.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Msgmsg-inner.aliyuncs.com + Essess.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Bssbss.aliyuncs.com + Workorderworkorder.aliyuncs.com + Drcdrc.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Dmdm.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + Otsots-pop.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Stssts.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + CFcf.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Greengreen.aliyuncs.com + Aasaas.aliyuncs.com + Alidnsalidns.aliyuncs.com + Pushcloudpush.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + Dtsdts.aliyuncs.com + Domaindomain.aliyuncs.com + ROSros.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Cdncdn.aliyuncs.com + + + + cn-haidian-cm12-c01 + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + Rdsrds.aliyuncs.com + + + + cn-anhui-gov + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + cn-shenzhen + + CScs.aliyuncs.com + COScos.aliyuncs.com + Onsons.aliyuncs.com + Essess.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + Pushcloudpush.aliyuncs.com + Kmskms.cn-shenzhen.aliyuncs.com + Locationlocation.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Alertalert.aliyuncs.com + Drcdrc.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Iotiot.aliyuncs.com + HPChpc.aliyuncs.com + Bssbss.aliyuncs.com + Omsoms.aliyuncs.com + Ubsmsubsms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + BatchComputebatchcompute.cn-shenzhen.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Apigatewayapigateway.cn-shenzhen.aliyuncs.com + CloudAPIapigateway.cn-shenzhen.aliyuncs.com + Stssts.aliyuncs.com + Vpcvpc.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Oascn-shenzhen.oas.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Crmcrm-cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.aliyuncs.com + Dtsdts.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + jaqjaq.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Greengreen.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Ossoss-cn-shenzhen.aliyuncs.com + + + + ap-southeast-2 + + Rdsrds.ap-southeast-2.aliyuncs.com + Kmskms.ap-southeast-2.aliyuncs.com + Vpcvpc.ap-southeast-2.aliyuncs.com + Ecsecs.ap-southeast-2.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.ap-southeast-2.aliyuncs.com + + + + cn-qingdao + + CScs.aliyuncs.com + COScos.aliyuncs.com + HPChpc.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.cn-qingdao.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + Dtsdts.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Essess.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + Drcdrc.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.cn-qingdao.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Dmdm.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Iotiot.aliyuncs.com + Bssbss.aliyuncs.com + Omsoms.aliyuncs.com + Ubsmsubsms.cn-qingdao.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + BatchComputebatchcompute.cn-qingdao.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Otsots-pop.aliyuncs.com + PTSpts.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Apigatewayapigateway.cn-qingdao.aliyuncs.com + CloudAPIapigateway.cn-qingdao.aliyuncs.com + Stssts.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Greengreen.aliyuncs.com + Aasaas.aliyuncs.com + Alidnsalidns.aliyuncs.com + Pushcloudpush.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + Domaindomain.aliyuncs.com + ROSros.aliyuncs.com + jaqjaq.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Ossoss-cn-qingdao.aliyuncs.com + + + + cn-shenzhen-su18-b02 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-shenzhen-su18-b03 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + cn-shenzhen-su18-b01 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + ap-southeast-antgroup-1 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + oss-cn-bjzwy + + Ossoss-cn-bjzwy.aliyuncs.com + + + + cn-henan-am12001 + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + cn-beijing + + CScs.aliyuncs.com + COScos.aliyuncs.com + Jaqjaq.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Stssts.aliyuncs.com + Smssms.aliyuncs.com + Msgmsg-inner.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + HPChpc.aliyuncs.com + Oascn-beijing.oas.aliyuncs.com + Locationlocation.aliyuncs.com + Onsons.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Hpchpc.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + jaqjaq.aliyuncs.com + Workorderworkorder.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Bssbss.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + Alertalert.aliyuncs.com + Omsoms.aliyuncs.com + Ubsmsubsms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Apigatewayapigateway.cn-beijing.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Kmskms.cn-beijing.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.aliyuncs.com + Dtsdts.aliyuncs.com + Drcdrc.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Ossoss-cn-beijing.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + Rdsrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Alidnsalidns.aliyuncs.com + Greengreen.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Cdncdn.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + cn-hangzhou-d + + CScs.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Emremr.aliyuncs.com + Smssms.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Dtsdts.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Bssbss.aliyuncs.com + Mscmsc-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Alidnsalidns.aliyuncs.com + Iotiot.aliyuncs.com + HPChpc.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Alertalert.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + Otsots-pop.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Ubsmsubsms.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + CFcf.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Greengreen.aliyuncs.com + Aasaas.aliyuncs.com + Stssts.aliyuncs.com + Pushcloudpush.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + ROSros.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Drcdrc.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + + + + cn-gansu-am6 + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + Rdsrds.aliyuncs.com + + + + cn-ningxiazhongwei + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + cn-shanghai-et2-b01 + + CScs.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + Onsons.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Jaqjaq.aliyuncs.com + Dtsdts.aliyuncs.com + Kmskms.cn-hangzhou.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Bssbss.aliyuncs.com + Mscmsc-inner.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Dmdm.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + Ubsmsubsms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Ace-opsace-ops.cn-hangzhou.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + AMSams.aliyuncs.com + Otsots-pop.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Acsacs.aliyun-inc.com + Httpdnshttpdns-api.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Stssts.aliyuncs.com + HPChpc.aliyuncs.com + Emremr.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.aliyuncs.com + Slbslb.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + Domaindomain.aliyuncs.com + ROSros.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Drdsdrds.aliyuncs.com + Vpc-innervpc-inner.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Greengreen.aliyuncs.com + Drcdrc.aliyuncs.com + Ossoss-cn-hangzhou.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + + + + cn-ningxia-am7-c01 + + Ecsecs-cn-hangzhou.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + cn-shenzhen-finance-1 + + Kmskms.cn-shenzhen-finance-1.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + Vpcvpc.aliyuncs.com + + + + ap-southeast-1 + + CScs.aliyuncs.com + Riskrisk-cn-hangzhou.aliyuncs.com + COScos.aliyuncs.com + Essess.aliyuncs.com + Billingbilling.aliyuncs.com + Dqsdqs.aliyuncs.com + Ddsmongodb.aliyuncs.com + Alidnsalidns.aliyuncs.com + Smssms.aliyuncs.com + Drdsdrds.aliyuncs.com + Dtsdts.aliyuncs.com + Kmskms.ap-southeast-1.aliyuncs.com + Locationlocation.aliyuncs.com + Msgmsg-inner.aliyuncs.com + ChargingServicechargingservice.aliyuncs.com + R-kvstorer-kvstore-cn-hangzhou.aliyuncs.com + Alertalert.aliyuncs.com + Mscmsc-inner.aliyuncs.com + HighDDosyd-highddos-cn-hangzhou.aliyuncs.com + Yundunyundun-cn-hangzhou.aliyuncs.com + Ubsms-innerubsms-inner.aliyuncs.com + Ocsm-kvstore.aliyuncs.com + Dmdm.aliyuncs.com + Greengreen.aliyuncs.com + Commondrivercommon.driver.aliyuncs.com + oceanbaseoceanbase.aliyuncs.com + Workorderworkorder.aliyuncs.com + Yundunhsmyundunhsm.aliyuncs.com + Iotiot.aliyuncs.com + HPChpc.aliyuncs.com + jaqjaq.aliyuncs.com + Omsoms.aliyuncs.com + livelive.aliyuncs.com + Ecsecs-cn-hangzhou.aliyuncs.com + M-kvstorem-kvstore.aliyuncs.com + Vpcvpc.aliyuncs.com + BatchComputebatchCompute.aliyuncs.com + AMSams.aliyuncs.com + ROSros.aliyuncs.com + PTSpts.aliyuncs.com + Qualitycheckqualitycheck.aliyuncs.com + Bssbss.aliyuncs.com + Ubsmsubsms.aliyuncs.com + Apigatewayapigateway.ap-southeast-1.aliyuncs.com + CloudAPIapigateway.cn-hangzhou.aliyuncs.com + Stssts.aliyuncs.com + CmsSiteMonitorsitemonitor.aliyuncs.com + Aceace.cn-hangzhou.aliyuncs.com + Mtsmts.cn-hangzhou.aliyuncs.com + CFcf.aliyuncs.com + Crmcrm-cn-hangzhou.aliyuncs.com + Location-innerlocation-inner.aliyuncs.com + Aasaas.aliyuncs.com + Emremr.ap-southeast-1.aliyuncs.com + Httpdnshttpdns-api.aliyuncs.com + Drcdrc.aliyuncs.com + Pushcloudpush.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.aliyuncs.com + YundunDdosinner-yundun-ddos.cn-hangzhou.aliyuncs.com + Domaindomain.aliyuncs.com + Otsots-pop.aliyuncs.com + Cdncdn.aliyuncs.com + Ramram.aliyuncs.com + Salessales.cn-hangzhou.aliyuncs.com + Rdsrds.aliyuncs.com + OssAdminoss-admin.aliyuncs.com + Onsons.aliyuncs.com + Ossoss-ap-southeast-1.aliyuncs.com + + + + cn-shenzhen-st4-d01 + + Ecsecs-cn-hangzhou.aliyuncs.com + + + + eu-central-1 + + Rdsrds.eu-central-1.aliyuncs.com + Ecsecs.eu-central-1.aliyuncs.com + Vpcvpc.eu-central-1.aliyuncs.com + Kmskms.eu-central-1.aliyuncs.com + Cmsmetrics.cn-hangzhou.aliyuncs.com + Slbslb.eu-central-1.aliyuncs.com + + + \ No newline at end of file diff --git a/vendor/github.com/denverdino/aliyungo/common/regions.go b/vendor/github.com/denverdino/aliyungo/common/regions.go index 781a727bc..62e6e9d81 100644 --- a/vendor/github.com/denverdino/aliyungo/common/regions.go +++ b/vendor/github.com/denverdino/aliyungo/common/regions.go @@ -5,23 +5,28 @@ type Region string // Constants of region definition const ( - Hangzhou = Region("cn-hangzhou") - Qingdao = Region("cn-qingdao") - Beijing = Region("cn-beijing") - Hongkong = Region("cn-hongkong") - Shenzhen = Region("cn-shenzhen") - USWest1 = Region("us-west-1") - USEast1 = Region("us-east-1") + Hangzhou = Region("cn-hangzhou") + Qingdao = Region("cn-qingdao") + Beijing = Region("cn-beijing") + Hongkong = Region("cn-hongkong") + Shenzhen = Region("cn-shenzhen") + Shanghai = Region("cn-shanghai") + Zhangjiakou = Region("cn-zhangjiakou") + APSouthEast1 = Region("ap-southeast-1") - Shanghai = Region("cn-shanghai") - MEEast1 = Region("me-east-1") APNorthEast1 = Region("ap-northeast-1") APSouthEast2 = Region("ap-southeast-2") - EUCentral1 = Region("eu-central-1") + + USWest1 = Region("us-west-1") + USEast1 = Region("us-east-1") + + MEEast1 = Region("me-east-1") + + EUCentral1 = Region("eu-central-1") ) var ValidRegions = []Region{ - Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, + Hangzhou, Qingdao, Beijing, Shenzhen, Hongkong, Shanghai, Zhangjiakou, USWest1, USEast1, APNorthEast1, APSouthEast1, APSouthEast2, MEEast1, diff --git a/vendor/github.com/denverdino/aliyungo/common/types.go b/vendor/github.com/denverdino/aliyungo/common/types.go index c562aedfc..a74e150e9 100644 --- a/vendor/github.com/denverdino/aliyungo/common/types.go +++ b/vendor/github.com/denverdino/aliyungo/common/types.go @@ -13,3 +13,77 @@ const ( PrePaid = InstanceChargeType("PrePaid") PostPaid = InstanceChargeType("PostPaid") ) + +type DescribeEndpointArgs struct { + Id Region + ServiceCode string + Type string +} + +type EndpointItem struct { + Protocols struct { + Protocols []string + } + Type string + Namespace string + Id Region + SerivceCode string + Endpoint string +} + +type DescribeEndpointResponse struct { + Response + EndpointItem +} + +type NetType string + +const ( + Internet = NetType("Internet") + Intranet = NetType("Intranet") +) + +type TimeType string + +const ( + Hour = TimeType("Hour") + Day = TimeType("Day") + Month = TimeType("Month") + Year = TimeType("Year") +) + +type NetworkType string + +const ( + Classic = NetworkType("Classic") + VPC = NetworkType("VPC") +) + +type BusinessInfo struct { + Pack string `json:"pack,omitempty"` + ActivityId string `json:"activityId,omitempty"` +} + +//xml +type Endpoints struct { + Endpoint []Endpoint `xml:"Endpoint"` +} + +type Endpoint struct { + Name string `xml:"name,attr"` + RegionIds RegionIds `xml:"RegionIds"` + Products Products `xml:"Products"` +} + +type RegionIds struct { + RegionId string `xml:"RegionId"` +} + +type Products struct { + Product []Product `xml:"Product"` +} + +type Product struct { + ProductName string `xml:"ProductName"` + DomainName string `xml:"DomainName"` +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/client.go b/vendor/github.com/denverdino/aliyungo/ecs/client.go index 063c0738c..d70a1554e 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/client.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/client.go @@ -1,8 +1,9 @@ package ecs import ( - "github.com/denverdino/aliyungo/common" "os" + + "github.com/denverdino/aliyungo/common" ) // Interval for checking status in WaitForXXX method @@ -19,6 +20,12 @@ const ( // ECSDefaultEndpoint is the default API endpoint of ECS services ECSDefaultEndpoint = "https://ecs-cn-hangzhou.aliyuncs.com" ECSAPIVersion = "2014-05-26" + + ECSServiceCode = "ecs" + + VPCDefaultEndpoint = "https://vpc.aliyuncs.com" + VPCAPIVersion = "2016-04-28" + VPCServiceCode = "vpc" ) // NewClient creates a new instance of ECS client @@ -30,8 +37,38 @@ func NewClient(accessKeyId, accessKeySecret string) *Client { return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) } +func NewECSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + endpoint := os.Getenv("ECS_ENDPOINT") + if endpoint == "" { + endpoint = ECSDefaultEndpoint + } + + return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) +} + +func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret, ECSServiceCode, regionID) + return client +} + func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { client := &Client{} client.Init(endpoint, ECSAPIVersion, accessKeyId, accessKeySecret) return client } + +func NewVPCClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + endpoint := os.Getenv("VPC_ENDPOINT") + if endpoint == "" { + endpoint = VPCDefaultEndpoint + } + + return NewVPCClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) +} + +func NewVPCClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, VPCAPIVersion, accessKeyId, accessKeySecret, VPCServiceCode, regionID) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/ecs/images.go b/vendor/github.com/denverdino/aliyungo/ecs/images.go index c623caf9c..0a4e1e2c0 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/images.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/images.go @@ -63,8 +63,12 @@ type DescribeImagesResponse struct { type DiskDeviceMapping struct { SnapshotId string //Why Size Field is string-type. - Size string - Device string + Size string + Device string + //For import images + Format string + OSSBucket string + OSSObject string } // @@ -112,6 +116,7 @@ func (client *Client) DescribeImages(args *DescribeImagesArgs) (images []ImageTy type CreateImageArgs struct { RegionId common.Region SnapshotId string + InstanceId string ImageName string ImageVersion string Description string @@ -227,6 +232,38 @@ func (client *Client) CopyImage(args *CopyImageArgs) (string, error) { return response.ImageId, nil } + +// ImportImageArgs repsents arguements to import image from oss +type ImportImageArgs struct { + RegionId common.Region + ImageName string + ImageVersion string + Description string + ClientToken string + Architecture string + OSType string + Platform string + DiskDeviceMappings struct { + DiskDeviceMapping []DiskDeviceMapping + } +} + +func (client *Client) ImportImage(args *ImportImageArgs) (string, error) { + response := &CopyImageResponse{} + err := client.Invoke("ImportImage", args, &response) + if err != nil { + return "", err + } + return response.ImageId, nil +} + +type ImportImageResponse struct { + common.Response + RegionId common.Region + ImageId string + ImportTaskId string +} + // Default timeout value for WaitForImageReady method const ImageDefaultTimeout = 120 diff --git a/vendor/github.com/denverdino/aliyungo/ecs/instances.go b/vendor/github.com/denverdino/aliyungo/ecs/instances.go index cce16dff4..f2fe74e98 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/instances.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/instances.go @@ -15,12 +15,14 @@ type InstanceStatus string // Constants of InstanceStatus const ( - Creating = InstanceStatus("Creating") + Creating = InstanceStatus("Creating") // For backward compatability + Pending = InstanceStatus("Pending") Running = InstanceStatus("Running") Starting = InstanceStatus("Starting") Stopped = InstanceStatus("Stopped") Stopping = InstanceStatus("Stopping") + Deleted = InstanceStatus("Deleted") ) type LockReason string @@ -279,6 +281,7 @@ type ModifyInstanceAttributeArgs struct { Description string Password string HostName string + UserData string } type ModifyInstanceAttributeResponse struct { @@ -323,6 +326,38 @@ func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, return nil } +// WaitForInstance waits for instance to given status +// when instance.NotFound wait until timeout +func (client *Client) WaitForInstanceAsyn(instanceId string, status InstanceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + instance, err := client.DescribeInstanceAttribute(instanceId) + if err != nil { + e, _ := err.(*common.Error) + if e.ErrorResponse.Code != "InvalidInstanceId.NotFound" { + return err + } + time.Sleep(DefaultWaitForInterval * time.Second) + continue + } + if instance.Status == status { + //TODO + //Sleep one more time for timing issues + time.Sleep(DefaultWaitForInterval * time.Second) + break + } + timeout = timeout - DefaultWaitForInterval + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + time.Sleep(DefaultWaitForInterval * time.Second) + + } + return nil +} + type DescribeInstanceVncUrlArgs struct { RegionId common.Region InstanceId string @@ -510,6 +545,43 @@ func (client *Client) CreateInstance(args *CreateInstanceArgs) (instanceId strin return response.InstanceId, err } +type RunInstanceArgs struct { + CreateInstanceArgs + MinAmount int + MaxAmount int + AutoReleaseTime string + NetworkType string + InnerIpAddress string + BusinessInfo string +} + +type RunInstanceResponse struct { + common.Response + InstanceIdSets InstanceIdSets +} + +type InstanceIdSets struct { + InstanceIdSet []string +} + +type BusinessInfo struct { + Pack string `json:"pack,omitempty"` + ActivityId string `json:"activityId,omitempty"` +} + +func (client *Client) RunInstances(args *RunInstanceArgs) (instanceIdSet []string, err error) { + if args.UserData != "" { + // Encode to base64 string + args.UserData = base64.StdEncoding.EncodeToString([]byte(args.UserData)) + } + response := RunInstanceResponse{} + err = client.Invoke("RunInstances", args, &response) + if err != nil { + return nil, err + } + return response.InstanceIdSets.InstanceIdSet, err +} + type SecurityGroupArgs struct { InstanceId string SecurityGroupId string diff --git a/builtin/providers/alicloud/extension_nat_gateway.go b/vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go similarity index 80% rename from builtin/providers/alicloud/extension_nat_gateway.go rename to vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go index 3dac446a3..dfcb74d32 100644 --- a/builtin/providers/alicloud/extension_nat_gateway.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/nat_gateway.go @@ -1,8 +1,7 @@ -package alicloud +package ecs import ( "github.com/denverdino/aliyungo/common" - "github.com/denverdino/aliyungo/ecs" ) type BandwidthPackageType struct { @@ -25,6 +24,10 @@ type ForwardTableIdType struct { ForwardTableId []string } +type SnatTableIdType struct { + SnatTableId []string +} + type BandwidthPackageIdType struct { BandwidthPackageId []string } @@ -39,7 +42,7 @@ type CreateNatGatewayResponse struct { // CreateNatGateway creates Virtual Private Cloud // // You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/vpc&createvpc -func CreateNatGateway(client *ecs.Client, args *CreateNatGatewayArgs) (resp *CreateNatGatewayResponse, err error) { +func (client *Client) CreateNatGateway(args *CreateNatGatewayArgs) (resp *CreateNatGatewayResponse, err error) { response := CreateNatGatewayResponse{} err = client.Invoke("CreateNatGateway", args, &response) if err != nil { @@ -53,6 +56,7 @@ type NatGatewaySetType struct { Description string BandwidthPackageIds BandwidthPackageIdType ForwardTableIds ForwardTableIdType + SnatTableIds SnatTableIdType InstanceChargeType string Name string NatGatewayId string @@ -77,7 +81,7 @@ type DescribeNatGatewaysArgs struct { common.Pagination } -func DescribeNatGateways(client *ecs.Client, args *DescribeNatGatewaysArgs) (natGateways []NatGatewaySetType, +func (client *Client) DescribeNatGateways(args *DescribeNatGatewaysArgs) (natGateways []NatGatewaySetType, pagination *common.PaginationResult, err error) { args.Validate() @@ -103,7 +107,7 @@ type ModifyNatGatewayAttributeResponse struct { common.Response } -func ModifyNatGatewayAttribute(client *ecs.Client, args *ModifyNatGatewayAttributeArgs) error { +func (client *Client) ModifyNatGatewayAttribute(args *ModifyNatGatewayAttributeArgs) error { response := ModifyNatGatewayAttributeResponse{} return client.Invoke("ModifyNatGatewayAttribute", args, &response) } @@ -114,7 +118,7 @@ type ModifyNatGatewaySpecArgs struct { Spec NatGatewaySpec } -func ModifyNatGatewaySpec(client *ecs.Client, args *ModifyNatGatewaySpecArgs) error { +func (client *Client) ModifyNatGatewaySpec(args *ModifyNatGatewaySpecArgs) error { response := ModifyNatGatewayAttributeResponse{} return client.Invoke("ModifyNatGatewaySpec", args, &response) } @@ -128,7 +132,7 @@ type DeleteNatGatewayResponse struct { common.Response } -func DeleteNatGateway(client *ecs.Client, args *DeleteNatGatewayArgs) error { +func (client *Client) DeleteNatGateway(args *DeleteNatGatewayArgs) error { response := DeleteNatGatewayResponse{} err := client.Invoke("DeleteNatGateway", args, &response) return err @@ -140,10 +144,20 @@ type DescribeBandwidthPackagesArgs struct { NatGatewayId string } +type PublicIpAddresseType struct { + AllocationId string + IpAddress string +} + type DescribeBandwidthPackageType struct { Bandwidth string BandwidthPackageId string IpCount string + PublicIpAddresses struct { + PublicIpAddresse []PublicIpAddresseType + } + + ZoneId string } type DescribeBandwidthPackagesResponse struct { @@ -153,12 +167,14 @@ type DescribeBandwidthPackagesResponse struct { } } -func DescribeBandwidthPackages(client *ecs.Client, args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) { +func (client *Client) DescribeBandwidthPackages(args *DescribeBandwidthPackagesArgs) ([]DescribeBandwidthPackageType, error) { response := &DescribeBandwidthPackagesResponse{} + err := client.Invoke("DescribeBandwidthPackages", args, response) if err != nil { return nil, err } + return response.BandwidthPackages.BandwidthPackage, err } @@ -171,20 +187,12 @@ type DeleteBandwidthPackageResponse struct { common.Response } -func DeleteBandwidthPackage(client *ecs.Client, args *DeleteBandwidthPackageArgs) error { +func (client *Client) DeleteBandwidthPackage(args *DeleteBandwidthPackageArgs) error { response := DeleteBandwidthPackageResponse{} err := client.Invoke("DeleteBandwidthPackage", args, &response) return err } -type DescribeSnatTableEntriesArgs struct { - RegionId common.Region -} - -func DescribeSnatTableEntries(client *ecs.Client, args *DescribeSnatTableEntriesArgs) { - -} - type NatGatewaySpec string const ( diff --git a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go index ef057393f..eaec701de 100644 --- a/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go +++ b/vendor/github.com/denverdino/aliyungo/ecs/security_groups.go @@ -33,6 +33,7 @@ type DescribeSecurityGroupAttributeArgs struct { SecurityGroupId string RegionId common.Region NicType NicType //enum for internet (default) |intranet + Direction string // enum ingress egress } // diff --git a/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go b/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go new file mode 100644 index 000000000..aa75574c3 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/ecs/snat_entry.go @@ -0,0 +1,95 @@ +package ecs + +import "github.com/denverdino/aliyungo/common" + +type CreateSnatEntryArgs struct { + RegionId common.Region + SnatTableId string + SourceVSwitchId string + SnatIp string +} + +type CreateSnatEntryResponse struct { + common.Response + SnatEntryId string +} + +type SnatEntrySetType struct { + RegionId common.Region + SnatEntryId string + SnatIp string + SnatTableId string + SourceCIDR string + SourceVSwitchId string + Status string +} + +type DescribeSnatTableEntriesArgs struct { + RegionId common.Region + SnatTableId string + common.Pagination +} + +type DescribeSnatTableEntriesResponse struct { + common.Response + common.PaginationResult + SnatTableEntries struct { + SnatTableEntry []SnatEntrySetType + } +} + +type ModifySnatEntryArgs struct { + RegionId common.Region + SnatTableId string + SnatEntryId string + SnatIp string +} + +type ModifySnatEntryResponse struct { + common.Response +} + +type DeleteSnatEntryArgs struct { + RegionId common.Region + SnatTableId string + SnatEntryId string +} + +type DeleteSnatEntryResponse struct { + common.Response +} + +func (client *Client) CreateSnatEntry(args *CreateSnatEntryArgs) (resp *CreateSnatEntryResponse, err error) { + response := CreateSnatEntryResponse{} + err = client.Invoke("CreateSnatEntry", args, &response) + if err != nil { + return nil, err + } + return &response, err +} + +func (client *Client) DescribeSnatTableEntries(args *DescribeSnatTableEntriesArgs) (snatTableEntries []SnatEntrySetType, + pagination *common.PaginationResult, err error) { + + args.Validate() + response := DescribeSnatTableEntriesResponse{} + + err = client.Invoke("DescribeSnatTableEntries", args, &response) + + if err != nil { + return nil, nil, err + } + + return response.SnatTableEntries.SnatTableEntry, &response.PaginationResult, nil +} + +func (client *Client) ModifySnatEntry(args *ModifySnatEntryArgs) error { + response := ModifySnatEntryResponse{} + return client.Invoke("ModifySnatEntry", args, &response) +} + +func (client *Client) DeleteSnatEntry(args *DeleteSnatEntryArgs) error { + response := DeleteSnatEntryResponse{} + err := client.Invoke("DeleteSnatEntry", args, &response) + return err +} diff --git a/vendor/github.com/denverdino/aliyungo/rds/client.go b/vendor/github.com/denverdino/aliyungo/rds/client.go new file mode 100644 index 000000000..3701e0aa2 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/rds/client.go @@ -0,0 +1,48 @@ +package rds + +import ( + "github.com/denverdino/aliyungo/common" + + "os" +) + +type Client struct { + common.Client +} + +const ( + // ECSDefaultEndpoint is the default API endpoint of RDS services + RDSDefaultEndpoint = "https://rds.aliyuncs.com" + RDSAPIVersion = "2014-08-15" + RDSServiceCode = "rds" +) + +// NewClient creates a new instance of RDS client +func NewClient(accessKeyId, accessKeySecret string) *Client { + endpoint := os.Getenv("RDS_ENDPOINT") + if endpoint == "" { + endpoint = RDSDefaultEndpoint + } + return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret) +} + +func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client { + client := &Client{} + client.Init(endpoint, RDSAPIVersion, accessKeyId, accessKeySecret) + return client +} + +func NewRDSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + endpoint := os.Getenv("RDS_ENDPOINT") + if endpoint == "" { + endpoint = RDSDefaultEndpoint + } + + return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) +} + +func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, RDSAPIVersion, accessKeyId, accessKeySecret, RDSServiceCode, regionID) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/rds/instances.go b/vendor/github.com/denverdino/aliyungo/rds/instances.go new file mode 100644 index 000000000..8204ddf17 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/rds/instances.go @@ -0,0 +1,843 @@ +package rds + +import ( + "github.com/denverdino/aliyungo/common" + "time" +) + +type DBInstanceIPArray struct { + SecurityIps string + DBInstanceIPArrayName string + DBInstanceIPArrayAttribute string +} + +// ref: https://help.aliyun.com/document_detail/26242.html +type ModifySecurityIpsArgs struct { + DBInstanceId string + DBInstanceIPArray +} + +func (client *Client) ModifySecurityIps(args *ModifySecurityIpsArgs) (resp common.Response, err error) { + response := common.Response{} + err = client.Invoke("ModifySecurityIps", args, &response) + return response, err +} + +type DescribeDBInstanceIPsArgs struct { + DBInstanceId string +} + +type DBInstanceIPList struct { + DBInstanceIPArrayName string + DBInstanceIPArrayAttribute string + SecurityIPList string +} + +type DescribeDBInstanceIPsResponse struct { + common.Response + Items struct { + DBInstanceIPArray []DBInstanceIPList + } +} + +// DescribeDBInstanceIPArrayList describe security ips +// +// You can read doc at https://help.aliyun.com/document_detail/26241.html?spm=5176.doc26242.6.715.d9pxvr +func (client *Client) DescribeDBInstanceIPs(args *DescribeDBInstanceIPsArgs) (resp *DescribeDBInstanceIPsResponse, err error) { + response := DescribeDBInstanceIPsResponse{} + err = client.Invoke("DescribeDBInstanceIPArrayList", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +// InstanceStatus represents instance status +type InstanceStatus string + +// Constants of InstanceStatus +const ( + Creating = InstanceStatus("Creating") // For backward compatability + Running = InstanceStatus("Running") + Deleting = InstanceStatus("Deleting") + Rebooting = InstanceStatus("Rebooting") + + Restoring = InstanceStatus("Restoring") + Importing = InstanceStatus("Importing") + DBInstanceNetTypeChanging = InstanceStatus("DBInstanceNetTypeChanging") +) + +type DBPayType string + +const ( + Prepaid = DBPayType("Prepaid") + Postpaid = DBPayType("Postpaid") +) + +type CommodityCode string + +const ( + Rds = CommodityCode("rds") + Bards = CommodityCode("bards") + Rords = CommodityCode("rords") +) + +type Engine string + +const ( + MySQL = Engine("MySQL") + SQLServer = Engine("SQLServer") + PPAS = Engine("PPAS") + PG = Engine("PG") +) + +type ConnectionMode string + +const ( + Performance = ConnectionMode("Performance") + Safty = ConnectionMode("Safty") +) + +// default resource value for create order +const DefaultResource = "buy" + +type CreateOrderArgs struct { + CommodityCode CommodityCode + RegionId common.Region + ZoneId string + Engine Engine + EngineVersion string + PayType DBPayType + DBInstanceClass string + DBInstanceStorage int + DBInstanceNetType common.NetType + InstanceNetworkType common.NetworkType + VPCId string + VSwitchId string + UsedTime int + TimeType common.TimeType + Quantity int + InstanceUsedType string + Resource string + AutoPay string + AutoRenew string + BackupId string + RestoreTime string + SecurityIPList string + BusinessInfo string +} + +type CreateOrderResponse struct { + common.Response + DBInstanceId string + OrderId int +} + +// CreateOrder create db instance order +// you can read doc at http://docs.alibaba-inc.com/pages/viewpage.action?pageId=259349053 +func (client *Client) CreateOrder(args *CreateOrderArgs) (resp CreateOrderResponse, err error) { + response := CreateOrderResponse{} + err = client.Invoke("CreateOrder", args, &response) + return response, err +} + +type DescribeDBInstancesArgs struct { + DBInstanceId string +} + +type DescribeDBInstanceAttributeResponse struct { + common.Response + Items struct { + DBInstanceAttribute []DBInstanceAttribute + } +} + +type DBInstanceAttribute struct { + DBInstanceId string + PayType DBPayType + DBInstanceType string + InstanceNetworkType string + ConnectionMode string + RegionId string + ZoneId string + ConnectionString string + Port string + Engine Engine + EngineVersion string + DBInstanceClass string + DBInstanceMemory int64 + DBInstanceStorage int + DBInstanceNetType string + DBInstanceStatus InstanceStatus + DBInstanceDescription string + LockMode string + LockReason string + DBMaxQuantity int + AccountMaxQuantity int + CreationTime string + ExpireTime string + MaintainTime string + AvailabilityValue string + MaxIOPS int + MaxConnections int + MasterInstanceId string + IncrementSourceDBInstanceId string + GuardDBInstanceId string + TempDBInstanceId string + ReadOnlyDBInstanceIds ReadOnlyDBInstanceIds + SecurityIPList string +} + +type ReadOnlyDBInstanceIds struct { + ReadOnlyDBInstanceId []string +} + +// DescribeDBInstanceAttribute describes db instance +// +// You can read doc at https://help.aliyun.com/document_detail/26231.html?spm=5176.doc26228.6.702.uhzm31 +func (client *Client) DescribeDBInstanceAttribute(args *DescribeDBInstancesArgs) (resp *DescribeDBInstanceAttributeResponse, err error) { + + response := DescribeDBInstanceAttributeResponse{} + + err = client.Invoke("DescribeDBInstanceAttribute", args, &response) + + if err == nil { + return &response, nil + } + + return nil, err +} + +type DescribeDatabasesArgs struct { + DBInstanceId string + DBName string + DBStatus InstanceStatus +} + +type DescribeDatabasesResponse struct { + common.Response + Databases struct { + Database []Database + } +} + +type Database struct { + DBName string + DBInstanceId string + Engine string + DBStatus InstanceStatus + CharacterSetName InstanceStatus + DBDescription InstanceStatus + Account InstanceStatus + AccountPrivilege InstanceStatus + Accounts struct { + AccountPrivilegeInfo []AccountPrivilegeInfo + } +} + +type AccountPrivilegeInfo struct { + Account string + AccountPrivilege string +} + +// DescribeDatabases describes db database +// +// You can read doc at https://help.aliyun.com/document_detail/26260.html?spm=5176.doc26258.6.732.gCx1a3 +func (client *Client) DescribeDatabases(args *DescribeDatabasesArgs) (resp *DescribeDatabasesResponse, err error) { + + response := DescribeDatabasesResponse{} + + err = client.Invoke("DescribeDatabases", args, &response) + + if err == nil { + return &response, nil + } + + return nil, err +} + +type DescribeAccountsArgs struct { + DBInstanceId string + AccountName string +} + +type DescribeAccountsResponse struct { + common.Response + Accounts struct { + DBInstanceAccount []DBInstanceAccount + } +} + +type DBInstanceAccount struct { + DBInstanceId string + AccountName string + AccountStatus AccountStatus + AccountDescription string + DatabasePrivileges struct { + DatabasePrivilege []DatabasePrivilege + } +} + +type AccountStatus string + +const ( + Unavailable = AccountStatus("Unavailable") + Available = AccountStatus("Available") +) + +type DatabasePrivilege struct { + DBName string + AccountPrivilege AccountPrivilege +} + +// DescribeAccounts describes db accounts +// +// You can read doc at https://help.aliyun.com/document_detail/26265.html?spm=5176.doc26266.6.739.UjtjaI +func (client *Client) DescribeAccounts(args *DescribeAccountsArgs) (resp *DescribeAccountsResponse, err error) { + + response := DescribeAccountsResponse{} + + err = client.Invoke("DescribeAccounts", args, &response) + + if err == nil { + return &response, nil + } + + return nil, err +} + +// Default timeout value for WaitForInstance method +const InstanceDefaultTimeout = 120 +const DefaultWaitForInterval = 10 + +// WaitForInstance waits for instance to given status +func (client *Client) WaitForInstance(instanceId string, status InstanceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + args := DescribeDBInstancesArgs{ + DBInstanceId: instanceId, + } + + resp, err := client.DescribeDBInstanceAttribute(&args) + if err != nil { + return err + } + + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + + timeout = timeout - DefaultWaitForInterval + time.Sleep(DefaultWaitForInterval * time.Second) + + if len(resp.Items.DBInstanceAttribute) < 1 { + continue + } + instance := resp.Items.DBInstanceAttribute[0] + if instance.DBInstanceStatus == status { + break + } + + } + return nil +} + +func (client *Client) WaitForAllDatabase(instanceId string, databaseNames []string, status InstanceStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + args := DescribeDatabasesArgs{ + DBInstanceId: instanceId, + } + + resp, err := client.DescribeDatabases(&args) + if err != nil { + return err + } + + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + + timeout = timeout - DefaultWaitForInterval + time.Sleep(DefaultWaitForInterval * time.Second) + + ready := 0 + + for _, nm := range databaseNames { + for _, db := range resp.Databases.Database { + if db.DBName == nm { + if db.DBStatus == status { + ready++ + break + } + } + } + } + + if ready == len(databaseNames) { + break + } + + } + return nil +} + +func (client *Client) WaitForAccount(instanceId string, accountName string, status AccountStatus, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + args := DescribeAccountsArgs{ + DBInstanceId: instanceId, + AccountName: accountName, + } + + resp, err := client.DescribeAccounts(&args) + if err != nil { + return err + } + + accs := resp.Accounts.DBInstanceAccount + + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + + timeout = timeout - DefaultWaitForInterval + time.Sleep(DefaultWaitForInterval * time.Second) + + if len(accs) < 1 { + continue + } + + acc := accs[0] + + if acc.AccountStatus == status { + break + } + + } + return nil +} + +func (client *Client) WaitForPublicConnection(instanceId string, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + args := DescribeDBInstanceNetInfoArgs{ + DBInstanceId: instanceId, + } + + resp, err := client.DescribeDBInstanceNetInfo(&args) + if err != nil { + return err + } + + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + + timeout = timeout - DefaultWaitForInterval + time.Sleep(DefaultWaitForInterval * time.Second) + + ready := false + for _, info := range resp.DBInstanceNetInfos.DBInstanceNetInfo { + if info.IPType == Public { + ready = true + } + } + + if ready { + break + } + + } + return nil +} + +func (client *Client) WaitForAccountPrivilege(instanceId, accountName, dbName string, privilege AccountPrivilege, timeout int) error { + if timeout <= 0 { + timeout = InstanceDefaultTimeout + } + for { + args := DescribeAccountsArgs{ + DBInstanceId: instanceId, + AccountName: accountName, + } + + resp, err := client.DescribeAccounts(&args) + if err != nil { + return err + } + + accs := resp.Accounts.DBInstanceAccount + + if timeout <= 0 { + return common.GetClientErrorFromString("Timeout") + } + + timeout = timeout - DefaultWaitForInterval + time.Sleep(DefaultWaitForInterval * time.Second) + + if len(accs) < 1 { + continue + } + + acc := accs[0] + + ready := false + for _, dp := range acc.DatabasePrivileges.DatabasePrivilege { + if dp.DBName == dbName && dp.AccountPrivilege == privilege { + ready = true + } + } + + if ready { + break + } + + } + return nil +} + +type DeleteDBInstanceArgs struct { + DBInstanceId string +} + +type DeleteDBInstanceResponse struct { + common.Response +} + +// DeleteInstance deletes db instance +// +// You can read doc at https://help.aliyun.com/document_detail/26229.html?spm=5176.doc26315.6.700.7SmyAT +func (client *Client) DeleteInstance(instanceId string) error { + args := DeleteDBInstanceArgs{DBInstanceId: instanceId} + response := DeleteDBInstanceResponse{} + err := client.Invoke("DeleteDBInstance", &args, &response) + return err +} + +type DeleteDatabaseArgs struct { + DBInstanceId string + DBName string +} + +type DeleteDatabaseResponse struct { + common.Response +} + +// DeleteInstance deletes database +// +// You can read doc at https://help.aliyun.com/document_detail/26259.html?spm=5176.doc26260.6.731.Abjwne +func (client *Client) DeleteDatabase(instanceId, dbName string) error { + args := DeleteDatabaseArgs{ + DBInstanceId: instanceId, + DBName: dbName, + } + response := DeleteDatabaseResponse{} + err := client.Invoke("DeleteDatabase", &args, &response) + return err +} + +type DescribeRegionsArgs struct { +} + +type DescribeRegionsResponse struct { + Regions struct { + RDSRegion []RDSRegion + } +} + +type RDSRegion struct { + RegionId string + ZoneId string +} + +// DescribeRegions describe rds regions +// +// You can read doc at https://help.aliyun.com/document_detail/26243.html?spm=5176.doc26244.6.715.OSNUa8 +func (client *Client) DescribeRegions() (resp *DescribeRegionsResponse, err error) { + args := DescribeRegionsArgs{} + response := DescribeRegionsResponse{} + err = client.Invoke("DescribeRegions", &args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type CreateDatabaseResponse struct { + common.Response +} + +type CreateDatabaseArgs struct { + DBInstanceId string + DBName string + CharacterSetName string + DBDescription string +} + +// CreateDatabase create rds database +// +// You can read doc at https://help.aliyun.com/document_detail/26243.html?spm=5176.doc26244.6.715.OSNUa8 +func (client *Client) CreateDatabase(args *CreateDatabaseArgs) (resp *CreateDatabaseResponse, err error) { + response := CreateDatabaseResponse{} + err = client.Invoke("CreateDatabase", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type CreateAccountResponse struct { + common.Response +} + +type AccountType struct { + Normal string + Super string +} + +type CreateAccountArgs struct { + DBInstanceId string + AccountName string + AccountPassword string + AccountType AccountType + AccountDescription string +} + +// CreateAccount create rds account +// +// You can read doc at https://help.aliyun.com/document_detail/26263.html?spm=5176.doc26240.6.736.ZDihok +func (client *Client) CreateAccount(args *CreateAccountArgs) (resp *CreateAccountResponse, err error) { + response := CreateAccountResponse{} + err = client.Invoke("CreateAccount", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type DeleteAccountResponse struct { + common.Response +} + +type DeleteAccountArgs struct { + DBInstanceId string + AccountName string +} + +// DeleteAccount delete account +// +// You can read doc at https://help.aliyun.com/document_detail/26264.html?spm=5176.doc26269.6.737.CvlZp6 +func (client *Client) DeleteAccount(instanceId, accountName string) (resp *DeleteAccountResponse, err error) { + args := DeleteAccountArgs{ + DBInstanceId: instanceId, + AccountName: accountName, + } + + response := DeleteAccountResponse{} + err = client.Invoke("DeleteAccount", &args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type GrantAccountPrivilegeResponse struct { + common.Response +} + +type GrantAccountPrivilegeArgs struct { + DBInstanceId string + AccountName string + DBName string + AccountPrivilege AccountPrivilege +} + +type AccountPrivilege string + +const ( + ReadOnly = AccountPrivilege("ReadOnly") + ReadWrite = AccountPrivilege("ReadWrite") +) + +// GrantAccountPrivilege grant database privilege to account +// +// You can read doc at https://help.aliyun.com/document_detail/26266.html?spm=5176.doc26264.6.739.o2y01n +func (client *Client) GrantAccountPrivilege(args *GrantAccountPrivilegeArgs) (resp *GrantAccountPrivilegeResponse, err error) { + response := GrantAccountPrivilegeResponse{} + err = client.Invoke("GrantAccountPrivilege", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type AllocateInstancePublicConnectionResponse struct { + common.Response +} + +type AllocateInstancePublicConnectionArgs struct { + DBInstanceId string + ConnectionStringPrefix string + Port string +} + +// AllocateInstancePublicConnection allocate public connection +// +// You can read doc at https://help.aliyun.com/document_detail/26234.html?spm=5176.doc26265.6.708.PdsJnL +func (client *Client) AllocateInstancePublicConnection(args *AllocateInstancePublicConnectionArgs) (resp *AllocateInstancePublicConnectionResponse, err error) { + response := AllocateInstancePublicConnectionResponse{} + err = client.Invoke("AllocateInstancePublicConnection", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type DescribeDBInstanceNetInfoArgs struct { + DBInstanceId string +} + +type DescribeDBInstanceNetInfoResponse struct { + common.Response + InstanceNetworkType string + DBInstanceNetInfos struct { + DBInstanceNetInfo []DBInstanceNetInfo + } +} + +type DBInstanceNetInfo struct { + ConnectionString string + IPAddress string + Port string + VPCId string + VSwitchId string + IPType IPType +} + +type IPType string + +const ( + Inner = IPType("Inner") + Private = IPType("Private") + Public = IPType("Public") +) + +// DescribeDBInstanceNetInfo describe rds net info +// +// You can read doc at https://help.aliyun.com/document_detail/26237.html?spm=5176.doc26234.6.711.vHOktx +func (client *Client) DescribeDBInstanceNetInfo(args *DescribeDBInstanceNetInfoArgs) (resp *DescribeDBInstanceNetInfoResponse, err error) { + response := DescribeDBInstanceNetInfoResponse{} + err = client.Invoke("DescribeDBInstanceNetInfo", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type BackupPolicy struct { + PreferredBackupTime string // HH:mmZ - HH:mm Z + PreferredBackupPeriod string // Monday - Sunday + BackupRetentionPeriod int // 7 - 730 + BackupLog string // enum Enable | Disabled + LogBackupRetentionPeriod string +} + +type ModifyBackupPolicyArgs struct { + DBInstanceId string + BackupPolicy +} + +type ModifyBackupPolicyResponse struct { + common.Response +} + +// ModifyBackupPolicy modify backup policy +// +// You can read doc at https://help.aliyun.com/document_detail/26276.html?spm=5176.doc26250.6.751.KOew21 +func (client *Client) ModifyBackupPolicy(args *ModifyBackupPolicyArgs) (resp *ModifyBackupPolicyResponse, err error) { + response := ModifyBackupPolicyResponse{} + err = client.Invoke("ModifyBackupPolicy", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type DescribeBackupPolicyArgs struct { + DBInstanceId string +} + +type DescribeBackupPolicyResponse struct { + common.Response + BackupPolicy +} + +// DescribeBackupPolicy describe backup policy +// +// You can read doc at https://help.aliyun.com/document_detail/26275.html?spm=5176.doc26276.6.750.CUqjDn +func (client *Client) DescribeBackupPolicy(args *DescribeBackupPolicyArgs) (resp *DescribeBackupPolicyResponse, err error) { + response := DescribeBackupPolicyResponse{} + err = client.Invoke("DescribeBackupPolicy", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +type ModifyDBInstanceSpecArgs struct { + DBInstanceId string + PayType DBPayType + DBInstanceClass string + DBInstanceStorage string +} + +type ModifyDBInstanceSpecResponse struct { + common.Response +} + +// ModifyDBInstanceSpec modify db instance spec +// +// You can read doc at https://help.aliyun.com/document_detail/26233.html?spm=5176.doc26258.6.707.2QOLrM +func (client *Client) ModifyDBInstanceSpec(args *ModifyDBInstanceSpecArgs) (resp *ModifyDBInstanceSpecResponse, err error) { + response := ModifyDBInstanceSpecResponse{} + err = client.Invoke("ModifyDBInstanceSpec", args, &response) + + if err != nil { + return nil, err + } + return &response, nil +} + +var WEEK_ENUM = []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} + +var BACKUP_TIME = []string{ + "00:00Z-01:00Z", "01:00Z-02:00Z", "02:00Z-03:00Z", "03:00Z-04:00Z", "04:00Z-05:00Z", + "05:00Z-06:00Z", "06:00Z-07:00Z", "07:00Z-08:00Z", "08:00Z-09:00Z", "09:00Z-10:00Z", + "10:00Z-11:00Z", "11:00Z-12:00Z", "12:00Z-13:00Z", "13:00Z-14:00Z", "14:00Z-15:00Z", + "15:00Z-16:00Z", "16:00Z-17:00Z", "17:00Z-18:00Z", "18:00Z-19:00Z", "19:00Z-20:00Z", + "20:00Z-21:00Z", "21:00Z-22:00Z", "22:00Z-23:00Z", "23:00Z-24:00Z", +} + +var CHARACTER_SET_NAME = []string{ + "utf8", "gbk", "latin1", "utf8mb4", + "Chinese_PRC_CI_AS", "Chinese_PRC_CS_AS", "SQL_Latin1_General_CP1_CI_AS", "SQL_Latin1_General_CP1_CS_AS", "Chinese_PRC_BIN", +} diff --git a/vendor/github.com/denverdino/aliyungo/rds/monitoring.go b/vendor/github.com/denverdino/aliyungo/rds/monitoring.go new file mode 100644 index 000000000..70a7eb6ff --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/rds/monitoring.go @@ -0,0 +1,50 @@ +package rds + +import ( + "github.com/denverdino/aliyungo/common" + "github.com/denverdino/aliyungo/util" +) + +type DescribeDBInstancePerformanceArgs struct { + DBInstanceId string + key string + StartTime string + EndTime string +} + +type PerformanceValueType struct { + Value string + Date util.ISO6801Time +} + +type PerformanceKeyType struct { + Key string + Unit string + ValueFormat string + Values struct { + PerformanceValue []PerformanceValueType + } +} + +type DescribeDBInstancePerformanceResponse struct { + common.Response + DBInstanceId string + Engine string + StartTime util.ISO6801Time + EndTime util.ISO6801Time + PerformanceKeys struct { + PerformanceKey []PerformanceKeyType + } +} + +func (client *DescribeDBInstancePerformanceArgs) Setkey(key string) { + client.key = key +} + +func (client *Client) DescribeDBInstancePerformance(args *DescribeDBInstancePerformanceArgs) (resp DescribeDBInstancePerformanceResponse, err error) { + + response := DescribeDBInstancePerformanceResponse{} + err = client.Invoke("DescribeDBInstancePerformance", args, &response) + return response, err + +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/client.go b/vendor/github.com/denverdino/aliyungo/slb/client.go index 8dd26b691..0f9b705e4 100644 --- a/vendor/github.com/denverdino/aliyungo/slb/client.go +++ b/vendor/github.com/denverdino/aliyungo/slb/client.go @@ -1,8 +1,9 @@ package slb import ( - "github.com/denverdino/aliyungo/common" "os" + + "github.com/denverdino/aliyungo/common" ) type Client struct { @@ -13,6 +14,8 @@ const ( // SLBDefaultEndpoint is the default API endpoint of SLB services SLBDefaultEndpoint = "https://slb.aliyuncs.com" SLBAPIVersion = "2014-05-15" + + SLBServiceCode = "slb" ) // NewClient creates a new instance of ECS client @@ -29,3 +32,18 @@ func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) client.Init(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret) return client } + +func NewSLBClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client { + endpoint := os.Getenv("SLB_ENDPOINT") + if endpoint == "" { + endpoint = SLBDefaultEndpoint + } + + return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID) +} + +func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client { + client := &Client{} + client.NewInit(endpoint, SLBAPIVersion, accessKeyId, accessKeySecret, SLBServiceCode, regionID) + return client +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/listeners.go b/vendor/github.com/denverdino/aliyungo/slb/listeners.go index d43557619..50f90f7f2 100644 --- a/vendor/github.com/denverdino/aliyungo/slb/listeners.go +++ b/vendor/github.com/denverdino/aliyungo/slb/listeners.go @@ -138,22 +138,22 @@ const ( ) type TCPListenerType struct { - LoadBalancerId string - ListenerPort int - BackendServerPort int - Bandwidth int - Scheduler SchedulerType - PersistenceTimeout int - HealthCheckType HealthCheckType - HealthCheckDomain string - HealthCheckURI string - HealthCheckConnectPort int - HealthyThreshold int - UnhealthyThreshold int - HealthCheckTimeout int - HealthCheckInterval int - HealthCheckHttpCode HealthCheckHttpCodeType - VServerGroupId string + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckType HealthCheckType + HealthCheckDomain string + HealthCheckURI string + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckConnectTimeout int + HealthCheckInterval int + HealthCheckHttpCode HealthCheckHttpCodeType + VServerGroupId string } type CreateLoadBalancerTCPListenerArgs TCPListenerType @@ -168,18 +168,18 @@ func (client *Client) CreateLoadBalancerTCPListener(args *CreateLoadBalancerTCPL } type UDPListenerType struct { - LoadBalancerId string - ListenerPort int - BackendServerPort int - Bandwidth int - Scheduler SchedulerType - PersistenceTimeout int - HealthCheckConnectPort int - HealthyThreshold int - UnhealthyThreshold int - HealthCheckTimeout int - HealthCheckInterval int - VServerGroupId string + LoadBalancerId string + ListenerPort int + BackendServerPort int + Bandwidth int + Scheduler SchedulerType + PersistenceTimeout int + HealthCheckConnectPort int + HealthyThreshold int + UnhealthyThreshold int + HealthCheckConnectTimeout int + HealthCheckInterval int + VServerGroupId string } type CreateLoadBalancerUDPListenerArgs UDPListenerType diff --git a/vendor/github.com/denverdino/aliyungo/slb/rules.go b/vendor/github.com/denverdino/aliyungo/slb/rules.go new file mode 100644 index 000000000..94eb402b6 --- /dev/null +++ b/vendor/github.com/denverdino/aliyungo/slb/rules.go @@ -0,0 +1,126 @@ +package slb + +import "github.com/denverdino/aliyungo/common" + +type CreateRulesResponse struct { + common.Response +} + +type CreateRulesArgs struct { + RegionId common.Region + LoadBalancerId string + ListenerPort int + RuleList string +} + +type Rule struct { + RuleId string + RuleName string + Domain string + Url string `json:",omitempty"` + VServerGroupId string +} + +// Create forward rules +// +// You can read doc at https://help.aliyun.com/document_detail/35226.html?spm=5176.doc35226.6.671.625Omh +func (client *Client) CreateRules(args *CreateRulesArgs) error { + response := CreateRulesResponse{} + err := client.Invoke("CreateRules", args, &response) + if err != nil { + return err + } + return err +} + +type DeleteRulesArgs struct { + RegionId common.Region + RuleIds string +} + +type DeleteRulesResponse struct { + common.Response +} + +// Delete forward rules +// +// You can read doc at https://help.aliyun.com/document_detail/35227.html?spm=5176.doc35226.6.672.6iNBtR +func (client *Client) DeleteRules(args *DeleteRulesArgs) error { + response := DeleteRulesResponse{} + err := client.Invoke("DeleteRules", args, &response) + if err != nil { + return err + } + return err +} + +type SetRuleArgs struct { + RegionId common.Region + RuleId string + VServerGroupId string +} + +type SetRuleResponse struct { + common.Response +} + +// Modify forward rules +// +// You can read doc at https://help.aliyun.com/document_detail/35228.html?spm=5176.doc35227.6.673.rq40a9 +func (client *Client) SetRule(args *SetRuleArgs) error { + response := SetRuleResponse{} + err := client.Invoke("SetRule", args, &response) + if err != nil { + return err + } + return err +} + +type DescribeRuleAttributeArgs struct { + RegionId common.Region + RuleId string +} + +type DescribeRuleAttributeResponse struct { + common.Response + LoadBalancerId string + ListenerPort int + Rule +} + +// Describe rule +// +// You can read doc at https://help.aliyun.com/document_detail/35229.html?spm=5176.doc35226.6.674.DRJeKJ +func (client *Client) DescribeRuleAttribute(args *DescribeRuleAttributeArgs) (*DescribeRuleAttributeResponse, error) { + response := &DescribeRuleAttributeResponse{} + err := client.Invoke("DescribeRuleAttribute", args, response) + if err != nil { + return nil, err + } + return response, nil +} + +type DescribeRulesArgs struct { + RegionId common.Region + LoadBalancerId string + ListenerPort int +} + +type DescribeRulesResponse struct { + common.Response + Rules struct { + Rule []Rule + } +} + +// Describe rule +// +// You can read doc at https://help.aliyun.com/document_detail/35229.html?spm=5176.doc35226.6.674.DRJeKJ +func (client *Client) DescribeRules(args *DescribeRulesArgs) (*DescribeRulesResponse, error) { + response := &DescribeRulesResponse{} + err := client.Invoke("DescribeRules", args, response) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/vendor/github.com/denverdino/aliyungo/slb/servers.go b/vendor/github.com/denverdino/aliyungo/slb/servers.go index a3fb2a406..18be45891 100644 --- a/vendor/github.com/denverdino/aliyungo/slb/servers.go +++ b/vendor/github.com/denverdino/aliyungo/slb/servers.go @@ -23,7 +23,6 @@ type AddBackendServersResponse struct { type SetBackendServersResponse AddBackendServersResponse - // SetBackendServers set weight of backend servers func (client *Client) SetBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) { @@ -42,7 +41,6 @@ func (client *Client) SetBackendServers(loadBalancerId string, backendServers [] return response.BackendServers.BackendServer, err } - // AddBackendServers Add backend servers // // You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&AddBackendServers diff --git a/vendor/vendor.json b/vendor/vendor.json index bee755880..3d129580d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1292,28 +1292,34 @@ "revisionTime": "2016-10-29T20:57:26Z" }, { - "checksumSHA1": "ADySw3nBHyzEHB6afBSeVRN2A4g=", + "checksumSHA1": "SdiAYZOqWQ60ifRUHLwLiDMKMYA=", "path": "github.com/denverdino/aliyungo/common", - "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", - "revisionTime": "2017-01-17T10:57:15Z" + "revision": "c4c75afbf7ea86e66672c1b6ed981385b4ad5ec2", + "revisionTime": "2017-03-21T07:55:32Z" }, { - "checksumSHA1": "9ZY3RlumKp5DAMfL08YwMoOOT2o=", + "checksumSHA1": "UVYu5rvfoXgJnIpUyGcaovMvpms=", "path": "github.com/denverdino/aliyungo/ecs", - "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", - "revisionTime": "2017-01-17T10:57:15Z" + "revision": "c4c75afbf7ea86e66672c1b6ed981385b4ad5ec2", + "revisionTime": "2017-03-21T07:55:32Z" }, { - "checksumSHA1": "QlA7zv05k7HWeR3tg4uHqIlFcg8=", + "checksumSHA1": "riQMe2AR7qkLRkQ/MSr8gQp3zL4=", + "path": "github.com/denverdino/aliyungo/rds", + "revision": "c4c75afbf7ea86e66672c1b6ed981385b4ad5ec2", + "revisionTime": "2017-03-21T07:55:32Z" + }, + { + "checksumSHA1": "2g6VZONB51rul5YuSBvngH6u4A0=", "path": "github.com/denverdino/aliyungo/slb", - "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", - "revisionTime": "2017-01-17T10:57:15Z" + "revision": "c4c75afbf7ea86e66672c1b6ed981385b4ad5ec2", + "revisionTime": "2017-03-21T07:55:32Z" }, { "checksumSHA1": "Lp0KtT7ycgq31ox3Uzhpxyw0U+Y=", "path": "github.com/denverdino/aliyungo/util", - "revision": "d123f5d1fa71b211b70b2e9b56a62da21076884a", - "revisionTime": "2017-01-17T10:57:15Z" + "revision": "c4c75afbf7ea86e66672c1b6ed981385b4ad5ec2", + "revisionTime": "2017-03-21T07:55:32Z" }, { "checksumSHA1": "yDQQpeUxwqB3C+4opweg6znWJQk=", diff --git a/website/source/docs/providers/alicloud/r/db_instance.html.markdown b/website/source/docs/providers/alicloud/r/db_instance.html.markdown new file mode 100644 index 000000000..7580f61e6 --- /dev/null +++ b/website/source/docs/providers/alicloud/r/db_instance.html.markdown @@ -0,0 +1,106 @@ +--- +layout: "alicloud" +page_title: "Alicloud: alicloud_db_instance" +sidebar_current: "docs-alicloud-resource-db-instance" +description: |- + Provides an RDS instance resource. +--- + +# alicloud\_db\_instance + +Provides an RDS instance resource. A DB instance is an isolated database +environment in the cloud. A DB instance can contain multiple user-created +databases. + +## Example Usage + +``` +resource "alicloud_db_instance" "default" { + commodity_code = "rds" + engine = "MySQL" + engine_version = "5.6" + db_instance_class = "rds.mysql.t1.small" + db_instance_storage = "10" + db_instance_net_type = "Intranet" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `engine` - (Required) Database type. Value options: MySQL, SQLServer, PostgreSQL, and PPAS. +* `engine_version` - (Required) Database version. Value options: + - 5.5/5.6/5.7 for MySQL + - 2008r2/2012 for SQLServer + - 9.4 for PostgreSQL + - 9.3 for PPAS +* `db_instance_class` - (Required) Instance type. For details, see [Instance type table](https://intl.aliyun.com/help/doc-detail/26312.htm?spm=a3c0i.o26228en.a3.2.bRUHF3). +* `db_instance_storage` - (Required) User-defined storage space. Value range: + - [5, 2000] for MySQL/PostgreSQL/PPAS HA dual node edition; + - [20,1000] for MySQL 5.7 basic single node edition; + - [10, 2000] for SQL Server 2008R2; + - [20,2000] for SQL Server 2012 basic single node edition + Increase progressively at a rate of 5 GB. The unit is GB. For details, see [Instance type table](https://intl.aliyun.com/help/doc-detail/26312.htm?spm=a3c0i.o26228en.a3.3.bRUHF3). +* `instance_charge_type` - (Optional) Valid values are `Prepaid`, `Postpaid`, The default is `Postpaid`. +* `period` - (Optional) The time that you have bought the resource, in month. Only valid when instance_charge_type is set as `PrePaid`. Value range [1, 12]. +* `zone_id` - (Optional) Selected zone to create database instance. You cannot set the ZoneId parameter if the MultiAZ parameter is set to true. +* `multi_az` - (Optional) Specifies if the database instance is a multiple Availability Zone deployment. +* `db_instance_net_type` - (Optional) Network connection type of an instance. Internet: public network; Intranet: private network +* `allocate_public_connection` - (Optional) If set to true will applies for an Internet connection string of an instance. +* `instance_network_type` - (Optional) VPC: VPC instance; Classic: classic instance. If no value is specified, a classic instance will be created by default. +* `vswitch_id` - (Optional) The virtual switch ID to launch in VPC. If you want to create instances in VPC network, this parameter must be set. +* `master_user_name` - (Optional) The master user name for the database instance. Operation account requiring a uniqueness check. It may consist of lower case letters, numbers and underlines, and must start with a letter and have no more than 16 characters. +* `master_user_password` - (Optional) The master password for the database instance. Operation password. It may consist of letters, digits, or underlines, with a length of 6 to 32 characters. +* `preferred_backup_period` - (Optional) Backup period. Values: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, and Sunday. +* `preferred_backup_time` - (Optional) Backup time, in the format ofHH:mmZ- HH:mm Z. +* `backup_retention_period` - (Optional) Retention days of the backup (7 to 730 days). The default value is 7 days. +* `security_ips` - (Optional) List of IP addresses under the IP address white list array. The list contains up to 1,000 IP addresses, separated by commas. Supported formats include 0.0.0.0/0, 10.23.12.24 (IP), and 10.23.12.24/24 (Classless Inter-Domain Routing (CIDR) mode. /24 represents the length of the prefix in an IP address. The range of the prefix length is [1,32]). +* `db_mappings` - (Optional) Database mappings to attach to db instance. See [Block database](#block-database) below for details. + + +## Block database + +The database mapping supports the following: + +* `db_name` - (Required) Name of the database requiring a uniqueness check. It may consist of lower case letters, numbers and underlines, and must start with a letter and have no more than 64 characters. +* `character_set_name` - (Required) Character set. The value range is limited to the following: + - MySQL type: + + utf8 + + gbk + + latin1 + + utf8mb4 (included in versions 5.5 and 5.6). + - SQLServer type: + + Chinese_PRC_CI_AS + + Chinese_PRC_CS_AS + + SQL_Latin1_General_CP1_CI_AS + + SQL_Latin1_General_CP1_CS_AS + + Chinese_PRC_BIN +* `db_description` - (Optional) Database description, which cannot exceed 256 characters. NOTE: It cannot begin with https://. + + +~> **NOTE:** We neither support modify any of database attribute, nor insert/remove item at the same time. +We recommend split to two separate operations. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The RDS instance ID. +* `instance_charge_type` - The instance charge type. +* `period` - The time that you have bought the resource. +* `engine` - Database type. +* `engine_version` - The database engine version. +* `db_instance_class` - The RDS instance class. +* `db_instance_storage` - The amount of allocated storage. +* `port` - The database port. +* `zone_id` - The zone ID of the DB instance. +* `db_instance_net_type` - Network connection type of an instance, `Internet` or `Intranet`. +* `instance_network_type` - The instance network type and it has two values: `vpc` and `classic`. +* `db_mappings` - Database mappings attached to db instance. +* `preferred_backup_period` - Backup period. +* `preferred_backup_time` - Backup time. +* `backup_retention_period` - Retention days of the backup. +* `security_ips` - Security ips of instance whitelist. +* `connections` - Views all the connection information of a specified instance. + diff --git a/website/source/docs/providers/alicloud/r/instance.html.markdown b/website/source/docs/providers/alicloud/r/instance.html.markdown index 2d94fff92..f01b42964 100644 --- a/website/source/docs/providers/alicloud/r/instance.html.markdown +++ b/website/source/docs/providers/alicloud/r/instance.html.markdown @@ -54,22 +54,20 @@ resource "alicloud_slb" "vpc" { The following arguments are supported: -* `availability_zone` - (Required) The Zone to start the instance in. * `image_id` - (Required) The Image to use for the instance. * `instance_type` - (Required) The type of instance to start. -* `security_group_ids` - (Required) A list of security group ids to associate with. If you are creating Instances in a VPC, use `vpc_security_group_ids` instead. -`security_group_ids` instead. -* `instance_name` - (Optional) The name of the ECS. This instance_name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. If not specified, -Terraform will autogenerate a name beginning with `tf-ecs`. +* `io_optimized` - (Required) Valid values are `none`, `optimized`, If `optimized`, the launched ECS instance will be I/O optimized. +* `security_group_ids` - (Optional) A list of security group ids to associate with. +* `availability_zone` - (Optional) The Zone to start the instance in. +* `instance_name` - (Optional) The name of the ECS. This instance_name can have a string of 2 to 128 characters, must contain only alphanumeric characters or hyphens, such as "-",".","_", and must not begin or end with a hyphen, and must not begin with http:// or https://. If not specified, +Terraform will autogenerate a default name is `ECS-Instance`. * `allocate_public_ip` - (Optional) Associate a public ip address with an instance in a VPC or Classic. Boolean value, Default is false. -* `io_optimized` - (Optional) Valid - values are `none`, `optimized`, If `optimized`, the launched ECS instance will be I/O optimized. Default is `optimized`. -* `system_disk_category` - (Optional) Valid values are `cloud`, `cloud_efficiency`, `cloud_ssd`, For I/O optimized instance type, `cloud_ssd` and `cloud_efficiency` disks are supported. For non I/O Optimized instance type, `cloud` disk are supported. +* `system_disk_category` - (Optional) Valid values are `cloud`, `cloud_efficiency`, `cloud_ssd`, For I/O optimized instance type, `cloud_ssd` and `cloud_efficiency` disks are supported. For non I/O Optimized instance type, `cloud` disk are supported. * `system_disk_size` - (Optional) Size of the system disk, value range: 40GB ~ 500GB. Default is 40GB. * `description` - (Optional) Description of the instance, This description can have a string of 2 to 256 characters, It cannot begin with http:// or https://. Default value is null. * `internet_charge_type` - (Optional) Internet charge type of the instance, Valid values are `PayByBandwidth`, `PayByTraffic`. Default is `PayByBandwidth`. * `internet_max_bandwidth_in` - (Optional) Maximum incoming bandwidth from the public network, measured in Mbps (Mega bit per second). Value range: [1, 200]. If this value is not specified, then automatically sets it to 200 Mbps. -* `internet_max_bandwidth_out` - (Optional) Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). Value range: +* `internet_max_bandwidth_out` - (Optional) Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). Value range: `internet_charge_type` is `PayByBandwidth`: this value range [0, 100], If this value is not specified, then automatically sets it to 0 Mbps; If `internet_charge_type` is `PayByTraffic`: this value range [1, 100]. this value must be set value, such as 5. * `host_name` - (Optional) Host name of the ECS, which is a string of at least two characters. “hostname” cannot start or end with “.” or “-“. In addition, two or more consecutive “.” or “-“ symbols are not allowed. On Windows, the host name can contain a maximum of 15 characters, which can be a combination of uppercase/lowercase letters, numerals, and “-“. The host name cannot contain dots (“.”) or contain only numeric characters. On other OSs such as Linux, the host name can contain a maximum of 30 characters, which can be segments separated by dots (“.”), where each segment can contain uppercase/lowercase letters, numerals, or “_“. @@ -77,7 +75,6 @@ On other OSs such as Linux, the host name can contain a maximum of 30 characters * `vswitch_id` - (Optional) The virtual switch ID to launch in VPC. If you want to create instances in VPC network, this parameter must be set. * `instance_charge_type` - (Optional) Valid values are `PrePaid`, `PostPaid`, The default is `PostPaid`. * `period` - (Optional) The time that you have bought the resource, in month. Only valid when instance_charge_type is set as `PrePaid`. Value range [1, 12]. -* `private_ip` - (Optional) Private IP address to associate with the instance in a VPC. * `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference From 17f60ac6821972e8c63882b76dd57815c54ace7d Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 24 Mar 2017 13:06:00 +0200 Subject: [PATCH 04/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f330066..67bc297db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ FEATURES: + * **New Resource:** `alicloud_db_instance` [GH-12913] * **New Resource:** `aws_api_gateway_usage_plan` [GH-12542] * **New Resource:** `aws_api_gateway_usage_plan_key` [GH-12851] * **New Resource:** `github_repository_webhook` [GH-12924] From 99a12f5df30acd8afaf00e2328d8870abcaad301 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Mar 2017 10:20:23 -0400 Subject: [PATCH 05/97] interpolation strings were beeing validated Interpolation strings for non-computed values in a list were being passed to the schema's ValidateFunc. --- helper/schema/schema_test.go | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 2d79341b3..4d93ffd17 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -7,6 +7,7 @@ import ( "reflect" "sort" "strconv" + "strings" "testing" "github.com/hashicorp/hil" @@ -4924,6 +4925,47 @@ func TestSchemaMap_Validate(t *testing.T) { }, Err: true, }, + + // The Validation function should not see interpolation strings from + // non-computed values. + "set with partially computed list and map": { + Schema: map[string]*Schema{ + "outer": &Schema{ + Type: TypeSet, + Optional: true, + Computed: true, + Elem: &Resource{ + Schema: map[string]*Schema{ + "list": &Schema{ + Type: TypeList, + Optional: true, + Elem: &Schema{ + Type: TypeString, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + if strings.HasPrefix(v.(string), "${") { + es = append(es, fmt.Errorf("should not have interpolations")) + } + return + }, + }, + }, + }, + }, + }, + }, + Config: map[string]interface{}{ + "outer": []map[string]interface{}{ + { + "list": []interface{}{"${var.a}", "${var.b}", "c"}, + }, + }, + }, + Vars: map[string]string{ + "var.a": "A", + "var.b": config.UnknownVariableValue, + }, + Err: false, + }, } for tn, tc := range cases { From 2e6a44d5ffbd74ec617a99022d0cb8e374e5375a Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Mar 2017 11:31:14 -0400 Subject: [PATCH 06/97] reify the list values before validation If the list was marked as computed, all values will be raw config values. Fetch the individual keys from the config to get any known values before validating. --- helper/schema/schema.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 9f103d185..f62c4d128 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -1219,6 +1219,13 @@ func (m schemaMap) validateList( for i, raw := range raws { key := fmt.Sprintf("%s.%d", k, i) + // Reify the key value from the ResourceConfig. + // If the list was computed we have all raw values, but some of these + // may be known in the config, and aren't individually marked as Computed. + if r, ok := c.Get(key); ok { + raw = r + } + var ws2 []string var es2 []error switch t := schema.Elem.(type) { From 96617b609bf7abd0979b9c099b91cc79feac4461 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Fri, 24 Mar 2017 12:14:05 -0400 Subject: [PATCH 07/97] remove consul tests from travis runs they have timing out too often --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 88c853d68..04cc6f309 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,10 @@ language: go go: - 1.8 +# add TF_CONSUL_TEST=1 to run consul tests +# they were causing timouts in travis env: - - CONSUL_VERSION=0.7.5 TF_CONSUL_TEST=1 GOMAXPROCS=4 + - CONSUL_VERSION=0.7.5 GOMAXPROCS=4 # Fetch consul for the backend and provider tests before_install: From 84a9bcde9a9f7f181d7e2ddaaf7a0fa89d4d8bac Mon Sep 17 00:00:00 2001 From: = Date: Fri, 24 Mar 2017 10:22:50 -0600 Subject: [PATCH 08/97] Fixes route53 test --- builtin/providers/aws/data_source_aws_route53_zone_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/aws/data_source_aws_route53_zone_test.go b/builtin/providers/aws/data_source_aws_route53_zone_test.go index 42d0eb72f..49c684f29 100644 --- a/builtin/providers/aws/data_source_aws_route53_zone_test.go +++ b/builtin/providers/aws/data_source_aws_route53_zone_test.go @@ -11,7 +11,7 @@ import ( func TestAccDataSourceAwsRoute53Zone(t *testing.T) { rInt := acctest.RandInt() - publicResourceName := "aws_route53_zon.test" + publicResourceName := "aws_route53_zone.test" publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt) privateResourceName := "aws_route53_zone.test_private" privateDomain := fmt.Sprintf("test.acc-%d.", rInt) From 68e32c165cee24210811bfe18ff64979743a84f8 Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Fri, 24 Mar 2017 16:34:40 +0000 Subject: [PATCH 09/97] Switching to a simpler ARM Template (#13043) --- .../resource_arm_template_deployment_test.go | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/builtin/providers/azurerm/resource_arm_template_deployment_test.go b/builtin/providers/azurerm/resource_arm_template_deployment_test.go index 3c9b5eb84..d69716d8b 100644 --- a/builtin/providers/azurerm/resource_arm_template_deployment_test.go +++ b/builtin/providers/azurerm/resource_arm_template_deployment_test.go @@ -13,7 +13,7 @@ import ( func TestAccAzureRMTemplateDeployment_basic(t *testing.T) { ri := acctest.RandInt() - config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri) + config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicMultiple, ri, ri) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -31,7 +31,7 @@ func TestAccAzureRMTemplateDeployment_basic(t *testing.T) { func TestAccAzureRMTemplateDeployment_disappears(t *testing.T) { ri := acctest.RandInt() - config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri) + config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicSingle, ri, ri, ri) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -163,7 +163,47 @@ func testCheckAzureRMTemplateDeploymentDestroy(s *terraform.State) error { return nil } -var testAccAzureRMTemplateDeployment_basicExample = ` +var testAccAzureRMTemplateDeployment_basicSingle = ` + resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "West US" + } + + resource "azurerm_template_deployment" "test" { + name = "acctesttemplate-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + template_body = < Date: Fri, 24 Mar 2017 19:35:36 +0200 Subject: [PATCH 10/97] provider/aws: aws_network_acl_rule treat all and -1 for protocol the (#13049) same Fixes: #13012 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSNetworkAclRule_allProtocol' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/24 18:42:05 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSNetworkAclRule_allProtocol -timeout 120m === RUN TestAccAWSNetworkAclRule_allProtocol --- PASS: TestAccAWSNetworkAclRule_allProtocol (53.95s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 53.974s ``` --- .../aws/resource_aws_network_acl_rule.go | 6 ++ .../aws/resource_aws_network_acl_rule_test.go | 57 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/builtin/providers/aws/resource_aws_network_acl_rule.go b/builtin/providers/aws/resource_aws_network_acl_rule.go index 6b5f0c299..5cce925c5 100644 --- a/builtin/providers/aws/resource_aws_network_acl_rule.go +++ b/builtin/providers/aws/resource_aws_network_acl_rule.go @@ -41,6 +41,12 @@ func resourceAwsNetworkAclRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + if old == "all" && new == "-1" || old == "-1" && new == "all" { + return true + } + return false + }, }, "rule_action": { Type: schema.TypeString, diff --git a/builtin/providers/aws/resource_aws_network_acl_rule_test.go b/builtin/providers/aws/resource_aws_network_acl_rule_test.go index e793ebf53..f9ab943a8 100644 --- a/builtin/providers/aws/resource_aws_network_acl_rule_test.go +++ b/builtin/providers/aws/resource_aws_network_acl_rule_test.go @@ -66,6 +66,25 @@ func TestAccAWSNetworkAclRule_ipv6(t *testing.T) { }) } +func TestAccAWSNetworkAclRule_allProtocol(t *testing.T) { + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNetworkAclRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNetworkAclRuleAllProtocolConfig, + ExpectNonEmptyPlan: false, + }, + { + Config: testAccAWSNetworkAclRuleAllProtocolConfigNoRealUpdate, + ExpectNonEmptyPlan: false, + }, + }, + }) +} + func TestResourceAWSNetworkAclRule_validateICMPArgumentValue(t *testing.T) { type testCases struct { Value string @@ -251,6 +270,44 @@ resource "aws_network_acl_rule" "baz" { } ` +const testAccAWSNetworkAclRuleAllProtocolConfigNoRealUpdate = ` +resource "aws_vpc" "foo" { + cidr_block = "10.3.0.0/16" +} +resource "aws_network_acl" "bar" { + vpc_id = "${aws_vpc.foo.id}" +} +resource "aws_network_acl_rule" "baz" { + network_acl_id = "${aws_network_acl.bar.id}" + rule_number = 150 + egress = false + protocol = "all" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = 22 + to_port = 22 +} +` + +const testAccAWSNetworkAclRuleAllProtocolConfig = ` +resource "aws_vpc" "foo" { + cidr_block = "10.3.0.0/16" +} +resource "aws_network_acl" "bar" { + vpc_id = "${aws_vpc.foo.id}" +} +resource "aws_network_acl_rule" "baz" { + network_acl_id = "${aws_network_acl.bar.id}" + rule_number = 150 + egress = false + protocol = "-1" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = 22 + to_port = 22 +} +` + const testAccAWSNetworkAclRuleIpv6Config = ` resource "aws_vpc" "foo" { cidr_block = "10.3.0.0/16" From 812369e0cdf46444e2204adb38418a4269d76df4 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 24 Mar 2017 19:36:04 +0200 Subject: [PATCH 11/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67bc297db..bff0ea39d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ BUG FIXES: * provider/aws: Set aws_vpc ipv6 for associated only [GH-12899] * provider/aws: Fix AWS ECS placement strategy spread fields [GH-12998] * provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013] + * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From 6b44a1b9ba13ff6f6bdd35f0489445c481f3c306 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 24 Mar 2017 19:49:27 +0200 Subject: [PATCH 12/97] provider/aws: Only allow 1 value in alb_listener_rule condition (#13051) Fixes: #12983 ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSALBListenerRule_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/24 19:31:26 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSALBListenerRule_ -timeout 120m === RUN TestAccAWSALBListenerRule_basic --- PASS: TestAccAWSALBListenerRule_basic (247.76s) === RUN TestAccAWSALBListenerRule_multipleConditionThrowsError --- PASS: TestAccAWSALBListenerRule_multipleConditionThrowsError (0.02s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 247.815s ``` --- .../aws/resource_aws_alb_listener_rule.go | 1 + .../resource_aws_alb_listener_rule_test.go | 129 ++++++++++++++++++ .../aws/r/alb_listener_rule.html.markdown | 2 +- 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_alb_listener_rule.go b/builtin/providers/aws/resource_aws_alb_listener_rule.go index 1d5ba163f..d82d7410a 100644 --- a/builtin/providers/aws/resource_aws_alb_listener_rule.go +++ b/builtin/providers/aws/resource_aws_alb_listener_rule.go @@ -66,6 +66,7 @@ func resourceAwsAlbListenerRule() *schema.Resource { }, "values": { Type: schema.TypeList, + MaxItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, diff --git a/builtin/providers/aws/resource_aws_alb_listener_rule_test.go b/builtin/providers/aws/resource_aws_alb_listener_rule_test.go index e27a0a57d..0407ca614 100644 --- a/builtin/providers/aws/resource_aws_alb_listener_rule_test.go +++ b/builtin/providers/aws/resource_aws_alb_listener_rule_test.go @@ -3,6 +3,7 @@ package aws import ( "errors" "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -43,6 +44,23 @@ func TestAccAWSALBListenerRule_basic(t *testing.T) { }) } +func TestAccAWSALBListenerRule_multipleConditionThrowsError(t *testing.T) { + albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum)) + targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSALBListenerRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName), + ExpectError: regexp.MustCompile(`attribute supports 1 item maximum`), + }, + }, + }) +} + func testAccCheckAWSALBListenerRuleExists(n string, res *elbv2.Rule) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -104,6 +122,117 @@ func testAccCheckAWSALBListenerRuleDestroy(s *terraform.State) error { return nil } +func testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName string) string { + return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" { + listener_arn = "${aws_alb_listener.front_end.arn}" + priority = 100 + + action { + type = "forward" + target_group_arn = "${aws_alb_target_group.test.arn}" + } + + condition { + field = "path-pattern" + values = ["/static/*", "static"] + } +} + +resource "aws_alb_listener" "front_end" { + load_balancer_arn = "${aws_alb.alb_test.id}" + protocol = "HTTP" + port = "80" + + default_action { + target_group_arn = "${aws_alb_target_group.test.id}" + type = "forward" + } +} + +resource "aws_alb" "alb_test" { + name = "%s" + internal = true + security_groups = ["${aws_security_group.alb_test.id}"] + subnets = ["${aws_subnet.alb_test.*.id}"] + + idle_timeout = 30 + enable_deletion_protection = false + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_alb_target_group" "test" { + name = "%s" + port = 8080 + protocol = "HTTP" + vpc_id = "${aws_vpc.alb_test.id}" + + health_check { + path = "/health" + interval = 60 + port = 8081 + protocol = "HTTP" + timeout = 3 + healthy_threshold = 3 + unhealthy_threshold = 3 + matcher = "200-299" + } +} + +variable "subnets" { + default = ["10.0.1.0/24", "10.0.2.0/24"] + type = "list" +} + +data "aws_availability_zones" "available" {} + +resource "aws_vpc" "alb_test" { + cidr_block = "10.0.0.0/16" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_subnet" "alb_test" { + count = 2 + vpc_id = "${aws_vpc.alb_test.id}" + cidr_block = "${element(var.subnets, count.index)}" + map_public_ip_on_launch = true + availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}" + + tags { + TestName = "TestAccAWSALB_basic" + } +} + +resource "aws_security_group" "alb_test" { + name = "allow_all_alb_test" + description = "Used for ALB Testing" + vpc_id = "${aws_vpc.alb_test.id}" + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags { + TestName = "TestAccAWSALB_basic" + } +}`, albName, targetGroupName) +} + func testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName string) string { return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" { listener_arn = "${aws_alb_listener.front_end.arn}" diff --git a/website/source/docs/providers/aws/r/alb_listener_rule.html.markdown b/website/source/docs/providers/aws/r/alb_listener_rule.html.markdown index 6cf918e52..c2393e671 100644 --- a/website/source/docs/providers/aws/r/alb_listener_rule.html.markdown +++ b/website/source/docs/providers/aws/r/alb_listener_rule.html.markdown @@ -55,7 +55,7 @@ Action Blocks (for `default_action`) support the following: Condition Blocks (for `default_condition`) support the following: * `field` - (Required) The name of the field. The only valid value is `path-pattern`. -* `values` - (Required) The path patterns to match. +* `values` - (Required) The path patterns to match. A maximum of 1 can be defined. ## Attributes Reference From f895302630f1bdcca7bbec4d421ae35802b10dc9 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Fri, 24 Mar 2017 19:50:17 +0200 Subject: [PATCH 13/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bff0ea39d..10ed0be04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ BUG FIXES: * provider/aws: Fix AWS ECS placement strategy spread fields [GH-12998] * provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013] * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] + * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From c18ad6ed2550889ab691cfe000d56c6cbf7decee Mon Sep 17 00:00:00 2001 From: Jon Oden Date: Fri, 24 Mar 2017 15:05:29 -0400 Subject: [PATCH 14/97] fix bool vs Compatibool ref used in flattenBackends (#12538) * fix bool vs Compatobool value used in flattendBackends * remove log line * update test case --- builtin/providers/fastly/resource_fastly_service_v1.go | 4 ++-- builtin/providers/fastly/resource_fastly_service_v1_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/providers/fastly/resource_fastly_service_v1.go b/builtin/providers/fastly/resource_fastly_service_v1.go index da734c9f0..852af30ce 100644 --- a/builtin/providers/fastly/resource_fastly_service_v1.go +++ b/builtin/providers/fastly/resource_fastly_service_v1.go @@ -1922,7 +1922,7 @@ func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} { nb := map[string]interface{}{ "name": b.Name, "address": b.Address, - "auto_loadbalance": gofastly.CBool(b.AutoLoadbalance), + "auto_loadbalance": b.AutoLoadbalance, "between_bytes_timeout": int(b.BetweenBytesTimeout), "connect_timeout": int(b.ConnectTimeout), "error_threshold": int(b.ErrorThreshold), @@ -1930,7 +1930,7 @@ func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} { "max_conn": int(b.MaxConn), "port": int(b.Port), "shield": b.Shield, - "ssl_check_cert": gofastly.CBool(b.SSLCheckCert), + "ssl_check_cert": b.SSLCheckCert, "ssl_hostname": b.SSLHostname, "ssl_cert_hostname": b.SSLCertHostname, "ssl_sni_hostname": b.SSLSNIHostname, diff --git a/builtin/providers/fastly/resource_fastly_service_v1_test.go b/builtin/providers/fastly/resource_fastly_service_v1_test.go index c05006138..ba6ca5592 100644 --- a/builtin/providers/fastly/resource_fastly_service_v1_test.go +++ b/builtin/providers/fastly/resource_fastly_service_v1_test.go @@ -84,14 +84,14 @@ func TestResourceFastlyFlattenBackend(t *testing.T) { "name": "test.notexample.com", "address": "www.notexample.com", "port": 80, - "auto_loadbalance": gofastly.CBool(true), + "auto_loadbalance": true, "between_bytes_timeout": 10000, "connect_timeout": 1000, "error_threshold": 0, "first_byte_timeout": 15000, "max_conn": 200, "request_condition": "", - "ssl_check_cert": gofastly.CBool(true), + "ssl_check_cert": true, "ssl_hostname": "", "ssl_cert_hostname": "", "ssl_sni_hostname": "", From c5935ab1ad42bd16a87a3ac8df8b11b5406cd9a8 Mon Sep 17 00:00:00 2001 From: Clint Date: Fri, 24 Mar 2017 14:06:17 -0500 Subject: [PATCH 15/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ed0be04..bd31b46cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ BUG FIXES: * provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013] * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] + * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From f6a3e7d2b2972ef09fbfa3b86213a2673b1f7b8c Mon Sep 17 00:00:00 2001 From: clint shryock Date: Fri, 24 Mar 2017 15:51:56 -0500 Subject: [PATCH 16/97] docs: Update getting started docs to use a valid AMI in us-east-1, vpc Supersedes #12932 , populating the entire guide with the correct AMI(s) --- website/source/intro/getting-started/build.html.md | 8 ++++---- website/source/intro/getting-started/change.html.md | 12 ++++++------ .../intro/getting-started/dependencies.html.md | 6 +++--- .../source/intro/getting-started/provision.html.md | 4 ++-- .../source/intro/getting-started/variables.html.md | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/website/source/intro/getting-started/build.html.md b/website/source/intro/getting-started/build.html.md index ac278cb71..67099c7c0 100644 --- a/website/source/intro/getting-started/build.html.md +++ b/website/source/intro/getting-started/build.html.md @@ -59,7 +59,7 @@ provider "aws" { } resource "aws_instance" "example" { - ami = "ami-0d729a60" + ami = "ami-2757f631" instance_type = "t2.micro" } ``` @@ -125,7 +125,7 @@ $ terraform plan ... + aws_instance.example - ami: "ami-0d729a60" + ami: "ami-2757f631" availability_zone: "" ebs_block_device.#: "" ephemeral_block_device.#: "" @@ -170,7 +170,7 @@ since Terraform waits for the EC2 instance to become available. ``` $ terraform apply aws_instance.example: Creating... - ami: "" => "ami-0d729a60" + ami: "" => "ami-2757f631" instance_type: "" => "t2.micro" [...] @@ -201,7 +201,7 @@ You can inspect the state using `terraform show`: $ terraform show aws_instance.example: id = i-32cf65a8 - ami = ami-0d729a60 + ami = ami-2757f631 availability_zone = us-east-1a instance_state = running instance_type = t2.micro diff --git a/website/source/intro/getting-started/change.html.md b/website/source/intro/getting-started/change.html.md index 20fb9ce84..6f9ea6f50 100644 --- a/website/source/intro/getting-started/change.html.md +++ b/website/source/intro/getting-started/change.html.md @@ -23,20 +23,20 @@ can see how the infrastructure evolved over time. ## Configuration -Let's modify the `ami` of our instance. Edit the "aws\_instance.example" +Let's modify the `ami` of our instance. Edit the `aws_instance.example` resource in your configuration and change it to the following: ``` resource "aws_instance" "example" { - ami = "ami-13be557e" + ami = "ami-b374d5a5" instance_type = "t2.micro" } ``` ~> **Note:** EC2 Classic users please use AMI `ami-656be372` and type `t1.micro` -We've changed the AMI from being an Ubuntu 14.04 LTS AMI to being -an Ubuntu 16.04 LTS AMI. Terraform configurations are meant to be +We've changed the AMI from being an Ubuntu 16.04 LTS AMI to being +an Ubuntu 16.10 AMI. Terraform configurations are meant to be changed like this. You can also completely remove resources and Terraform will know to destroy the old one. @@ -49,7 +49,7 @@ $ terraform plan ... -/+ aws_instance.example - ami: "ami-0d729a60" => "ami-13be557e" (forces new resource) + ami: "ami-2757f631" => "ami-b374d5a5" (forces new resource) availability_zone: "us-east-1a" => "" ebs_block_device.#: "0" => "" ephemeral_block_device.#: "0" => "" @@ -86,7 +86,7 @@ aws_instance.example: Refreshing state... (ID: i-64c268fe) aws_instance.example: Destroying... aws_instance.example: Destruction complete aws_instance.example: Creating... - ami: "" => "ami-13be557e" + ami: "" => "ami-b374d5a5" availability_zone: "" => "" ebs_block_device.#: "" => "" ephemeral_block_device.#: "" => "" diff --git a/website/source/intro/getting-started/dependencies.html.md b/website/source/intro/getting-started/dependencies.html.md index 7254e9f23..2e0e4b47f 100644 --- a/website/source/intro/getting-started/dependencies.html.md +++ b/website/source/intro/getting-started/dependencies.html.md @@ -70,7 +70,7 @@ $ terraform plan public_ip: "" + aws_instance.example - ami: "ami-13be557e" + ami: "ami-b374d5a5" availability_zone: "" ebs_block_device.#: "" ephemeral_block_device.#: "" @@ -102,7 +102,7 @@ following: ``` $ terraform apply aws_instance.example: Creating... - ami: "" => "ami-13be557e" + ami: "" => "ami-b374d5a5" instance_type: "" => "t2.micro" [..] aws_instance.example: Still creating... (10s elapsed) @@ -166,7 +166,7 @@ created in parallel to everything else. ``` resource "aws_instance" "another" { - ami = "ami-13be557e" + ami = "ami-b374d5a5" instance_type = "t2.micro" } ``` diff --git a/website/source/intro/getting-started/provision.html.md b/website/source/intro/getting-started/provision.html.md index f78c3a412..d6d65033c 100644 --- a/website/source/intro/getting-started/provision.html.md +++ b/website/source/intro/getting-started/provision.html.md @@ -25,7 +25,7 @@ To define a provisioner, modify the resource block defining the ``` resource "aws_instance" "example" { - ami = "ami-13be557e" + ami = "ami-b374d5a5" instance_type = "t2.micro" provisioner "local-exec" { @@ -61,7 +61,7 @@ then run `apply`: ``` $ terraform apply aws_instance.example: Creating... - ami: "" => "ami-13be557e" + ami: "" => "ami-b374d5a5" instance_type: "" => "t2.micro" aws_eip.ip: Creating... instance: "" => "i-213f350a" diff --git a/website/source/intro/getting-started/variables.html.md b/website/source/intro/getting-started/variables.html.md index e77c83d76..032616d14 100644 --- a/website/source/intro/getting-started/variables.html.md +++ b/website/source/intro/getting-started/variables.html.md @@ -167,8 +167,8 @@ support for the `us-west-2` region as well: variable "amis" { type = "map" default = { - us-east-1 = "ami-13be557e" - us-west-2 = "ami-06b94666" + us-east-1 = "ami-b374d5a5" + us-west-2 = "ami-4b32be2b" } } ``` From 32db4d184f8795f29a259c27c0966105e2560241 Mon Sep 17 00:00:00 2001 From: Sean Chittenden Date: Fri, 24 Mar 2017 15:05:19 -0700 Subject: [PATCH 17/97] Add `consul` check type to `circonus_check` resource (#13030) * Add the `consul` check type to the `circonus_check` resource. * Fix a tab-complete fail. `Parse` != `Path`, but lexically close. * Dept of 2nd thoughts: `s/service_name/service/g` --- builtin/providers/circonus/check.go | 10 + builtin/providers/circonus/consts.go | 13 + .../circonus/resource_circonus_check.go | 23 +- .../resource_circonus_check_consul.go | 412 ++++++++++++++++++ .../resource_circonus_check_consul_test.go | 282 ++++++++++++ .../circonus/resource_circonus_check_http.go | 4 + .../circonus/resource_circonus_check_json.go | 4 + builtin/providers/circonus/validators.go | 5 + .../providers/circonus/r/check.html.markdown | 137 ++++++ 9 files changed, 886 insertions(+), 4 deletions(-) create mode 100644 builtin/providers/circonus/resource_circonus_check_consul.go create mode 100644 builtin/providers/circonus/resource_circonus_check_consul_test.go diff --git a/builtin/providers/circonus/check.go b/builtin/providers/circonus/check.go index 6200e94bf..8058b315e 100644 --- a/builtin/providers/circonus/check.go +++ b/builtin/providers/circonus/check.go @@ -25,6 +25,7 @@ const ( const ( apiCheckTypeCAQL circonusCheckType = "caql" + apiCheckTypeConsul circonusCheckType = "consul" apiCheckTypeICMPPing circonusCheckType = "ping_icmp" apiCheckTypeHTTP circonusCheckType = "http" apiCheckTypeJSON circonusCheckType = "json" @@ -108,15 +109,24 @@ func (c *circonusCheck) Fixup() error { } func (c *circonusCheck) Validate() error { + if len(c.Metrics) == 0 { + return fmt.Errorf("At least one %s must be specified", checkMetricAttr) + } + if c.Timeout > float32(c.Period) { return fmt.Errorf("Timeout (%f) can not exceed period (%d)", c.Timeout, c.Period) } + // Check-type specific validation switch apiCheckType(c.Type) { case apiCheckTypeCloudWatchAttr: if !(c.Period == 60 || c.Period == 300) { return fmt.Errorf("Period must be either 1m or 5m for a %s check", apiCheckTypeCloudWatchAttr) } + case apiCheckTypeConsulAttr: + if v, found := c.Config[config.URL]; !found || v == "" { + return fmt.Errorf("%s must have at least one check mode set: %s, %s, or %s must be set", checkConsulAttr, checkConsulServiceAttr, checkConsulNodeAttr, checkConsulStateAttr) + } } return nil diff --git a/builtin/providers/circonus/consts.go b/builtin/providers/circonus/consts.go index 6b505482a..9dd0d248f 100644 --- a/builtin/providers/circonus/consts.go +++ b/builtin/providers/circonus/consts.go @@ -17,6 +17,19 @@ const ( providerAutoTagAttr = "auto_tag" providerKeyAttr = "key" + apiConsulCheckBlacklist = "check_name_blacklist" + apiConsulDatacenterAttr = "dc" + apiConsulNodeBlacklist = "node_blacklist" + apiConsulServiceBlacklist = "service_blacklist" + apiConsulStaleAttr = "stale" + checkConsulTokenHeader = `X-Consul-Token` + checkConsulV1NodePrefix = "node" + checkConsulV1Prefix = "/v1/health" + checkConsulV1ServicePrefix = "service" + checkConsulV1StatePrefix = "state" + defaultCheckConsulHTTPAddr = "http://consul.service.consul" + defaultCheckConsulPort = "8500" + defaultCheckJSONMethod = "GET" defaultCheckJSONPort = "443" defaultCheckJSONVersion = "1.1" diff --git a/builtin/providers/circonus/resource_circonus_check.go b/builtin/providers/circonus/resource_circonus_check.go index 0c2b6d501..06bf2e9cf 100644 --- a/builtin/providers/circonus/resource_circonus_check.go +++ b/builtin/providers/circonus/resource_circonus_check.go @@ -33,21 +33,22 @@ const ( checkCAQLAttr = "caql" checkCloudWatchAttr = "cloudwatch" checkCollectorAttr = "collector" + checkConsulAttr = "consul" checkHTTPAttr = "http" checkHTTPTrapAttr = "httptrap" checkICMPPingAttr = "icmp_ping" checkJSONAttr = "json" + checkMetricAttr = "metric" checkMetricLimitAttr = "metric_limit" checkMySQLAttr = "mysql" checkNameAttr = "name" checkNotesAttr = "notes" checkPeriodAttr = "period" checkPostgreSQLAttr = "postgresql" - checkMetricAttr = "metric" checkStatsdAttr = "statsd" + checkTCPAttr = "tcp" checkTagsAttr = "tags" checkTargetAttr = "target" - checkTCPAttr = "tcp" checkTimeoutAttr = "timeout" checkTypeAttr = "type" @@ -75,6 +76,7 @@ const ( // Circonus API constants from their API endpoints apiCheckTypeCAQLAttr apiCheckType = "caql" apiCheckTypeCloudWatchAttr apiCheckType = "cloudwatch" + apiCheckTypeConsulAttr apiCheckType = "consul" apiCheckTypeHTTPAttr apiCheckType = "http" apiCheckTypeHTTPTrapAttr apiCheckType = "httptrap" apiCheckTypeICMPPingAttr apiCheckType = "ping_icmp" @@ -90,6 +92,7 @@ var checkDescriptions = attrDescrs{ checkCAQLAttr: "CAQL check configuration", checkCloudWatchAttr: "CloudWatch check configuration", checkCollectorAttr: "The collector(s) that are responsible for gathering the metrics", + checkConsulAttr: "Consul check configuration", checkHTTPAttr: "HTTP check configuration", checkHTTPTrapAttr: "HTTP Trap check configuration", checkICMPPingAttr: "ICMP ping check configuration", @@ -157,6 +160,7 @@ func resourceCheck() *schema.Resource { }), }, }, + checkConsulAttr: schemaCheckConsul, checkHTTPAttr: schemaCheckHTTP, checkHTTPTrapAttr: schemaCheckHTTPTrap, checkJSONAttr: schemaCheckJSON, @@ -577,6 +581,7 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error { checkTypeParseMap := map[string]func(*circonusCheck, interfaceList) error{ checkCAQLAttr: checkConfigToAPICAQL, checkCloudWatchAttr: checkConfigToAPICloudWatch, + checkConsulAttr: checkConfigToAPIConsul, checkHTTPAttr: checkConfigToAPIHTTP, checkHTTPTrapAttr: checkConfigToAPIHTTPTrap, checkICMPPingAttr: checkConfigToAPIICMPPing, @@ -589,8 +594,17 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error { for checkType, fn := range checkTypeParseMap { if listRaw, found := d.GetOk(checkType); found { - if err := fn(c, listRaw.(*schema.Set).List()); err != nil { - return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err) + switch u := listRaw.(type) { + case []interface{}: + if err := fn(c, u); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err) + } + case *schema.Set: + if err := fn(c, u.List()); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err) + } + default: + return fmt.Errorf("PROVIDER BUG: unsupported check type interface: %q", checkType) } } } @@ -604,6 +618,7 @@ func parseCheckTypeConfig(c *circonusCheck, d *schema.ResourceData) error { checkTypeConfigHandlers := map[apiCheckType]func(*circonusCheck, *schema.ResourceData) error{ apiCheckTypeCAQLAttr: checkAPIToStateCAQL, apiCheckTypeCloudWatchAttr: checkAPIToStateCloudWatch, + apiCheckTypeConsulAttr: checkAPIToStateConsul, apiCheckTypeHTTPAttr: checkAPIToStateHTTP, apiCheckTypeHTTPTrapAttr: checkAPIToStateHTTPTrap, apiCheckTypeICMPPingAttr: checkAPIToStateICMPPing, diff --git a/builtin/providers/circonus/resource_circonus_check_consul.go b/builtin/providers/circonus/resource_circonus_check_consul.go new file mode 100644 index 000000000..dd3f496c5 --- /dev/null +++ b/builtin/providers/circonus/resource_circonus_check_consul.go @@ -0,0 +1,412 @@ +package circonus + +import ( + "fmt" + "net" + "net/url" + "regexp" + "strings" + + "github.com/circonus-labs/circonus-gometrics/api/config" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" +) + +const ( + // circonus_check.consul.* resource attribute names + checkConsulACLTokenAttr = "acl_token" + checkConsulAllowStaleAttr = "allow_stale" + checkConsulCAChainAttr = "ca_chain" + checkConsulCertFileAttr = "certificate_file" + checkConsulCheckNameBlacklistAttr = "check_blacklist" + checkConsulCiphersAttr = "ciphers" + checkConsulDatacenterAttr = "dc" + checkConsulHTTPAddrAttr = "http_addr" + checkConsulHeadersAttr = "headers" + checkConsulKeyFileAttr = "key_file" + checkConsulNodeAttr = "node" + checkConsulNodeBlacklistAttr = "node_blacklist" + checkConsulServiceAttr = "service" + checkConsulServiceNameBlacklistAttr = "service_blacklist" + checkConsulStateAttr = "state" +) + +var checkConsulDescriptions = attrDescrs{ + checkConsulACLTokenAttr: "A Consul ACL token", + checkConsulAllowStaleAttr: "Allow Consul to read from a non-leader system", + checkConsulCAChainAttr: "A path to a file containing all the certificate authorities that should be loaded to validate the remote certificate (for TLS checks)", + checkConsulCertFileAttr: "A path to a file containing the client certificate that will be presented to the remote server (for TLS-enabled checks)", + checkConsulCheckNameBlacklistAttr: "A blacklist of check names to exclude from metric results", + checkConsulCiphersAttr: "A list of ciphers to be used in the TLS protocol (for HTTPS checks)", + checkConsulDatacenterAttr: "The Consul datacenter to extract health information from", + checkConsulHeadersAttr: "Map of HTTP Headers to send along with HTTP Requests", + checkConsulHTTPAddrAttr: "The HTTP Address of a Consul agent to query", + checkConsulKeyFileAttr: "A path to a file containing key to be used in conjunction with the cilent certificate (for TLS checks)", + checkConsulNodeAttr: "Node Name or NodeID of a Consul agent", + checkConsulNodeBlacklistAttr: "A blacklist of node names or IDs to exclude from metric results", + checkConsulServiceAttr: "Name of the Consul service to check", + checkConsulServiceNameBlacklistAttr: "A blacklist of service names to exclude from metric results", + checkConsulStateAttr: "Check for Consul services in this particular state", +} + +var consulHealthCheckRE = regexp.MustCompile(fmt.Sprintf(`^%s/(%s|%s|%s)/(.+)`, checkConsulV1Prefix, checkConsulV1NodePrefix, checkConsulV1ServicePrefix, checkConsulV1StatePrefix)) + +var schemaCheckConsul = &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: convertToHelperSchema(checkConsulDescriptions, map[schemaAttr]*schema.Schema{ + checkConsulACLTokenAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulACLTokenAttr, `^[a-zA-Z0-9\-]+$`), + }, + checkConsulAllowStaleAttr: &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + checkConsulCAChainAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulCAChainAttr, `.+`), + }, + checkConsulCertFileAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulCertFileAttr, `.+`), + }, + checkConsulCheckNameBlacklistAttr: &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateRegexp(checkConsulCheckNameBlacklistAttr, `^[A-Za-z0-9_-]+$`), + }, + }, + checkConsulCiphersAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulCiphersAttr, `.+`), + }, + checkConsulDatacenterAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulCertFileAttr, `^[a-zA-Z0-9]+$`), + }, + checkConsulHTTPAddrAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: defaultCheckConsulHTTPAddr, + ValidateFunc: validateHTTPURL(checkConsulHTTPAddrAttr, urlIsAbs|urlWithoutPath), + }, + checkConsulHeadersAttr: &schema.Schema{ + Type: schema.TypeMap, + Elem: schema.TypeString, + Optional: true, + ValidateFunc: validateHTTPHeaders, + }, + checkConsulKeyFileAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulKeyFileAttr, `.+`), + }, + checkConsulNodeAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulNodeAttr, `^[a-zA-Z0-9_\-]+$`), + ConflictsWith: []string{ + checkConsulAttr + "." + checkConsulServiceAttr, + checkConsulAttr + "." + checkConsulStateAttr, + }, + }, + checkConsulNodeBlacklistAttr: &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateRegexp(checkConsulNodeBlacklistAttr, `^[A-Za-z0-9_-]+$`), + }, + }, + checkConsulServiceAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulServiceAttr, `^[a-zA-Z0-9_\-]+$`), + ConflictsWith: []string{ + checkConsulAttr + "." + checkConsulNodeAttr, + checkConsulAttr + "." + checkConsulStateAttr, + }, + }, + checkConsulServiceNameBlacklistAttr: &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateRegexp(checkConsulServiceNameBlacklistAttr, `^[A-Za-z0-9_-]+$`), + }, + }, + checkConsulStateAttr: &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateRegexp(checkConsulStateAttr, `^(any|passing|warning|critical)$`), + ConflictsWith: []string{ + checkConsulAttr + "." + checkConsulNodeAttr, + checkConsulAttr + "." + checkConsulServiceAttr, + }, + }, + }), + }, +} + +// checkAPIToStateConsul reads the Config data out of circonusCheck.CheckBundle into +// the statefile. +func checkAPIToStateConsul(c *circonusCheck, d *schema.ResourceData) error { + consulConfig := make(map[string]interface{}, len(c.Config)) + + // swamp is a sanity check: it must be empty by the time this method returns + swamp := make(map[config.Key]string, len(c.Config)) + for k, s := range c.Config { + swamp[k] = s + } + + saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) { + if s, ok := c.Config[apiKey]; ok && s != "" { + consulConfig[string(attrName)] = s + } + + delete(swamp, apiKey) + } + + saveStringConfigToState(config.CAChain, checkConsulCAChainAttr) + saveStringConfigToState(config.CertFile, checkConsulCertFileAttr) + saveStringConfigToState(config.Ciphers, checkConsulCiphersAttr) + + // httpAddrURL is used to compose the http_addr value using multiple c.Config + // values. + var httpAddrURL url.URL + + headers := make(map[string]interface{}, len(c.Config)+1) // +1 is for the ACLToken + headerPrefixLen := len(config.HeaderPrefix) + + // Explicitly handle several config parameters in sequence: URL, then port, + // then everything else. + if v, found := c.Config[config.URL]; found { + u, err := url.Parse(v) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("unable to parse %q from config: {{err}}", config.URL), err) + } + + queryArgs := u.Query() + if vals, found := queryArgs[apiConsulStaleAttr]; found && len(vals) > 0 { + consulConfig[string(checkConsulAllowStaleAttr)] = true + } + + if dc := queryArgs.Get(apiConsulDatacenterAttr); dc != "" { + consulConfig[string(checkConsulDatacenterAttr)] = dc + } + + httpAddrURL.Host = u.Host + httpAddrURL.Scheme = u.Scheme + + md := consulHealthCheckRE.FindStringSubmatch(u.EscapedPath()) + if md == nil { + return fmt.Errorf("config %q failed to match the health regexp", config.URL) + } + + checkMode := md[1] + checkArg := md[2] + switch checkMode { + case checkConsulV1NodePrefix: + consulConfig[string(checkConsulNodeAttr)] = checkArg + case checkConsulV1ServicePrefix: + consulConfig[string(checkConsulServiceAttr)] = checkArg + case checkConsulV1StatePrefix: + consulConfig[string(checkConsulStateAttr)] = checkArg + default: + return fmt.Errorf("PROVIDER BUG: unsupported check mode %q from %q", checkMode, u.EscapedPath()) + } + + delete(swamp, config.URL) + } + + if v, found := c.Config[config.Port]; found { + hostInfo := strings.SplitN(httpAddrURL.Host, ":", 2) + switch { + case len(hostInfo) == 1 && v != defaultCheckConsulPort, len(hostInfo) > 1: + httpAddrURL.Host = net.JoinHostPort(hostInfo[0], v) + } + + delete(swamp, config.Port) + } + + if v, found := c.Config[apiConsulCheckBlacklist]; found { + consulConfig[checkConsulCheckNameBlacklistAttr] = strings.Split(v, ",") + } + + if v, found := c.Config[apiConsulNodeBlacklist]; found { + consulConfig[checkConsulNodeBlacklistAttr] = strings.Split(v, ",") + } + + if v, found := c.Config[apiConsulServiceBlacklist]; found { + consulConfig[checkConsulServiceNameBlacklistAttr] = strings.Split(v, ",") + } + + // NOTE(sean@): headers attribute processed last. See below. + + consulConfig[string(checkConsulHTTPAddrAttr)] = httpAddrURL.String() + + saveStringConfigToState(config.KeyFile, checkConsulKeyFileAttr) + + // Process the headers last in order to provide an escape hatch capible of + // overriding any other derived value above. + for k, v := range c.Config { + if len(k) <= headerPrefixLen { + continue + } + + // Handle all of the prefix variable headers, like `header_` + if strings.Compare(string(k[:headerPrefixLen]), string(config.HeaderPrefix)) == 0 { + key := k[headerPrefixLen:] + switch key { + case checkConsulTokenHeader: + consulConfig[checkConsulACLTokenAttr] = v + default: + headers[string(key)] = v + } + } + + delete(swamp, k) + } + consulConfig[string(checkConsulHeadersAttr)] = headers + + whitelistedConfigKeys := map[config.Key]struct{}{ + config.Port: struct{}{}, + config.ReverseSecretKey: struct{}{}, + config.SubmissionURL: struct{}{}, + config.URL: struct{}{}, + } + + for k := range swamp { + if _, ok := whitelistedConfigKeys[k]; ok { + delete(c.Config, k) + } + + if _, ok := whitelistedConfigKeys[k]; !ok { + return fmt.Errorf("PROVIDER BUG: API Config not empty: %#v", swamp) + } + } + + if err := d.Set(checkConsulAttr, []interface{}{consulConfig}); err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkConsulAttr), err) + } + + return nil +} + +func checkConfigToAPIConsul(c *circonusCheck, l interfaceList) error { + c.Type = string(apiCheckTypeConsul) + + // Iterate over all `consul` attributes, even though we have a max of 1 in the + // schema. + for _, mapRaw := range l { + consulConfig := newInterfaceMap(mapRaw) + if v, found := consulConfig[checkConsulCAChainAttr]; found { + c.Config[config.CAChain] = v.(string) + } + + if v, found := consulConfig[checkConsulCertFileAttr]; found { + c.Config[config.CertFile] = v.(string) + } + + if v, found := consulConfig[checkConsulCheckNameBlacklistAttr]; found { + listRaw := v.([]interface{}) + checks := make([]string, 0, len(listRaw)) + for _, v := range listRaw { + checks = append(checks, v.(string)) + } + c.Config[apiConsulCheckBlacklist] = strings.Join(checks, ",") + } + + if v, found := consulConfig[checkConsulCiphersAttr]; found { + c.Config[config.Ciphers] = v.(string) + } + + if headers := consulConfig.CollectMap(checkConsulHeadersAttr); headers != nil { + for k, v := range headers { + h := config.HeaderPrefix + config.Key(k) + c.Config[h] = v + } + } + + if v, found := consulConfig[checkConsulKeyFileAttr]; found { + c.Config[config.KeyFile] = v.(string) + } + + { + // Extract all of the input attributes necessary to construct the + // Consul agent's URL. + + httpAddr := consulConfig[checkConsulHTTPAddrAttr].(string) + checkURL, err := url.Parse(httpAddr) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Unable to parse %s's attribute %q: {{err}}", checkConsulAttr, httpAddr), err) + } + + hostInfo := strings.SplitN(checkURL.Host, ":", 2) + if len(c.Target) == 0 { + c.Target = hostInfo[0] + } + + if len(hostInfo) > 1 { + c.Config[config.Port] = hostInfo[1] + } + + if v, found := consulConfig[checkConsulNodeAttr]; found && v.(string) != "" { + checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1NodePrefix, v.(string)}, "/") + } + + if v, found := consulConfig[checkConsulServiceAttr]; found && v.(string) != "" { + checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1ServicePrefix, v.(string)}, "/") + } + + if v, found := consulConfig[checkConsulStateAttr]; found && v.(string) != "" { + checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1StatePrefix, v.(string)}, "/") + } + + q := checkURL.Query() + + if v, found := consulConfig[checkConsulAllowStaleAttr]; found && v.(bool) { + q.Set(apiConsulStaleAttr, "") + } + + if v, found := consulConfig[checkConsulDatacenterAttr]; found && v.(string) != "" { + q.Set(apiConsulDatacenterAttr, v.(string)) + } + + checkURL.RawQuery = q.Encode() + + c.Config[config.URL] = checkURL.String() + } + + if v, found := consulConfig[checkConsulNodeBlacklistAttr]; found { + listRaw := v.([]interface{}) + checks := make([]string, 0, len(listRaw)) + for _, v := range listRaw { + checks = append(checks, v.(string)) + } + c.Config[apiConsulNodeBlacklist] = strings.Join(checks, ",") + } + + if v, found := consulConfig[checkConsulServiceNameBlacklistAttr]; found { + listRaw := v.([]interface{}) + checks := make([]string, 0, len(listRaw)) + for _, v := range listRaw { + checks = append(checks, v.(string)) + } + c.Config[apiConsulServiceBlacklist] = strings.Join(checks, ",") + } + } + + return nil +} diff --git a/builtin/providers/circonus/resource_circonus_check_consul_test.go b/builtin/providers/circonus/resource_circonus_check_consul_test.go new file mode 100644 index 000000000..f7ca7993d --- /dev/null +++ b/builtin/providers/circonus/resource_circonus_check_consul_test.go @@ -0,0 +1,282 @@ +package circonus + +import ( + "fmt" + "regexp" + "testing" + + "github.com/circonus-labs/circonus-gometrics/api/config" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccCirconusCheckConsul_node(t *testing.T) { + checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=state check - %s", acctest.RandString(5)) + + checkNode := fmt.Sprintf("my-node-name-or-node-id-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDestroyCirconusCheckBundle, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthNodeFmt, checkName, checkNode), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"), + resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.dc", "dc2"), + resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul:8501"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node", checkNode), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.#", "3"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.0", "a"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.1", "bad"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.2", "node"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""), + resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"), + ), + }, + }, + }) +} + +func TestAccCirconusCheckConsul_service(t *testing.T) { + checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=service check - %s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDestroyCirconusCheckBundle, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthServiceFmt, checkName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"), + resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""), + resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service", "consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.#", "3"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.0", "bad"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.1", "hombre"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.2", "service"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "name", checkName), + resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""), + resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"), + ), + }, + }, + }) +} + +func TestAccCirconusCheckConsul_state(t *testing.T) { + checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=state check - %s", acctest.RandString(5)) + + checkState := "critical" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDestroyCirconusCheckBundle, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthStateFmt, checkName, checkState), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"), + resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""), + // resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""), + resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.state", checkState), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.0", "worthless"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.1", "check"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "name", checkName), + resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""), + resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"), + resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"), + ), + }, + }, + }) +} + +const testAccCirconusCheckConsulConfigV1HealthNodeFmt = ` +resource "circonus_check" "consul_server" { + active = true + name = "%s" + period = "60s" + + collector { + id = "/broker/2110" + } + + consul { + dc = "dc2" + http_addr = "http://consul.service.consul:8501" + node = "%s" + node_blacklist = ["a","bad","node"] + } + + metric { + name = "LastContact" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + unit = "seconds" + } + + metric { + name = "KnownLeader" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "text" + } + + tags = [ "source:consul", "lifecycle:unittest" ] + + target = "consul.service.consul" +} +` + +const testAccCirconusCheckConsulConfigV1HealthServiceFmt = ` +resource "circonus_check" "consul_server" { + active = true + name = "%s" + period = "60s" + + collector { + id = "/broker/2110" + } + + consul { + service = "consul" + service_blacklist = ["bad","hombre","service"] + } + + metric { + name = "LastContact" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + unit = "seconds" + } + + metric { + name = "KnownLeader" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "text" + } + + tags = [ "source:consul", "lifecycle:unittest" ] + + target = "consul.service.consul" +} +` + +const testAccCirconusCheckConsulConfigV1HealthStateFmt = ` +resource "circonus_check" "consul_server" { + active = true + name = "%s" + period = "60s" + + collector { + id = "/broker/2110" + } + + consul { + state = "%s" + check_blacklist = ["worthless","check"] + } + + metric { + name = "LastContact" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + unit = "seconds" + } + + metric { + name = "KnownLeader" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "text" + } + + tags = [ "source:consul", "lifecycle:unittest" ] + + target = "consul.service.consul" +} +` diff --git a/builtin/providers/circonus/resource_circonus_check_http.go b/builtin/providers/circonus/resource_circonus_check_http.go index 8a9e7d67d..7b6d68b33 100644 --- a/builtin/providers/circonus/resource_circonus_check_http.go +++ b/builtin/providers/circonus/resource_circonus_check_http.go @@ -372,6 +372,10 @@ func checkConfigToAPIHTTP(c *circonusCheck, l interfaceList) error { if len(c.Target) == 0 { c.Target = hostInfo[0] } + + if len(hostInfo) > 1 && c.Config[config.Port] == "" { + c.Config[config.Port] = hostInfo[1] + } } if v, found := httpConfig[checkHTTPVersionAttr]; found { diff --git a/builtin/providers/circonus/resource_circonus_check_json.go b/builtin/providers/circonus/resource_circonus_check_json.go index f319983cb..e377d08f3 100644 --- a/builtin/providers/circonus/resource_circonus_check_json.go +++ b/builtin/providers/circonus/resource_circonus_check_json.go @@ -355,6 +355,10 @@ func checkConfigToAPIJSON(c *circonusCheck, l interfaceList) error { if len(c.Target) == 0 { c.Target = hostInfo[0] } + + if len(hostInfo) > 1 && c.Config[config.Port] == "" { + c.Config[config.Port] = hostInfo[1] + } } if v, found := jsonConfig[checkJSONVersionAttr]; found { diff --git a/builtin/providers/circonus/validators.go b/builtin/providers/circonus/validators.go index dca2de36c..c98ec2799 100644 --- a/builtin/providers/circonus/validators.go +++ b/builtin/providers/circonus/validators.go @@ -314,6 +314,7 @@ type urlParseFlags int const ( urlIsAbs urlParseFlags = 1 << iota urlOptional + urlWithoutPath urlWithoutPort urlWithoutSchema ) @@ -345,6 +346,10 @@ func validateHTTPURL(attrName schemaAttr, checkFlags urlParseFlags) func(v inter errors = append(errors, fmt.Errorf("Schema is present on URL %q (HINT: drop the https://%s)", v.(string), v.(string))) } + if checkFlags&urlWithoutPath != 0 && u.Path != "" { + errors = append(errors, fmt.Errorf("Path is present on URL %q (HINT: drop the %s)", v.(string), u.Path)) + } + if checkFlags&urlWithoutPort != 0 { hostParts := strings.SplitN(u.Host, ":", 2) if len(hostParts) != 1 { diff --git a/website/source/docs/providers/circonus/r/check.html.markdown b/website/source/docs/providers/circonus/r/check.html.markdown index 9c1787cc9..e83d6245f 100644 --- a/website/source/docs/providers/circonus/r/check.html.markdown +++ b/website/source/docs/providers/circonus/r/check.html.markdown @@ -88,6 +88,9 @@ resource "circonus_metric" "used" { enterprise collector running in your datacenter. One collection of metrics will be automatically created for each `collector` specified. +* `consul` - (Optional) A native Consul check. See below for details on how to + configure a `consul` check. + * `http` - (Optional) A poll-based HTTP check. See below for details on how to configure the `http` check. @@ -249,6 +252,140 @@ resource "circonus_check" "rds_metrics" { } ``` +### `consul` Check Type Attributes + +* `acl_token` - (Optional) An ACL Token authenticate the API request. When an + ACL Token is set, this value is transmitted as an HTTP Header in order to not + show up in any logs. The default value is an empty string. + +* `allow_stale` - (Optional) A boolean value that indicates whether or not this + check should require the health information come from the Consul leader node. + For scalability reasons, this value defaults to `false`. See below for + details on detecting the staleness of health information. + +* `ca_chain` - (Optional) A path to a file containing all the certificate + authorities that should be loaded to validate the remote certificate (required + when `http_addr` is a TLS-enabled endpoint). + +* `certificate_file` - (Optional) A path to a file containing the client + certificate that will be presented to the remote server (required when + `http_addr` is a TLS-enabled endpoint). + +* `check_blacklist` - (Optional) A list of check names to exclude from the + result of checks (i.e. no metrics will be generated by whose check name is in + the `check_blacklist`). This blacklist is applied to the `node`, + `service`, and `state` check modes. + +* `ciphers` - (Optional) A list of ciphers to be used in the TLS protocol + (only used when `http_addr` is a TLS-enabled endpoint). + +* `dc` - (Optional) Explicitly name the Consul datacenter to use. The default + value is an empty string. When an empty value is specified, the Consul + datacenter of the agent at the `http_addr` is implicitly used. + +* `headers` - (Optional) A map of the HTTP headers to be sent when executing the + check. NOTE: the `headers` attribute is processed last and will takes + precidence over any other derived value that is transmitted as an HTTP header + to Consul (i.e. it is possible to override the `acl_token` by setting a + headers value). + +* `http_addr` - (Optional) The Consul HTTP endpoint to to query for health + information. The default value is `http://consul.service.consul:8500`. The + scheme must change from `http` to `https` when the endpoint has been + TLS-enabled. + +* `key_file` - (Optional) A path to a file containing key to be used in + conjunction with the cilent certificate (required when `http_addr` is a + TLS-enabled endpoint). + +* `node` - (Optional) Check the health of this node. The value can be either a + Consul Node ID (Consul Version >= 0.7.4) or Node Name. See also the + `service_blacklist`, `node_blacklist`, and `check_blacklist` attributes. This + attribute conflicts with the `service` and `state` attributes. + +* `node_blacklist` - (Optional) A list of node IDs or node names to exclude from + the results of checks (i.e. no metrics will be generated from nodes in the + `node_blacklist`). This blacklist is applied to the `node`, `service`, and + `state` check modes. + +* `service` - (Optional) Check the cluster-wide health of this named service. + See also the `service_blacklist`, `node_blacklist`, and `check_blacklist` + attributes. This attribute conflicts with the `node` and `state` attributes. + +* `service_blacklist` - (Optional) A list of service names to exclude from the + result of checks (i.e. no metrics will be generated by services whose service + name is in the `service_blacklist`). This blacklist is applied to the `node`, + `service`, and `state` check modes. + +* `state` - (Optional) A Circonus check to monitor Consul checks across the + entire Consul cluster. This value may be either `passing`, `warning`, or + `critical`. This `consul` check mode is intended to act as the cluster check + of last resort. This check type is useful when first starting and is intended + to act as a check of last resort before transitioning to explicitly defined + checks for individual services or nodes. The metrics returned from check will + be sorted based on the `CreateIndex` of the entry in order to have a stable + set of metrics in the array of returned values. See also the + `service_blacklist`, `node_blacklist`, and `check_blacklist` attributes. This + attribute conflicts with the `node` and `state` attributes. + +Available metrics depend on the consul check being performed (`node`, `service`, +or `state`). In addition to the data avilable from the endpoints, the `consul` +check also returns a set of metrics that are a variant of: +`{Num,Pct}{,Passing,Warning,Critical}{Checks,Nodes,Services}` (see the +`GLOB_BRACE` section of your local `glob(3)` documentation). + +Example Consul check (partial metrics collection): + +``` +resource "circonus_check" "consul_server" { + active = true + name = "%s" + period = "60s" + + collector { + # Collector ID must be an Enterprise broker able to reach the Consul agent + # listed in `http_addr`. + id = "/broker/2110" + } + + consul { + service = "consul" + + # Other consul check modes: + # node = "consul1" + # state = "critical" + } + + metric { + name = "NumNodes" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + } + + metric { + name = "LastContact" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + unit = "seconds" + } + + metric { + name = "Index" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "numeric" + unit = "transactions" + } + + metric { + name = "KnownLeader" + tags = [ "source:consul", "lifecycle:unittest" ] + type = "text" + } + + tags = [ "source:consul", "lifecycle:unittest" ] +} +``` + ### `http` Check Type Attributes * `auth_method` - (Optional) HTTP Authentication method to use. When set must From 0d3190fd8f693188bd86cb55f378930bc13c04bb Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 25 Mar 2017 01:34:42 +0000 Subject: [PATCH 18/97] provider/openstack: Fixing typo in secgroup rule timeout test --- .../resource_openstack_networking_secgroup_rule_v2_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_secgroup_rule_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_secgroup_rule_v2_test.go index dae6dc3f7..7dd62e60b 100644 --- a/builtin/providers/openstack/resource_openstack_networking_secgroup_rule_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_secgroup_rule_v2_test.go @@ -208,7 +208,7 @@ resource "openstack_networking_secgroup_rule_v2" "secgroup_rule_1" { security_group_id = "${openstack_networking_secgroup_v2.secgroup_1.id}" timeouts { - create = "5m" + delete = "5m" } } From de8e2d7f2b98c6105731a55ff4faa5b65449dd62 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 25 Mar 2017 05:08:07 -0600 Subject: [PATCH 19/97] provider/openstack: Deprecating Instance Volume attribute (#13062) This commit deprecates the volume attribute in the openstack_compute_instance_v2 resource. It's recommended to either use the block_device attribute or the openstack_compute_volume_attach_v2 resource. --- .../resource_openstack_compute_instance_v2.go | 5 +++-- .../openstack/r/compute_instance_v2.html.markdown | 15 ++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 1fa514c29..63adbabb8 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -243,8 +243,9 @@ func resourceComputeInstanceV2() *schema.Resource { }, }, "volume": &schema.Schema{ - Type: schema.TypeSet, - Optional: true, + Type: schema.TypeSet, + Optional: true, + Deprecated: "Use block_device or openstack_compute_volume_attach_v2 instead", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": &schema.Schema{ diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 133790237..65ddced18 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -35,13 +35,13 @@ resource "openstack_compute_instance_v2" "basic" { ### Instance With Attached Volume ``` -resource "openstack_blockstorage_volume_v1" "myvol" { +resource "openstack_blockstorage_volume_v2" "myvol" { name = "myvol" size = 1 } -resource "openstack_compute_instance_v2" "volume-attached" { - name = "volume-attached" +resource "openstack_compute_instance_v2" "myinstance" { + name = "myinstance" image_id = "ad091b52-742f-469e-8f3c-fd81cadf0743" flavor_id = "3" key_pair = "my_key_pair_name" @@ -50,10 +50,11 @@ resource "openstack_compute_instance_v2" "volume-attached" { network { name = "my_network" } +} - volume { - volume_id = "${openstack_blockstorage_volume_v1.myvol.id}" - } +resource "openstack_compute_volume_attach_v2" "attached" { + compute_id = "${openstack_compute_instance_v2.myinstance.id}" + volume_id = "${openstack_blockstorage_volume_v2.myvol.id}" } ``` @@ -320,7 +321,7 @@ The following arguments are supported: following [reference](http://docs.openstack.org/developer/nova/block_device_mapping.html) for more information. -* `volume` - (Optional) Attach an existing volume to the instance. The volume +* `volume` - (Deprecated) Attach an existing volume to the instance. The volume structure is described below. *Note*: This is no longer the recommended method of attaching a volume to an instance. Please see `block_device` (above) or the `openstack_compute_volume_attach_v2` and From a097a2ef5b09bf08abfe8d4c011cfeec2de91fbf Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sat, 25 Mar 2017 13:09:12 +0200 Subject: [PATCH 20/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd31b46cc..29276f63b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ IMPROVEMENTS: * provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources [GH-12865] * provider/openstack: Adding Timeouts to Network Resources [GH-12866] * provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867] + * provider/openstack: Deprecating Instance Volume attribute [GH-13062] * provider/pagerduty: Validate credentials [GH-12854] BUG FIXES: From de255d43d6ef85ef407f78e4dfa33ac7978a8403 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 25 Mar 2017 05:09:35 -0600 Subject: [PATCH 21/97] provider/openstack: Decprecating Instance Floating IP attribute (#13063) This commit deprecates the floating_ip attributes from the openstack_compute_instance_v2 resource. It is recommended to use either the openstack_compute_floatingip_associate resource or configure an openstack_networking_port_v2 resource with a floating IP. --- .../resource_openstack_compute_instance_v2.go | 14 +++++++----- .../r/compute_instance_v2.html.markdown | 22 ++++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index 63adbabb8..f3d492162 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -79,9 +79,10 @@ func resourceComputeInstanceV2() *schema.Resource { DefaultFunc: schema.EnvDefaultFunc("OS_FLAVOR_NAME", nil), }, "floating_ip": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - ForceNew: false, + Type: schema.TypeString, + Optional: true, + ForceNew: false, + Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead", }, "user_data": &schema.Schema{ Type: schema.TypeString, @@ -150,9 +151,10 @@ func resourceComputeInstanceV2() *schema.Resource { Computed: true, }, "floating_ip": &schema.Schema{ - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead", }, "mac": &schema.Schema{ Type: schema.TypeString, diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 65ddced18..5445bf944 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -175,7 +175,7 @@ resource "openstack_compute_instance_v2" "instance_1" { ### Instance With Multiple Networks ``` -resource "openstack_compute_floatingip_v2" "myip" { +resource "openstack_networking_floatingip_v2" "myip" { pool = "my_pool" } @@ -191,13 +191,15 @@ resource "openstack_compute_instance_v2" "multi-net" { } network { - name = "my_second_network" - floating_ip = "${openstack_compute_floatingip_v2.myip.address}" - - # Terraform will use this network for provisioning - access_network = true + name = "my_second_network" } } + +resource "openstack_compute_floatingip_associate_v2" "myip" { + floating_ip = "${openstack_networking_floatingip_v2.myip.address}" + instance_id = "${openstack_compute_instance_v2.multi-net.id}" + fixed_ip = "${openstack_compute_instance_v2.multi-net.network.1.fixed_ip_v4}" +} ``` ### Instance With Personality @@ -281,7 +283,7 @@ The following arguments are supported: * `flavor_name` - (Optional; Required if `flavor_id` is empty) The name of the desired flavor for the server. Changing this resizes the existing server. -* `floating_ip` - (Optional) A *Compute* Floating IP that will be associated +* `floating_ip` - (Deprecated) A *Compute* Floating IP that will be associated with the Instance. The Floating IP must be provisioned already. See *Notes* for more information about Floating IPs. @@ -360,7 +362,7 @@ The `network` block supports: * `fixed_ip_v6` - (Optional) Specifies a fixed IPv6 address to be used on this network. Changing this creates a new server. -* `floating_ip` - (Optional) Specifies a floating IP address to be associated +* `floating_ip` - (Deprecated) Specifies a floating IP address to be associated with this network. Cannot be combined with a top-level floating IP. See *Notes* for more information about Floating IPs. @@ -452,6 +454,10 @@ The following attributes are exported: ### Floating IPs +Specifying Floating IPs within the instance is now deprecated. Please use +either the `openstack_compute_floatingip_associate_v2` resource or attach +the floating IP to an `openstack_networking_port_v2` resource. + Floating IPs can be associated in one of two ways: * You can specify a Floating IP address by using the top-level `floating_ip` From bbc6ea6f6cc9584b3a4a4283fc2371bb3ebe4db9 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sat, 25 Mar 2017 13:10:01 +0200 Subject: [PATCH 22/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29276f63b..d8bb09a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ IMPROVEMENTS: * provider/openstack: Adding Timeouts to Network Resources [GH-12866] * provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867] * provider/openstack: Deprecating Instance Volume attribute [GH-13062] + * provider/openstack: Decprecating Instance Floating IP attribute [GH-13063] * provider/pagerduty: Validate credentials [GH-12854] BUG FIXES: From 9af4c1da67d7bd24807e3a6a176058b3c9f0b3db Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sat, 25 Mar 2017 14:42:18 -0600 Subject: [PATCH 23/97] provider/openstack: Fix monitor_id typo in LBaaS v1 Pool (#13069) --- .../resource_openstack_lb_pool_v1.go | 2 +- .../resource_openstack_lb_pool_v1_test.go | 110 ++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go index eb0436ddf..7cf796c6f 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1.go @@ -240,7 +240,7 @@ func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("monitor_ids") { - oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups") + oldMIDsRaw, newMIDsRaw := d.GetChange("monitor_ids") oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set) monitorsToAdd := newMIDsSet.Difference(oldMIDsSet) monitorsToRemove := oldMIDsSet.Difference(newMIDsSet) diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go b/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go index c21a74b0d..72e905406 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v1_test.go @@ -104,6 +104,42 @@ func TestAccLBV1Pool_timeout(t *testing.T) { }) } +func TestAccLBV1Pool_updateMonitor(t *testing.T) { + var monitor_1 monitors.Monitor + var monitor_2 monitors.Monitor + var network networks.Network + var pool pools.Pool + var subnet subnets.Subnet + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLBV1PoolDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccLBV1Pool_updateMonitor_1, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), + testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), + testAccCheckLBV1PoolExists("openstack_lb_pool_v1.pool_1", &pool), + testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_1", &monitor_1), + testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_2", &monitor_2), + ), + }, + resource.TestStep{ + Config: testAccLBV1Pool_updateMonitor_2, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), + testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), + testAccCheckLBV1PoolExists("openstack_lb_pool_v1.pool_1", &pool), + testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_1", &monitor_1), + testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_2", &monitor_2), + ), + }, + }, + }) +} + func testAccCheckLBV1PoolDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -402,3 +438,77 @@ resource "openstack_lb_pool_v1" "pool_1" { } } ` + +const testAccLBV1Pool_updateMonitor_1 = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_lb_monitor_v1" "monitor_1" { + type = "TCP" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" +} + +resource "openstack_lb_monitor_v1" "monitor_2" { + type = "TCP" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" +} + +resource "openstack_lb_pool_v1" "pool_1" { + name = "pool_1" + protocol = "TCP" + lb_method = "ROUND_ROBIN" + monitor_ids = ["${openstack_lb_monitor_v1.monitor_1.id}"] + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" +} +` + +const testAccLBV1Pool_updateMonitor_2 = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_lb_monitor_v1" "monitor_1" { + type = "TCP" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" +} + +resource "openstack_lb_monitor_v1" "monitor_2" { + type = "TCP" + delay = 30 + timeout = 5 + max_retries = 3 + admin_state_up = "true" +} + +resource "openstack_lb_pool_v1" "pool_1" { + name = "pool_1" + protocol = "TCP" + lb_method = "ROUND_ROBIN" + monitor_ids = ["${openstack_lb_monitor_v1.monitor_2.id}"] + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" +} +` From e50fbfe779b4d9284745bb5c21f08ee00f7323e3 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sat, 25 Mar 2017 22:43:33 +0200 Subject: [PATCH 24/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8bb09a8b..26f993cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ BUG FIXES: * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] + * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool [GH-13069] ## 0.9.1 (March 17, 2017) From d89970900480c02f254c69fd5107664c871f207d Mon Sep 17 00:00:00 2001 From: Jasmin Gacic Date: Sat, 25 Mar 2017 21:43:41 +0100 Subject: [PATCH 25/97] Handling missing resources (#13053) --- builtin/providers/profitbricks/config.go | 2 +- builtin/providers/profitbricks/provider.go | 8 ++++---- .../profitbricks/resource_profitbricks_datacenter.go | 4 ++++ .../resource_profitbricks_datacenter_test.go | 8 ++++---- .../profitbricks/resource_profitbricks_firewall.go | 4 ++++ .../profitbricks/resource_profitbricks_ipblock.go | 4 ++++ .../providers/profitbricks/resource_profitbricks_lan.go | 4 ++++ .../profitbricks/resource_profitbricks_loadbalancer.go | 8 ++++++++ .../providers/profitbricks/resource_profitbricks_nic.go | 4 ++++ .../profitbricks/resource_profitbricks_server.go | 8 +++++++- .../profitbricks/resource_profitbricks_volume.go | 9 +++++++++ 11 files changed, 53 insertions(+), 10 deletions(-) diff --git a/builtin/providers/profitbricks/config.go b/builtin/providers/profitbricks/config.go index d3b74f2fe..259616d5d 100644 --- a/builtin/providers/profitbricks/config.go +++ b/builtin/providers/profitbricks/config.go @@ -11,7 +11,7 @@ type Config struct { Retries int } -// Client() returns a new client for accessing digital ocean. +// Client() returns a new client for accessing ProfitBricks. func (c *Config) Client() (*Config, error) { profitbricks.SetAuth(c.Username, c.Password) profitbricks.SetDepth("5") diff --git a/builtin/providers/profitbricks/provider.go b/builtin/providers/profitbricks/provider.go index dc0c782a0..5db06b91e 100644 --- a/builtin/providers/profitbricks/provider.go +++ b/builtin/providers/profitbricks/provider.go @@ -7,7 +7,7 @@ import ( "github.com/profitbricks/profitbricks-sdk-go" ) -// Provider returns a schema.Provider for DigitalOcean. +// Provider returns a schema.Provider for ProfitBricks. func Provider() terraform.ResourceProvider { return &schema.Provider{ Schema: map[string]*schema.Schema{ @@ -15,19 +15,19 @@ func Provider() terraform.ResourceProvider { Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_USERNAME", nil), - Description: "Profitbricks username for API operations.", + Description: "ProfitBricks username for API operations.", }, "password": { Type: schema.TypeString, Required: true, DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_PASSWORD", nil), - Description: "Profitbricks password for API operations.", + Description: "ProfitBricks password for API operations.", }, "endpoint": { Type: schema.TypeString, Optional: true, DefaultFunc: schema.EnvDefaultFunc("PROFITBRICKS_API_URL", profitbricks.Endpoint), - Description: "Profitbricks REST API URL.", + Description: "ProfitBricks REST API URL.", }, "retries": { Type: schema.TypeInt, diff --git a/builtin/providers/profitbricks/resource_profitbricks_datacenter.go b/builtin/providers/profitbricks/resource_profitbricks_datacenter.go index d402b57dc..fee3d03cf 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_datacenter.go +++ b/builtin/providers/profitbricks/resource_profitbricks_datacenter.go @@ -69,6 +69,10 @@ func resourceProfitBricksDatacenterCreate(d *schema.ResourceData, meta interface func resourceProfitBricksDatacenterRead(d *schema.ResourceData, meta interface{}) error { datacenter := profitbricks.GetDatacenter(d.Id()) if datacenter.StatusCode > 299 { + if datacenter.StatusCode == 404 { + d.SetId("") + return nil + } return fmt.Errorf("Error while fetching a data center ID %s %s", d.Id(), datacenter.Response) } diff --git a/builtin/providers/profitbricks/resource_profitbricks_datacenter_test.go b/builtin/providers/profitbricks/resource_profitbricks_datacenter_test.go index 7ed87ed4b..9d351b1ab 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_datacenter_test.go +++ b/builtin/providers/profitbricks/resource_profitbricks_datacenter_test.go @@ -11,7 +11,7 @@ import ( func TestAccProfitBricksDataCenter_Basic(t *testing.T) { var datacenter profitbricks.Datacenter - lanName := "datacenter-test" + dc_name := "datacenter-test" resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -21,11 +21,11 @@ func TestAccProfitBricksDataCenter_Basic(t *testing.T) { CheckDestroy: testAccCheckDProfitBricksDatacenterDestroyCheck, Steps: []resource.TestStep{ resource.TestStep{ - Config: fmt.Sprintf(testAccCheckProfitBricksDatacenterConfig_basic, lanName), + Config: fmt.Sprintf(testAccCheckProfitBricksDatacenterConfig_basic, dc_name), Check: resource.ComposeTestCheckFunc( testAccCheckProfitBricksDatacenterExists("profitbricks_datacenter.foobar", &datacenter), - testAccCheckProfitBricksDatacenterAttributes("profitbricks_datacenter.foobar", lanName), - resource.TestCheckResourceAttr("profitbricks_datacenter.foobar", "name", lanName), + testAccCheckProfitBricksDatacenterAttributes("profitbricks_datacenter.foobar", dc_name), + resource.TestCheckResourceAttr("profitbricks_datacenter.foobar", "name", dc_name), ), }, resource.TestStep{ diff --git a/builtin/providers/profitbricks/resource_profitbricks_firewall.go b/builtin/providers/profitbricks/resource_profitbricks_firewall.go index 4559a0428..12fb68c0c 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_firewall.go +++ b/builtin/providers/profitbricks/resource_profitbricks_firewall.go @@ -131,6 +131,10 @@ func resourceProfitBricksFirewallRead(d *schema.ResourceData, meta interface{}) fw := profitbricks.GetFirewallRule(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Get("nic_id").(string), d.Id()) if fw.StatusCode > 299 { + if fw.StatusCode == 404 { + d.SetId("") + return nil + } return fmt.Errorf("An error occured while fetching a firewall rule dcId: %s server_id: %s nic_id: %s ID: %s %s", d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Get("nic_id").(string), d.Id(), fw.Response) } diff --git a/builtin/providers/profitbricks/resource_profitbricks_ipblock.go b/builtin/providers/profitbricks/resource_profitbricks_ipblock.go index 7ba2fdab7..e11f60d10 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_ipblock.go +++ b/builtin/providers/profitbricks/resource_profitbricks_ipblock.go @@ -59,6 +59,10 @@ func resourceProfitBricksIPBlockRead(d *schema.ResourceData, meta interface{}) e ipblock := profitbricks.GetIpBlock(d.Id()) if ipblock.StatusCode > 299 { + if ipblock.StatusCode == 404 { + d.SetId("") + return nil + } return fmt.Errorf("An error occured while fetching an ip block ID %s %s", d.Id(), ipblock.Response) } diff --git a/builtin/providers/profitbricks/resource_profitbricks_lan.go b/builtin/providers/profitbricks/resource_profitbricks_lan.go index 3a3725bd0..725c25c08 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_lan.go +++ b/builtin/providers/profitbricks/resource_profitbricks_lan.go @@ -65,6 +65,10 @@ func resourceProfitBricksLanRead(d *schema.ResourceData, meta interface{}) error lan := profitbricks.GetLan(d.Get("datacenter_id").(string), d.Id()) if lan.StatusCode > 299 { + if lan.StatusCode == 404 { + d.SetId("") + return nil + } return fmt.Errorf("An error occured while fetching a lan ID %s %s", d.Id(), lan.Response) } diff --git a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go index a905831c4..a7ffd98f3 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go +++ b/builtin/providers/profitbricks/resource_profitbricks_loadbalancer.go @@ -75,6 +75,14 @@ func resourceProfitBricksLoadbalancerCreate(d *schema.ResourceData, meta interfa func resourceProfitBricksLoadbalancerRead(d *schema.ResourceData, meta interface{}) error { lb := profitbricks.GetLoadbalancer(d.Get("datacenter_id").(string), d.Id()) + if lb.StatusCode > 299 { + if lb.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("An error occured while fetching a lan ID %s %s", d.Id(), lb.Response) + } + d.Set("name", lb.Properties.Name) d.Set("ip", lb.Properties.Ip) d.Set("dhcp", lb.Properties.Dhcp) diff --git a/builtin/providers/profitbricks/resource_profitbricks_nic.go b/builtin/providers/profitbricks/resource_profitbricks_nic.go index a2f914cb0..084f02a9a 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_nic.go +++ b/builtin/providers/profitbricks/resource_profitbricks_nic.go @@ -109,6 +109,10 @@ func resourceProfitBricksNicCreate(d *schema.ResourceData, meta interface{}) err func resourceProfitBricksNicRead(d *schema.ResourceData, meta interface{}) error { nic := profitbricks.GetNic(d.Get("datacenter_id").(string), d.Get("server_id").(string), d.Id()) if nic.StatusCode > 299 { + if nic.StatusCode == 404 { + d.SetId("") + return nil + } return fmt.Errorf("Error occured while fetching a nic ID %s %s", d.Id(), nic.Response) } log.Printf("[INFO] LAN ON NIC: %d", nic.Properties.Lan) diff --git a/builtin/providers/profitbricks/resource_profitbricks_server.go b/builtin/providers/profitbricks/resource_profitbricks_server.go index ff29aef03..bfcd1678a 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_server.go +++ b/builtin/providers/profitbricks/resource_profitbricks_server.go @@ -449,7 +449,13 @@ func resourceProfitBricksServerRead(d *schema.ResourceData, meta interface{}) er serverId := d.Id() server := profitbricks.GetServer(dcId, serverId) - + if server.StatusCode > 299 { + if server.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error occured while fetching a server ID %s %s", d.Id(), server.Response) + } d.Set("name", server.Properties.Name) d.Set("cores", server.Properties.Cores) d.Set("ram", server.Properties.Ram) diff --git a/builtin/providers/profitbricks/resource_profitbricks_volume.go b/builtin/providers/profitbricks/resource_profitbricks_volume.go index 6efed8e84..8fca17854 100644 --- a/builtin/providers/profitbricks/resource_profitbricks_volume.go +++ b/builtin/providers/profitbricks/resource_profitbricks_volume.go @@ -163,6 +163,15 @@ func resourceProfitBricksVolumeRead(d *schema.ResourceData, meta interface{}) er dcId := d.Get("datacenter_id").(string) volume := profitbricks.GetVolume(dcId, d.Id()) + + if volume.StatusCode > 299 { + if volume.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("Error occured while fetching a volume ID %s %s", d.Id(), volume.Response) + } + if volume.StatusCode > 299 { return fmt.Errorf("An error occured while fetching a volume ID %s %s", d.Id(), volume.Response) From 25d128ca084d94cb29f0446892c0ea22e04481a5 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sat, 25 Mar 2017 22:44:05 +0200 Subject: [PATCH 26/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f993cdd..75b46b439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ IMPROVEMENTS: * provider/openstack: Deprecating Instance Volume attribute [GH-13062] * provider/openstack: Decprecating Instance Floating IP attribute [GH-13063] * provider/pagerduty: Validate credentials [GH-12854] + * provider/profitbricks: Handling missing resources [GH-13053] BUG FIXES: From 86d599cb3cb0465da97e5088fe265abae7bbdecd Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sat, 25 Mar 2017 23:37:19 +0200 Subject: [PATCH 27/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b46b439..2ec92d4ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ BUG FIXES: * provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013] * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] + * provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules [GH-12835] * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From 89905368b955edc763d7503cc63b6ca09389138a Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 26 Mar 2017 03:35:40 -0600 Subject: [PATCH 28/97] provider/openstack: Don't log the catalog (#13075) The OS_DEBUG feature has worked out great, but frequent logging of the service catalog during client initialization can make logging very chatty. This commit omits the service catalog in the logs. --- builtin/providers/openstack/types.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/types.go b/builtin/providers/openstack/types.go index 7bcc75560..e2d19304c 100644 --- a/builtin/providers/openstack/types.go +++ b/builtin/providers/openstack/types.go @@ -97,7 +97,9 @@ func (lrt *LogRoundTripper) logResponseBody(original io.ReadCloser, headers http return nil, err } debugInfo := lrt.formatJSON(bs.Bytes()) - log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) + if debugInfo != "" { + log.Printf("[DEBUG] OpenStack Response Body: %s", debugInfo) + } return ioutil.NopCloser(strings.NewReader(bs.String())), nil } @@ -127,6 +129,13 @@ func (lrt *LogRoundTripper) formatJSON(raw []byte) string { } } + // Ignore the catalog + if v, ok := data["token"].(map[string]interface{}); ok { + if _, ok := v["catalog"]; ok { + return "" + } + } + pretty, err := json.MarshalIndent(data, "", " ") if err != nil { log.Printf("[DEBUG] Unable to re-marshal OpenStack JSON: %s", err) From d89055154fb98bd456ebc2b025513a7d7c72edab Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sun, 26 Mar 2017 12:36:06 +0300 Subject: [PATCH 29/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec92d4ab..b2d0b1c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ IMPROVEMENTS: * provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867] * provider/openstack: Deprecating Instance Volume attribute [GH-13062] * provider/openstack: Decprecating Instance Floating IP attribute [GH-13063] + * provider/openstack: Don't log the catalog [GH-13075] * provider/pagerduty: Validate credentials [GH-12854] * provider/profitbricks: Handling missing resources [GH-13053] From fafd488a1c7a37dfaf7bcfe5c129cc2f66f3d5a3 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Sun, 26 Mar 2017 05:35:01 -0600 Subject: [PATCH 30/97] provider/openstack: 409 Response on Pool Create (#13074) This commit accounts for a 409 response when a LBaaS v2 pool is being created. Rather than error out, this should be considered a pending state. --- .../resource_openstack_lb_pool_v2.go | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_lb_pool_v2.go b/builtin/providers/openstack/resource_openstack_lb_pool_v2.go index 73742c668..d1a602f53 100644 --- a/builtin/providers/openstack/resource_openstack_lb_pool_v2.go +++ b/builtin/providers/openstack/resource_openstack_lb_pool_v2.go @@ -167,10 +167,33 @@ func resourcePoolV2Create(d *schema.ResourceData, meta interface{}) error { } log.Printf("[DEBUG] Create Options: %#v", createOpts) - pool, err := pools.Create(networkingClient, createOpts).Extract() + + var pool *pools.Pool + err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { + var err error + log.Printf("[DEBUG] Attempting to create LBaaSV2 pool") + pool, err = pools.Create(networkingClient, createOpts).Extract() + if err != nil { + switch errCode := err.(type) { + case gophercloud.ErrDefault500: + log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.") + return resource.RetryableError(err) + case gophercloud.ErrUnexpectedResponseCode: + if errCode.Actual == 409 { + log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.") + return resource.RetryableError(err) + } + default: + return resource.NonRetryableError(err) + } + } + return nil + }) + if err != nil { return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err) } + log.Printf("[INFO] pool ID: %s", pool.ID) log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 pool (%s) to become available.", pool.ID) From 9210222f3ca8157e429f9dc17865df8f61cbb936 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Sun, 26 Mar 2017 14:35:33 +0300 Subject: [PATCH 31/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d0b1c5c..173540679 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ IMPROVEMENTS: * provider/openstack: Deprecating Instance Volume attribute [GH-13062] * provider/openstack: Decprecating Instance Floating IP attribute [GH-13063] * provider/openstack: Don't log the catalog [GH-13075] + * provider/openstack: Handle 409/500 Response on Pool Create [GH-13074] * provider/pagerduty: Validate credentials [GH-12854] * provider/profitbricks: Handling missing resources [GH-13053] From 08c0ac68e98cc595414d094104c05b1bf3eb91de Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 27 Mar 2017 05:42:49 -0400 Subject: [PATCH 32/97] Correct handling of network ACL default IPv6 ingress/egress rules. (#12835) --- .../aws/resource_aws_default_network_acl.go | 12 ++- .../resource_aws_default_network_acl_test.go | 74 +++++++++++++++---- .../providers/aws/resource_aws_network_acl.go | 6 +- .../aws/resource_aws_network_acl_test.go | 52 +++++++++++++ 4 files changed, 122 insertions(+), 22 deletions(-) diff --git a/builtin/providers/aws/resource_aws_default_network_acl.go b/builtin/providers/aws/resource_aws_default_network_acl.go index 44443e924..419972b18 100644 --- a/builtin/providers/aws/resource_aws_default_network_acl.go +++ b/builtin/providers/aws/resource_aws_default_network_acl.go @@ -9,11 +9,14 @@ import ( "github.com/hashicorp/terraform/helper/schema" ) -// ACL Network ACLs all contain an explicit deny-all rule that cannot be -// destroyed or changed by users. This rule is numbered very high to be a +// ACL Network ACLs all contain explicit deny-all rules that cannot be +// destroyed or changed by users. This rules are numbered very high to be a // catch-all. // See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl -const awsDefaultAclRuleNumber = 32767 +const ( + awsDefaultAclRuleNumberIpv4 = 32767 + awsDefaultAclRuleNumberIpv6 = 32768 +) func resourceAwsDefaultNetworkAcl() *schema.Resource { return &schema.Resource{ @@ -258,7 +261,8 @@ func revokeAllNetworkACLEntries(netaclId string, meta interface{}) error { for _, e := range networkAcl.Entries { // Skip the default rules added by AWS. They can be neither // configured or deleted by users. See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl - if *e.RuleNumber == awsDefaultAclRuleNumber { + if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 || + *e.RuleNumber == awsDefaultAclRuleNumberIpv6 { continue } diff --git a/builtin/providers/aws/resource_aws_default_network_acl_test.go b/builtin/providers/aws/resource_aws_default_network_acl_test.go index 628943634..c5f9e02d1 100644 --- a/builtin/providers/aws/resource_aws_default_network_acl_test.go +++ b/builtin/providers/aws/resource_aws_default_network_acl_test.go @@ -36,8 +36,27 @@ func TestAccAWSDefaultNetworkAcl_basic(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_basic, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 2), + ), + }, + }, + }) +} + +func TestAccAWSDefaultNetworkAcl_basicIpv6Vpc(t *testing.T) { + var networkAcl ec2.NetworkAcl + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSDefaultNetworkConfig_basicIpv6Vpc, + Check: resource.ComposeTestCheckFunc( + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 4), ), }, }, @@ -58,8 +77,8 @@ func TestAccAWSDefaultNetworkAcl_deny_ingress(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_deny_ingress, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0, 2), ), }, }, @@ -77,8 +96,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_Subnets, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2), ), }, @@ -88,8 +107,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_Subnets_remove, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2), ), ExpectNonEmptyPlan: true, }, @@ -108,8 +127,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_Subnets, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2), ), }, @@ -128,8 +147,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) { resource.TestStep{ Config: testAccAWSDefaultNetworkConfig_Subnets_move, Check: resource.ComposeTestCheckFunc( - testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), - testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0), + testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl), + testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 2), ), }, }, @@ -141,14 +160,14 @@ func testAccCheckAWSDefaultNetworkAclDestroy(s *terraform.State) error { return nil } -func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.NetworkAclEntry, subnetCount int) resource.TestCheckFunc { +func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.NetworkAclEntry, subnetCount int, hiddenRuleCount int) resource.TestCheckFunc { return func(s *terraform.State) error { aclEntriesCount := len(acl.Entries) ruleCount := len(rules) - // Default ACL has 2 hidden rules we can't do anything about - ruleCount = ruleCount + 2 + // Default ACL has hidden rules we can't do anything about + ruleCount = ruleCount + hiddenRuleCount if ruleCount != aclEntriesCount { return fmt.Errorf("Expected (%d) Rules, got (%d)", ruleCount, aclEntriesCount) @@ -162,7 +181,7 @@ func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.Netwo } } -func testAccGetWSDefaultNetworkAcl(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc { +func testAccGetAWSDefaultNetworkAcl(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -426,3 +445,26 @@ resource "aws_default_network_acl" "default" { } } ` + +const testAccAWSDefaultNetworkConfig_basicIpv6Vpc = ` +provider "aws" { + region = "us-east-2" +} + +resource "aws_vpc" "tftestvpc" { + cidr_block = "10.1.0.0/16" + assign_generated_ipv6_cidr_block = true + + tags { + Name = "TestAccAWSDefaultNetworkAcl_basicIpv6Vpc" + } +} + +resource "aws_default_network_acl" "default" { + default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}" + + tags { + Name = "TestAccAWSDefaultNetworkAcl_basicIpv6Vpc" + } +} +` diff --git a/builtin/providers/aws/resource_aws_network_acl.go b/builtin/providers/aws/resource_aws_network_acl.go index 7a525e299..4777f4707 100644 --- a/builtin/providers/aws/resource_aws_network_acl.go +++ b/builtin/providers/aws/resource_aws_network_acl.go @@ -201,7 +201,8 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error { for _, e := range networkAcl.Entries { // Skip the default rules added by AWS. They can be neither // configured or deleted by users. - if *e.RuleNumber == awsDefaultAclRuleNumber { + if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 || + *e.RuleNumber == awsDefaultAclRuleNumberIpv6 { continue } @@ -358,7 +359,8 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2 // neither modified nor destroyed. They have a custom rule // number that is out of bounds for any other rule. If we // encounter it, just continue. There's no work to be done. - if *remove.RuleNumber == awsDefaultAclRuleNumber { + if *remove.RuleNumber == awsDefaultAclRuleNumberIpv4 || + *remove.RuleNumber == awsDefaultAclRuleNumberIpv6 { continue } diff --git a/builtin/providers/aws/resource_aws_network_acl_test.go b/builtin/providers/aws/resource_aws_network_acl_test.go index bc589267f..b97568b65 100644 --- a/builtin/providers/aws/resource_aws_network_acl_test.go +++ b/builtin/providers/aws/resource_aws_network_acl_test.go @@ -242,6 +242,8 @@ func TestAccAWSNetworkAcl_ipv6Rules(t *testing.T) { Config: testAccAWSNetworkAclIpv6Config, Check: resource.ComposeTestCheckFunc( testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), + resource.TestCheckResourceAttr( + "aws_network_acl.foos", "ingress.#", "1"), resource.TestCheckResourceAttr( "aws_network_acl.foos", "ingress.1976110835.protocol", "6"), resource.TestCheckResourceAttr( @@ -260,6 +262,29 @@ func TestAccAWSNetworkAcl_ipv6Rules(t *testing.T) { }) } +func TestAccAWSNetworkAcl_ipv6VpcRules(t *testing.T) { + var networkAcl ec2.NetworkAcl + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_network_acl.foos", + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSNetworkAclDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSNetworkAclIpv6VpcConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), + resource.TestCheckResourceAttr( + "aws_network_acl.foos", "ingress.#", "1"), + resource.TestCheckResourceAttr( + "aws_network_acl.foos", "ingress.1296304962.ipv6_cidr_block", "2600:1f16:d1e:9a00::/56"), + ), + }, + }, + }) +} + func TestAccAWSNetworkAcl_espProtocol(t *testing.T) { var networkAcl ec2.NetworkAcl @@ -436,6 +461,33 @@ resource "aws_network_acl" "foos" { } ` +const testAccAWSNetworkAclIpv6VpcConfig = ` +provider "aws" { + region = "us-east-2" +} + +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" + assign_generated_ipv6_cidr_block = true + + tags { + Name = "TestAccAWSNetworkAcl_ipv6VpcRules" + } +} + +resource "aws_network_acl" "foos" { + vpc_id = "${aws_vpc.foo.id}" + ingress = { + protocol = "tcp" + rule_no = 1 + action = "allow" + ipv6_cidr_block = "2600:1f16:d1e:9a00::/56" + from_port = 0 + to_port = 22 + } +} +` + const testAccAWSNetworkAclIngressConfig = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" From 0edbedd1a85b807dd60ea3dfb541d6e02e8f95ed Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 Mar 2017 03:50:39 -0600 Subject: [PATCH 33/97] random_pet resource (#12903) * vendor: adding golang-petname for random_pet resource * provider/random: random_pet resource --- builtin/providers/random/provider.go | 1 + builtin/providers/random/resource_pet.go | 66 ++++++ builtin/providers/random/resource_pet_test.go | 115 ++++++++++ .../dustinkirkland/golang-petname/LICENSE | 202 ++++++++++++++++++ .../dustinkirkland/golang-petname/README.md | 107 ++++++++++ .../golang-petname/golang-petname.1 | 51 +++++ .../dustinkirkland/golang-petname/petname.go | 78 +++++++ vendor/vendor.json | 6 + .../docs/providers/random/r/pet.html.md | 60 ++++++ website/source/layouts/random.erb | 3 + 10 files changed, 689 insertions(+) create mode 100644 builtin/providers/random/resource_pet.go create mode 100644 builtin/providers/random/resource_pet_test.go create mode 100644 vendor/github.com/dustinkirkland/golang-petname/LICENSE create mode 100644 vendor/github.com/dustinkirkland/golang-petname/README.md create mode 100644 vendor/github.com/dustinkirkland/golang-petname/golang-petname.1 create mode 100644 vendor/github.com/dustinkirkland/golang-petname/petname.go create mode 100644 website/source/docs/providers/random/r/pet.html.md diff --git a/builtin/providers/random/provider.go b/builtin/providers/random/provider.go index c0741dc3c..15665f465 100644 --- a/builtin/providers/random/provider.go +++ b/builtin/providers/random/provider.go @@ -13,6 +13,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "random_id": resourceId(), "random_shuffle": resourceShuffle(), + "random_pet": resourcePet(), }, } } diff --git a/builtin/providers/random/resource_pet.go b/builtin/providers/random/resource_pet.go new file mode 100644 index 000000000..4c6f3e335 --- /dev/null +++ b/builtin/providers/random/resource_pet.go @@ -0,0 +1,66 @@ +package random + +import ( + "fmt" + "strings" + + "github.com/dustinkirkland/golang-petname" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourcePet() *schema.Resource { + return &schema.Resource{ + Create: CreatePet, + Read: ReadPet, + Delete: schema.RemoveFromState, + + Schema: map[string]*schema.Schema{ + "keepers": { + Type: schema.TypeMap, + Optional: true, + ForceNew: true, + }, + + "length": { + Type: schema.TypeInt, + Optional: true, + Default: 2, + ForceNew: true, + }, + + "prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + + "separator": { + Type: schema.TypeString, + Optional: true, + Default: "-", + ForceNew: true, + }, + }, + } +} + +func CreatePet(d *schema.ResourceData, meta interface{}) error { + length := d.Get("length").(int) + separator := d.Get("separator").(string) + prefix := d.Get("prefix").(string) + + pet := strings.ToLower(petname.Generate(length, separator)) + + if prefix != "" { + pet = fmt.Sprintf("%s%s%s", prefix, separator, pet) + } + + d.SetId(pet) + + return nil +} + +func ReadPet(d *schema.ResourceData, meta interface{}) error { + return nil +} diff --git a/builtin/providers/random/resource_pet_test.go b/builtin/providers/random/resource_pet_test.go new file mode 100644 index 000000000..64bebb6f6 --- /dev/null +++ b/builtin/providers/random/resource_pet_test.go @@ -0,0 +1,115 @@ +package random + +import ( + "fmt" + "regexp" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccResourcePet_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccResourcePet_basic, + Check: resource.ComposeTestCheckFunc( + testAccResourcePetLength("random_pet.pet_1", "-", 2), + ), + }, + }, + }) +} + +func TestAccResourcePet_length(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccResourcePet_length, + Check: resource.ComposeTestCheckFunc( + testAccResourcePetLength("random_pet.pet_1", "-", 4), + ), + }, + }, + }) +} + +func TestAccResourcePet_prefix(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccResourcePet_prefix, + Check: resource.ComposeTestCheckFunc( + testAccResourcePetLength("random_pet.pet_1", "-", 3), + resource.TestMatchResourceAttr( + "random_pet.pet_1", "id", regexp.MustCompile("^consul-")), + ), + }, + }, + }) +} + +func TestAccResourcePet_separator(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccResourcePet_separator, + Check: resource.ComposeTestCheckFunc( + testAccResourcePetLength("random_pet.pet_1", "_", 3), + ), + }, + }, + }) +} + +func testAccResourcePetLength(id string, separator string, length int) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[id] + if !ok { + return fmt.Errorf("Not found: %s", id) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + petParts := strings.Split(rs.Primary.ID, separator) + if len(petParts) != length { + return fmt.Errorf("Length does not match") + } + + return nil + } +} + +const testAccResourcePet_basic = ` +resource "random_pet" "pet_1" { +} +` + +const testAccResourcePet_length = ` +resource "random_pet" "pet_1" { + length = 4 +} +` +const testAccResourcePet_prefix = ` +resource "random_pet" "pet_1" { + prefix = "consul" +} +` + +const testAccResourcePet_separator = ` +resource "random_pet" "pet_1" { + length = 3 + separator = "_" +} +` diff --git a/vendor/github.com/dustinkirkland/golang-petname/LICENSE b/vendor/github.com/dustinkirkland/golang-petname/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/dustinkirkland/golang-petname/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/dustinkirkland/golang-petname/README.md b/vendor/github.com/dustinkirkland/golang-petname/README.md new file mode 100644 index 000000000..13374040f --- /dev/null +++ b/vendor/github.com/dustinkirkland/golang-petname/README.md @@ -0,0 +1,107 @@ +#petname + +##NAME []() + +**petname** − a utility to generate "pet names", consisting of a random combination of adverbs, an adjective, and an animal name + +##SYNOPSIS []() + +**petname** \[-w|--words INT\] \[-l|--letters INT\] \[-s|--separator STR\] \[-d|--dir STR\] \[-c|--complexity INT\] \[-u|--ubuntu\] + +##OPTIONS []() +- -w|--words number of words in the name, default is 2 +- -l|--letters maximum number of letters in each word, default is unlimited +- -s|--separator string used to separate name words, default is ’-’ +- -d|--dir directory containing adverbs.txt, adjectives.txt, names.txt, default is */usr/share/petname/* +- -c|--complexity \[0, 1, 2\]; 0 = easy words, 1 = standard words, 2 = complex words, default=1 +- -u|--ubuntu generate ubuntu-style names, alliteration of first character of each word + +##DESCRIPTION []() + +This utility will generate "pet names", consisting of a random combination of an adverb, adjective, and an animal name. These are useful for unique hostnames or container names, for instance. + +As such, PetName tries to follow the tenets of Zooko’s triangle. Names are: + +- human meaningful +- decentralized +- secure + +##EXAMPLES []() + +``` +$ petname +wiggly-yellowtail + +$ petname --words 1 +robin + +$ petname --words 3 +primly-lasting-toucan + +$ petname --words 4 +angrily-impatiently-sage-longhorn + +$ petname --separator ":" +cool:gobbler + +$ petname --separator "" --words 3 +comparablyheartylionfish + +$ petname --ubuntu +amazed-asp + +$ petname --complexity 0 +massive-colt +``` + +##CODE []() + +Besides this shell utility, there are also native libraries: python-petname, python3-petname, and golang-petname. Here are some programmatic examples in code: + +**Golang Example** +```golang +package main + +import ( + "flag" + "fmt" + "github.com/dustinkirkland/golang-petname" +) + +var ( + words = flag.Int("words", 2, "The number of words in the pet name") + separator = flag.String("separator", "-", "The separator between words in the pet name") +) + +func main() { + flag.Parse() + fmt.Println(petname.Generate(\*words, \*separator)) +} +``` + +**Python Example** +See: https://pypi.golang.org/pypi/petname + +$ pip install petname +$ sudo apt-get install golang-petname + +```python +#!/usr/bin/python +import argparse +import petname + +parser = argparse.ArgumentParser(description="Generate human readable random names") +parser.add_argument("-w", "--words", help="Number of words in name, default=2", default=2) +parser.add_argument("-s", "--separator", help="Separator between words, default='-'", default="-") +parser.options = parser.parse_args() + +print petname.Generate(int(parser.options.words), parser.options.separator) +``` + +##AUTHOR []() + +This manpage and the utility were written by Dustin Kirkland <dustin.kirkland@gmail.com> for Ubuntu systems (but may be used by others). Permission is granted to copy, distribute and/or modify this document and the utility under the terms of the Apache2 License. + +The complete text of the Apache2 License can be found in */usr/share/common-licenses/Apache-2.0* on Debian/Ubuntu systems. + +------------------------------------------------------------------------ diff --git a/vendor/github.com/dustinkirkland/golang-petname/golang-petname.1 b/vendor/github.com/dustinkirkland/golang-petname/golang-petname.1 new file mode 100644 index 000000000..f9fdc7427 --- /dev/null +++ b/vendor/github.com/dustinkirkland/golang-petname/golang-petname.1 @@ -0,0 +1,51 @@ +.TH golang-petname 1 "15 December 2014" golang-petname "golang-petname" +.SH NAME +golang-petname \- utility to generate "pet names", consisting of a random combination of adverbs, an adjective, and a proper name + +.SH SYNOPSIS +\fBgolang-petname\fP [-w|--words INT] [-s|--separator STR] + +.SH OPTIONS + + --words number of words in the name, default is 2 + --separator string used to separate name words, default is '-' + +.SH DESCRIPTION + +This utility will generate "pet names", consisting of a random combination of an adverb, adjective, and proper name. These are useful for unique hostnames, for instance. + +The default packaging contains about 2000 names, 1300 adjectives, and 4000 adverbs, yielding nearly 10 billion unique combinations, covering over 32 bits of unique namespace. + +As such, PetName tries to follow the tenets of Zooko's triangle. Names are: + + - human meaningful + - decentralized + - secure + +.SH EXAMPLES + + $ golang-petname + wiggly-Anna + + $ golang-petname --words 1 + Marco + + $ golang-petname --words 3 + quickly-scornful-Johnathan + + $ golang-petname --words 4 + dolorously-leisurely-wee-Susan + + $ golang-petname --separator ":" + hospitable:Isla + + $ golang-petname --separator "" --words 3 + adeptlystaticNicole + +.SH SEE ALSO +\fIpetname\fP(1) + +.SH AUTHOR +This manpage and the utility were written by Dustin Kirkland for Ubuntu systems (but may be used by others). Permission is granted to copy, distribute and/or modify this document and the utility under the terms of the Apache2 License. + +The complete text of the Apache2 License can be found in \fI/usr/share/common-licenses/Apache-2.0\fP on Debian/Ubuntu systems. diff --git a/vendor/github.com/dustinkirkland/golang-petname/petname.go b/vendor/github.com/dustinkirkland/golang-petname/petname.go new file mode 100644 index 000000000..a66b7ebe9 --- /dev/null +++ b/vendor/github.com/dustinkirkland/golang-petname/petname.go @@ -0,0 +1,78 @@ +/* + petname: library for generating human-readable, random names + for objects (e.g. hostnames, containers, blobs) + + Copyright 2014 Dustin Kirkland + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package petname is a library for generating human-readable, random +// names for objects (e.g. hostnames, containers, blobs). +package petname + +import ( + "math/rand" + "strings" + "time" +) + +// These lists are autogenerated from the master lists in the project: +// - https://github.com/dustinkirkland/petname +// These lists only get modified after updating that branch, and then +// automatically updated by ./debian/update-wordlists.sh as part of +// my release process +var ( + adjectives = [...]string{"able", "above", "absolute", "accepted", "accurate", "ace", "active", "actual", "adapted", "adapting", "adequate", "adjusted", "advanced", "alert", "alive", "allowed", "allowing", "amazed", "amazing", "ample", "amused", "amusing", "apparent", "apt", "arriving", "artistic", "assured", "assuring", "awaited", "awake", "aware", "balanced", "becoming", "beloved", "better", "big", "blessed", "bold", "boss", "brave", "brief", "bright", "bursting", "busy", "calm", "capable", "capital", "careful", "caring", "casual", "causal", "central", "certain", "champion", "charmed", "charming", "cheerful", "chief", "choice", "civil", "classic", "clean", "clear", "clever", "climbing", "close", "closing", "coherent", "comic", "communal", "complete", "composed", "concise", "concrete", "content", "cool", "correct", "cosmic", "crack", "creative", "credible", "crisp", "crucial", "cuddly", "cunning", "curious", "current", "cute", "daring", "darling", "dashing", "dear", "decent", "deciding", "deep", "definite", "delicate", "desired", "destined", "devoted", "direct", "discrete", "distinct", "diverse", "divine", "dominant", "driven", "driving", "dynamic", "eager", "easy", "electric", "elegant", "emerging", "eminent", "enabled", "enabling", "endless", "engaged", "engaging", "enhanced", "enjoyed", "enormous", "enough", "epic", "equal", "equipped", "eternal", "ethical", "evident", "evolved", "evolving", "exact", "excited", "exciting", "exotic", "expert", "factual", "fair", "faithful", "famous", "fancy", "fast", "feasible", "fine", "finer", "firm", "first", "fit", "fitting", "fleet", "flexible", "flowing", "fluent", "flying", "fond", "frank", "free", "fresh", "full", "fun", "funny", "game", "generous", "gentle", "genuine", "giving", "glad", "glorious", "glowing", "golden", "good", "gorgeous", "grand", "grateful", "great", "growing", "grown", "guided", "guiding", "handy", "happy", "hardy", "harmless", "healthy", "helped", "helpful", "helping", "heroic", "hip", "holy", "honest", "hopeful", "hot", "huge", "humane", "humble", "humorous", "ideal", "immense", "immortal", "immune", "improved", "in", "included", "infinite", "informed", "innocent", "inspired", "integral", "intense", "intent", "internal", "intimate", "inviting", "joint", "just", "keen", "key", "kind", "knowing", "known", "large", "lasting", "leading", "learning", "legal", "legible", "lenient", "liberal", "light", "liked", "literate", "live", "living", "logical", "loved", "loving", "loyal", "lucky", "magical", "magnetic", "main", "major", "many", "massive", "master", "mature", "maximum", "measured", "meet", "merry", "mighty", "mint", "model", "modern", "modest", "moral", "more", "moved", "moving", "musical", "mutual", "national", "native", "natural", "nearby", "neat", "needed", "neutral", "new", "next", "nice", "noble", "normal", "notable", "noted", "novel", "obliging", "on", "one", "open", "optimal", "optimum", "organic", "oriented", "outgoing", "patient", "peaceful", "perfect", "pet", "picked", "pleasant", "pleased", "pleasing", "poetic", "polished", "polite", "popular", "positive", "possible", "powerful", "precious", "precise", "premium", "prepared", "present", "pretty", "primary", "prime", "pro", "probable", "profound", "promoted", "prompt", "proper", "proud", "proven", "pumped", "pure", "quality", "quick", "quiet", "rapid", "rare", "rational", "ready", "real", "refined", "regular", "related", "relative", "relaxed", "relaxing", "relevant", "relieved", "renewed", "renewing", "resolved", "rested", "rich", "right", "robust", "romantic", "ruling", "sacred", "safe", "saved", "saving", "secure", "select", "selected", "sensible", "set", "settled", "settling", "sharing", "sharp", "shining", "simple", "sincere", "singular", "skilled", "smart", "smashing", "smiling", "smooth", "social", "solid", "sought", "sound", "special", "splendid", "square", "stable", "star", "steady", "sterling", "still", "stirred", "stirring", "striking", "strong", "stunning", "subtle", "suitable", "suited", "summary", "sunny", "super", "superb", "supreme", "sure", "sweeping", "sweet", "talented", "teaching", "tender", "thankful", "thorough", "tidy", "tight", "together", "tolerant", "top", "topical", "tops", "touched", "touching", "tough", "true", "trusted", "trusting", "trusty", "ultimate", "unbiased", "uncommon", "unified", "unique", "united", "up", "upright", "upward", "usable", "useful", "valid", "valued", "vast", "verified", "viable", "vital", "vocal", "wanted", "warm", "wealthy", "welcome", "welcomed", "well", "whole", "willing", "winning", "wired", "wise", "witty", "wondrous", "workable", "working", "worthy"} + adverbs = [...]string{"abnormally", "absolutely", "accurately", "actively", "actually", "adequately", "admittedly", "adversely", "allegedly", "amazingly", "annually", "apparently", "arguably", "awfully", "badly", "barely", "basically", "blatantly", "blindly", "briefly", "brightly", "broadly", "carefully", "centrally", "certainly", "cheaply", "cleanly", "clearly", "closely", "commonly", "completely", "constantly", "conversely", "correctly", "curiously", "currently", "daily", "deadly", "deeply", "definitely", "directly", "distinctly", "duly", "eagerly", "early", "easily", "eminently", "endlessly", "enormously", "entirely", "equally", "especially", "evenly", "evidently", "exactly", "explicitly", "externally", "extremely", "factually", "fairly", "finally", "firmly", "firstly", "forcibly", "formally", "formerly", "frankly", "freely", "frequently", "friendly", "fully", "generally", "gently", "genuinely", "ghastly", "gladly", "globally", "gradually", "gratefully", "greatly", "grossly", "happily", "hardly", "heartily", "heavily", "hideously", "highly", "honestly", "hopefully", "hopelessly", "horribly", "hugely", "humbly", "ideally", "illegally", "immensely", "implicitly", "incredibly", "indirectly", "infinitely", "informally", "inherently", "initially", "instantly", "intensely", "internally", "jointly", "jolly", "kindly", "largely", "lately", "legally", "lightly", "likely", "literally", "lively", "locally", "logically", "loosely", "loudly", "lovely", "luckily", "mainly", "manually", "marginally", "mentally", "merely", "mildly", "miserably", "mistakenly", "moderately", "monthly", "morally", "mostly", "multiply", "mutually", "namely", "nationally", "naturally", "nearly", "neatly", "needlessly", "newly", "nicely", "nominally", "normally", "notably", "noticeably", "obviously", "oddly", "officially", "only", "openly", "optionally", "overly", "painfully", "partially", "partly", "perfectly", "personally", "physically", "plainly", "pleasantly", "poorly", "positively", "possibly", "precisely", "preferably", "presently", "presumably", "previously", "primarily", "privately", "probably", "promptly", "properly", "publicly", "purely", "quickly", "quietly", "radically", "randomly", "rapidly", "rarely", "rationally", "readily", "really", "reasonably", "recently", "regularly", "reliably", "remarkably", "remotely", "repeatedly", "rightly", "roughly", "routinely", "sadly", "safely", "scarcely", "secondly", "secretly", "seemingly", "sensibly", "separately", "seriously", "severely", "sharply", "shortly", "similarly", "simply", "sincerely", "singularly", "slightly", "slowly", "smoothly", "socially", "solely", "specially", "steadily", "strangely", "strictly", "strongly", "subtly", "suddenly", "suitably", "supposedly", "surely", "terminally", "terribly", "thankfully", "thoroughly", "tightly", "totally", "trivially", "truly", "typically", "ultimately", "unduly", "uniformly", "uniquely", "unlikely", "urgently", "usefully", "usually", "utterly", "vaguely", "vastly", "verbally", "vertically", "vigorously", "violently", "virtually", "visually", "weekly", "wholly", "widely", "wildly", "willingly", "wrongly", "yearly"} + names = [...]string{"ox", "ant", "ape", "asp", "bat", "bee", "boa", "bug", "cat", "cod", "cow", "cub", "doe", "dog", "eel", "eft", "elf", "elk", "emu", "ewe", "fly", "fox", "gar", "gnu", "hen", "hog", "imp", "jay", "kid", "kit", "koi", "lab", "man", "owl", "pig", "pug", "pup", "ram", "rat", "ray", "yak", "bass", "bear", "bird", "boar", "buck", "bull", "calf", "chow", "clam", "colt", "crab", "crow", "dane", "deer", "dodo", "dory", "dove", "drum", "duck", "fawn", "fish", "flea", "foal", "fowl", "frog", "gnat", "goat", "grub", "gull", "hare", "hawk", "ibex", "joey", "kite", "kiwi", "lamb", "lark", "lion", "loon", "lynx", "mako", "mink", "mite", "mole", "moth", "mule", "mutt", "newt", "orca", "oryx", "pika", "pony", "puma", "seal", "shad", "slug", "sole", "stag", "stud", "swan", "tahr", "teal", "tick", "toad", "tuna", "wasp", "wolf", "worm", "wren", "yeti", "adder", "akita", "alien", "aphid", "bison", "boxer", "bream", "bunny", "burro", "camel", "chimp", "civet", "cobra", "coral", "corgi", "crane", "dingo", "drake", "eagle", "egret", "filly", "finch", "gator", "gecko", "ghost", "ghoul", "goose", "guppy", "heron", "hippo", "horse", "hound", "husky", "hyena", "koala", "krill", "leech", "lemur", "liger", "llama", "louse", "macaw", "midge", "molly", "moose", "moray", "mouse", "panda", "perch", "prawn", "quail", "racer", "raven", "rhino", "robin", "satyr", "shark", "sheep", "shrew", "skink", "skunk", "sloth", "snail", "snake", "snipe", "squid", "stork", "swift", "swine", "tapir", "tetra", "tiger", "troll", "trout", "viper", "wahoo", "whale", "zebra", "alpaca", "amoeba", "baboon", "badger", "beagle", "bedbug", "beetle", "bengal", "bobcat", "caiman", "cattle", "cicada", "collie", "condor", "cougar", "coyote", "dassie", "donkey", "dragon", "earwig", "falcon", "feline", "ferret", "gannet", "gibbon", "glider", "goblin", "gopher", "grouse", "guinea", "hermit", "hornet", "iguana", "impala", "insect", "jackal", "jaguar", "jennet", "kitten", "kodiak", "lizard", "locust", "maggot", "magpie", "mammal", "mantis", "marlin", "marmot", "marten", "martin", "mayfly", "minnow", "monkey", "mullet", "muskox", "ocelot", "oriole", "osprey", "oyster", "parrot", "pigeon", "piglet", "poodle", "possum", "python", "quagga", "rabbit", "raptor", "rodent", "roughy", "salmon", "sawfly", "serval", "shiner", "shrimp", "spider", "sponge", "tarpon", "thrush", "tomcat", "toucan", "turkey", "turtle", "urchin", "vervet", "walrus", "weasel", "weevil", "wombat", "anchovy", "anemone", "bluejay", "buffalo", "bulldog", "buzzard", "caribou", "catfish", "chamois", "cheetah", "chicken", "chigger", "cowbird", "crappie", "crawdad", "cricket", "dogfish", "dolphin", "firefly", "garfish", "gazelle", "gelding", "giraffe", "gobbler", "gorilla", "goshawk", "grackle", "griffon", "grizzly", "grouper", "gryphon", "haddock", "hagfish", "halibut", "hamster", "herring", "jackass", "javelin", "jawfish", "jaybird", "katydid", "ladybug", "lamprey", "lemming", "leopard", "lioness", "lobster", "macaque", "mallard", "mammoth", "manatee", "mastiff", "meerkat", "mollusk", "monarch", "mongrel", "monitor", "monster", "mudfish", "muskrat", "mustang", "narwhal", "oarfish", "octopus", "opossum", "ostrich", "panther", "peacock", "pegasus", "pelican", "penguin", "phoenix", "piranha", "polecat", "primate", "quetzal", "raccoon", "rattler", "redbird", "redfish", "reptile", "rooster", "sawfish", "sculpin", "seagull", "skylark", "snapper", "spaniel", "sparrow", "sunbeam", "sunbird", "sunfish", "tadpole", "termite", "terrier", "unicorn", "vulture", "wallaby", "walleye", "warthog", "whippet", "wildcat", "aardvark", "airedale", "albacore", "anteater", "antelope", "arachnid", "barnacle", "basilisk", "blowfish", "bluebird", "bluegill", "bonefish", "bullfrog", "cardinal", "chipmunk", "cockatoo", "crawfish", "crayfish", "dinosaur", "doberman", "duckling", "elephant", "escargot", "flamingo", "flounder", "foxhound", "glowworm", "goldfish", "grubworm", "hedgehog", "honeybee", "hookworm", "humpback", "kangaroo", "killdeer", "kingfish", "labrador", "lacewing", "ladybird", "lionfish", "longhorn", "mackerel", "malamute", "marmoset", "mastodon", "moccasin", "mongoose", "monkfish", "mosquito", "pangolin", "parakeet", "pheasant", "pipefish", "platypus", "polliwog", "porpoise", "reindeer", "ringtail", "sailfish", "scorpion", "seahorse", "seasnail", "sheepdog", "shepherd", "silkworm", "squirrel", "stallion", "starfish", "starling", "stingray", "stinkbug", "sturgeon", "terrapin", "titmouse", "tortoise", "treefrog", "werewolf", "woodcock"} +) + +// Adverb returns a random adverb from a list of petname adverbs. +func Adverb() string { + return adverbs[rand.Intn(len(adverbs))] +} + +// Adjective returns a random adjective from a list of petname adjectives. +func Adjective() string { + return adjectives[rand.Intn(len(adjectives))] +} + +// Name returns a random name from a list of petname names. +func Name() string { + return names[rand.Intn(len(names))] +} + +// Generate generates and returns a random pet name. +// It takes two parameters: the number of words in the name, and a separator token. +// If a single word is requested, simply a Name() is returned. +// If two words are requested, a Adjective() and a Name() are returned. +// If three or more words are requested, a variable number of Adverb() and a Adjective and a Name() is returned. +// The separator can be any charater, string, or the empty string. +func Generate(words int, separator string) string { + if words == 1 { + return Name() + } else if words == 2 { + return Adjective() + separator + Name() + } + var petname []string + for i := 0; i < words-2; i++ { + petname = append(petname, Adverb()) + } + petname = append(petname, Adjective(), Name()) + return strings.Join(petname, separator) +} + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 3d129580d..ee9f48dda 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1395,6 +1395,12 @@ "revision": "7a41df006ff9af79a29f0ffa9c5f21fbe6314a2d", "revisionTime": "2017-01-10T07:11:07Z" }, + { + "checksumSHA1": "3Ue4yQFsolS+6tKtrSTtph7GJ74=", + "path": "github.com/dustinkirkland/golang-petname", + "revision": "242afa0b4f8af1fa581e7ea7f4b6d51735fa3fef", + "revisionTime": "2017-01-05T21:50:08Z" + }, { "checksumSHA1": "GCskdwYAPW2S34918Z5CgNMJ2Wc=", "path": "github.com/dylanmei/iso8601", diff --git a/website/source/docs/providers/random/r/pet.html.md b/website/source/docs/providers/random/r/pet.html.md new file mode 100644 index 000000000..dd212be3e --- /dev/null +++ b/website/source/docs/providers/random/r/pet.html.md @@ -0,0 +1,60 @@ +--- +layout: "random" +page_title: "Random: random_pet" +sidebar_current: "docs-random-resource-pet" +description: |- + Generates a random pet. +--- + +# random\_pet + +The resource `random_pet` generates random pet names that are intended to be +used as unique identifiers for other resources. + +This resource can be used in conjunction with resources that have +the `create_before_destroy` lifecycle flag set, to avoid conflicts with +unique names during the brief period where both the old and new resources +exist concurrently. + +## Example Usage + +The following example shows how to generate a unique pet name for an AWS EC2 +instance that changes each time a new AMI id is selected. + +``` +resource "random_pet" "server" { + keepers = { + # Generate a new pet name each time we switch to a new AMI id + ami_id = "${var.ami_id}" + } +} + +resource "aws_instance" "server" { + tags = { + Name = "web-server-${random_pet.server.id}" + } + + # Read the AMI id "through" the random_pet resource to ensure that + # both will change together. + ami = "${random_pet.server.keepers.ami_id}" + + # ... (other aws_instance arguments) ... +} +``` + +The result of the above will set the Name of the AWS Instance to +`web-server-simple-snake`. + +## Argument Reference + +The following arguments are supported: + +* `keepers` - (Optional) Arbitrary map of values that, when changed, will + trigger a new id to be generated. See + [the main provider documentation](../index.html) for more information. + +* `length` - (Optional) The lenth (in words) of the pet name. + +* `prefix` - (Optional) A string to prefix the name with. + +* `separator` - (Optional) The character to separate words in the pet name. diff --git a/website/source/layouts/random.erb b/website/source/layouts/random.erb index 224559c85..89d38b559 100644 --- a/website/source/layouts/random.erb +++ b/website/source/layouts/random.erb @@ -16,6 +16,9 @@ > random_id + > + random_pet + > random_shuffle From 47c8fc456af4980707c0bae584a5c36e290c3df3 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 12:51:29 +0300 Subject: [PATCH 34/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 173540679..0c7c438d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ FEATURES: * **New Resource:** `aws_api_gateway_usage_plan` [GH-12542] * **New Resource:** `aws_api_gateway_usage_plan_key` [GH-12851] * **New Resource:** `github_repository_webhook` [GH-12924] + * **New Resource:** `random_pet` [GH-12903] * **New Interpolation:** `substr` [GH-12870] IMPROVEMENTS: From 403ea9f6d7e8a97389be7f9d008503cabb5564f1 Mon Sep 17 00:00:00 2001 From: Raphael Randschau Date: Mon, 27 Mar 2017 12:00:11 +0200 Subject: [PATCH 35/97] provider/scaleway: work around parallel request limitation (#13045) according to the official scaleway support, requests within the same session can not be parallelized. While I do not know for sure that this is a write-only limitation, I've implemented it as a write-only limitation for now. Previously requests like this would produce a 500 internal server error: ``` resource "scaleway_ip" "test_ip" { count = 2 } ``` now this limitation should be lifted, for all scaleway resources --- builtin/providers/scaleway/provider.go | 4 ++++ builtin/providers/scaleway/resource_scaleway_ip.go | 14 ++++++++++---- .../scaleway/resource_scaleway_security_group.go | 9 +++++++++ .../resource_scaleway_security_group_rule.go | 9 +++++++++ .../providers/scaleway/resource_scaleway_server.go | 10 +++++++++- .../providers/scaleway/resource_scaleway_volume.go | 11 +++++++++++ .../resource_scaleway_volume_attachment.go | 7 +++++++ 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/builtin/providers/scaleway/provider.go b/builtin/providers/scaleway/provider.go index c05d6e977..16069fe21 100644 --- a/builtin/providers/scaleway/provider.go +++ b/builtin/providers/scaleway/provider.go @@ -1,11 +1,15 @@ package scaleway import ( + "sync" + "github.com/hashicorp/terraform/helper/mutexkv" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) +var mu = sync.Mutex{} + // Provider returns a terraform.ResourceProvider. func Provider() terraform.ResourceProvider { return &schema.Provider{ diff --git a/builtin/providers/scaleway/resource_scaleway_ip.go b/builtin/providers/scaleway/resource_scaleway_ip.go index 27cb6fb47..f388fe4e2 100644 --- a/builtin/providers/scaleway/resource_scaleway_ip.go +++ b/builtin/providers/scaleway/resource_scaleway_ip.go @@ -2,7 +2,6 @@ package scaleway import ( "log" - "sync" "github.com/hashicorp/terraform/helper/schema" "github.com/scaleway/scaleway-cli/pkg/api" @@ -31,13 +30,12 @@ func resourceScalewayIP() *schema.Resource { } } -var mu = sync.Mutex{} - func resourceScalewayIPCreate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() - defer mu.Unlock() resp, err := scaleway.NewIP() + mu.Unlock() if err != nil { return err } @@ -71,6 +69,10 @@ func resourceScalewayIPRead(d *schema.ResourceData, m interface{}) error { func resourceScalewayIPUpdate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + + mu.Lock() + defer mu.Unlock() + if d.HasChange("server") { if d.Get("server").(string) != "" { log.Printf("[DEBUG] Attaching IP %q to server %q\n", d.Id(), d.Get("server").(string)) @@ -88,6 +90,10 @@ func resourceScalewayIPUpdate(d *schema.ResourceData, m interface{}) error { func resourceScalewayIPDelete(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + + mu.Lock() + defer mu.Unlock() + err := scaleway.DeleteIP(d.Id()) if err != nil { return err diff --git a/builtin/providers/scaleway/resource_scaleway_security_group.go b/builtin/providers/scaleway/resource_scaleway_security_group.go index e30c08620..e2c44b676 100644 --- a/builtin/providers/scaleway/resource_scaleway_security_group.go +++ b/builtin/providers/scaleway/resource_scaleway_security_group.go @@ -34,6 +34,9 @@ func resourceScalewaySecurityGroup() *schema.Resource { func resourceScalewaySecurityGroupCreate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + req := api.ScalewayNewSecurityGroup{ Name: d.Get("name").(string), Description: d.Get("description").(string), @@ -94,6 +97,9 @@ func resourceScalewaySecurityGroupRead(d *schema.ResourceData, m interface{}) er func resourceScalewaySecurityGroupUpdate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + var req = api.ScalewayUpdateSecurityGroup{ Organization: scaleway.Organization, Name: d.Get("name").(string), @@ -112,6 +118,9 @@ func resourceScalewaySecurityGroupUpdate(d *schema.ResourceData, m interface{}) func resourceScalewaySecurityGroupDelete(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + err := scaleway.DeleteSecurityGroup(d.Id()) if err != nil { if serr, ok := err.(api.ScalewayAPIError); ok { diff --git a/builtin/providers/scaleway/resource_scaleway_security_group_rule.go b/builtin/providers/scaleway/resource_scaleway_security_group_rule.go index 03d29a95f..4359052b5 100644 --- a/builtin/providers/scaleway/resource_scaleway_security_group_rule.go +++ b/builtin/providers/scaleway/resource_scaleway_security_group_rule.go @@ -67,6 +67,9 @@ func resourceScalewaySecurityGroupRule() *schema.Resource { func resourceScalewaySecurityGroupRuleCreate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + req := api.ScalewayNewSecurityGroupRule{ Action: d.Get("action").(string), Direction: d.Get("direction").(string), @@ -140,6 +143,9 @@ func resourceScalewaySecurityGroupRuleRead(d *schema.ResourceData, m interface{} func resourceScalewaySecurityGroupRuleUpdate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + var req = api.ScalewayNewSecurityGroupRule{ Action: d.Get("action").(string), Direction: d.Get("direction").(string), @@ -160,6 +166,9 @@ func resourceScalewaySecurityGroupRuleUpdate(d *schema.ResourceData, m interface func resourceScalewaySecurityGroupRuleDelete(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + err := scaleway.DeleteSecurityGroupRule(d.Get("security_group").(string), d.Id()) if err != nil { if serr, ok := err.(api.ScalewayAPIError); ok { diff --git a/builtin/providers/scaleway/resource_scaleway_server.go b/builtin/providers/scaleway/resource_scaleway_server.go index 57183c152..1d27c76ea 100644 --- a/builtin/providers/scaleway/resource_scaleway_server.go +++ b/builtin/providers/scaleway/resource_scaleway_server.go @@ -108,6 +108,9 @@ func resourceScalewayServer() *schema.Resource { func resourceScalewayServerCreate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + image := d.Get("image").(string) var server = api.ScalewayServerDefinition{ Name: d.Get("name").(string), @@ -217,8 +220,10 @@ func resourceScalewayServerRead(d *schema.ResourceData, m interface{}) error { func resourceScalewayServerUpdate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway - var req api.ScalewayServerPatchDefinition + mu.Lock() + defer mu.Unlock() + var req api.ScalewayServerPatchDefinition if d.HasChange("name") { name := d.Get("name").(string) req.Name = &name @@ -258,6 +263,9 @@ func resourceScalewayServerUpdate(d *schema.ResourceData, m interface{}) error { func resourceScalewayServerDelete(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + s, err := scaleway.GetServer(d.Id()) if err != nil { return err diff --git a/builtin/providers/scaleway/resource_scaleway_volume.go b/builtin/providers/scaleway/resource_scaleway_volume.go index 6f72ff59a..e36cf15c9 100644 --- a/builtin/providers/scaleway/resource_scaleway_volume.go +++ b/builtin/providers/scaleway/resource_scaleway_volume.go @@ -45,6 +45,10 @@ func resourceScalewayVolume() *schema.Resource { func resourceScalewayVolumeCreate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + + mu.Lock() + defer mu.Unlock() + size := uint64(d.Get("size_in_gb").(int)) * gb req := api.ScalewayVolumeDefinition{ Name: d.Get("name").(string), @@ -88,6 +92,9 @@ func resourceScalewayVolumeRead(d *schema.ResourceData, m interface{}) error { func resourceScalewayVolumeUpdate(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + mu.Lock() + defer mu.Unlock() + var req api.ScalewayVolumePutDefinition if d.HasChange("name") { req.Name = String(d.Get("name").(string)) @@ -104,6 +111,10 @@ func resourceScalewayVolumeUpdate(d *schema.ResourceData, m interface{}) error { func resourceScalewayVolumeDelete(d *schema.ResourceData, m interface{}) error { scaleway := m.(*Client).scaleway + + mu.Lock() + defer mu.Unlock() + err := scaleway.DeleteVolume(d.Id()) if err != nil { if serr, ok := err.(api.ScalewayAPIError); ok { diff --git a/builtin/providers/scaleway/resource_scaleway_volume_attachment.go b/builtin/providers/scaleway/resource_scaleway_volume_attachment.go index 518b4acfe..532586c66 100644 --- a/builtin/providers/scaleway/resource_scaleway_volume_attachment.go +++ b/builtin/providers/scaleway/resource_scaleway_volume_attachment.go @@ -95,7 +95,9 @@ func resourceScalewayVolumeAttachmentCreate(d *schema.ResourceData, m interface{ var req = api.ScalewayServerPatchDefinition{ Volumes: &volumes, } + mu.Lock() err := scaleway.PatchServer(serverID, req) + mu.Unlock() if err == nil { return nil @@ -172,6 +174,9 @@ func resourceScalewayVolumeAttachmentDelete(d *schema.ResourceData, m interface{ scaleway := m.(*Client).scaleway scaleway.ClearCache() + mu.Lock() + defer mu.Unlock() + var startServerAgain = false // guard against server shutdown/ startup race conditiond @@ -221,7 +226,9 @@ func resourceScalewayVolumeAttachmentDelete(d *schema.ResourceData, m interface{ var req = api.ScalewayServerPatchDefinition{ Volumes: &volumes, } + mu.Lock() err := scaleway.PatchServer(serverID, req) + mu.Unlock() if err == nil { return nil From 8735e936d4b99c988b078c87360851967181f075 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 13:01:36 +0300 Subject: [PATCH 36/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7c438d9..d9fcaddfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ BUG FIXES: * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool [GH-13069] + * provider/scaleway: work around parallel request limitation [GH-13045] ## 0.9.1 (March 17, 2017) From 0ff734f1577b01bd67a51092a56fe4a2e15deb28 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 27 Mar 2017 13:16:01 +0300 Subject: [PATCH 37/97] provider/digitalocean: Support disk only resize (#13059) Allow to resize a droplet permanently (i.e. apply disk resize) if previously it was resized temporarily (CPU and RAM only). Fixes: #13007 ``` $ make testacc TEST=./builtin/providers/digitalocean TESTARGS='-run=TestAccDigitalOceanDroplet_ResizeOnlyDisk' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/25 03:54:23 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/digitalocean -v -run=TestAccDigitalOceanDroplet_ResizeOnlyDisk -timeout 120m === RUN TestAccDigitalOceanDroplet_ResizeOnlyDisk --- PASS: TestAccDigitalOceanDroplet_ResizeOnlyDisk (198.62s) PASS ok github.com/hashicorp/terraform/builtin/providers/digitalocean 198.638s ``` --- .../resource_digitalocean_droplet.go | 20 ++--- .../resource_digitalocean_droplet_test.go | 78 +++++++++++++++++++ 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet.go b/builtin/providers/digitalocean/resource_digitalocean_droplet.go index ff57a63d9..76212d579 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet.go @@ -322,8 +322,9 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("invalid droplet id: %v", err) } - if d.HasChange("size") { - oldSize, newSize := d.GetChange("size") + resize_disk := d.Get("resize_disk").(bool) + if d.HasChange("size") || d.HasChange("resize_disk") && resize_disk { + newSize := d.Get("size") _, _, err = client.DropletActions.PowerOff(id) if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") { @@ -339,13 +340,7 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) } // Resize the droplet - resize_disk := d.Get("resize_disk") - switch { - case resize_disk == true: - _, _, err = client.DropletActions.Resize(id, newSize.(string), true) - case resize_disk == false: - _, _, err = client.DropletActions.Resize(id, newSize.(string), false) - } + action, _, err := client.DropletActions.Resize(id, newSize.(string), resize_disk) if err != nil { newErr := powerOnAndWait(d, meta) if newErr != nil { @@ -356,11 +351,8 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{}) "Error resizing droplet (%s): %s", d.Id(), err) } - // Wait for the size to change - _, err = WaitForDropletAttribute( - d, newSize.(string), []string{"", oldSize.(string)}, "size", meta) - - if err != nil { + // Wait for the resize action to complete. + if err := waitForAction(client, action); err != nil { newErr := powerOnAndWait(d, meta) if newErr != nil { return fmt.Errorf( diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go b/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go index d68d7f96c..8b8a90efa 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go @@ -144,6 +144,56 @@ func TestAccDigitalOceanDroplet_ResizeWithOutDisk(t *testing.T) { }) } +func TestAccDigitalOceanDroplet_ResizeOnlyDisk(t *testing.T) { + var droplet godo.Droplet + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanDropletDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), + testAccCheckDigitalOceanDropletAttributes(&droplet), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), + ), + }, + + { + Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), + testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "size", "1gb"), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "disk", "20"), + ), + }, + + { + Config: testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), + testAccCheckDigitalOceanDropletResizeOnlyDisk(&droplet), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "size", "1gb"), + resource.TestCheckResourceAttr( + "digitalocean_droplet.foobar", "disk", "30"), + ), + }, + }, + }) +} + func TestAccDigitalOceanDroplet_UpdateUserData(t *testing.T) { var afterCreate, afterUpdate godo.Droplet rInt := acctest.RandInt() @@ -321,6 +371,21 @@ func testAccCheckDigitalOceanDropletResizeWithOutDisk(droplet *godo.Droplet) res } } +func testAccCheckDigitalOceanDropletResizeOnlyDisk(droplet *godo.Droplet) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if droplet.Size.Slug != "1gb" { + return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) + } + + if droplet.Disk != 30 { + return fmt.Errorf("Bad disk: %d", droplet.Disk) + } + + return nil + } +} + func testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(droplet *godo.Droplet) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -492,6 +557,19 @@ resource "digitalocean_droplet" "foobar" { `, rInt) } +func testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt int) string { + return fmt.Sprintf(` +resource "digitalocean_droplet" "foobar" { + name = "foo-%d" + size = "1gb" + image = "centos-7-x64" + region = "nyc3" + user_data = "foobar" + resize_disk = true +} +`, rInt) +} + // IPV6 only in singapore func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string { return fmt.Sprintf(` From 0e9f84b30b16f3fe0881452af4e61c31c3641cbc Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 13:17:34 +0300 Subject: [PATCH 38/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9fcaddfc..8955c6023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ IMPROVEMENTS: * provider/azurerm: Add support for setting the primary network interface [GH-11290] * provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource [GH-11306] * provider/consul: Add support for basic auth to the provider [GH-12679] + * provider/digitalocean: Support disk only resize [GH-13059] * provider/dnsimple: Allow dnsimple_record.priority attribute to be set [GH-12843] * provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config [GH-12743] * provider/google: Add local ssd count support for container clusters [GH-12281] From 07733e13ba9b107db4338b2f256ef921767ae16c Mon Sep 17 00:00:00 2001 From: peay Date: Mon, 27 Mar 2017 08:28:26 -0400 Subject: [PATCH 39/97] vSphere disks: read disk capacity instead of file size --- .../vsphere/resource_vsphere_virtual_disk.go | 64 ++++++++++++++++--- .../resource_vsphere_virtual_disk_test.go | 2 + 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_disk.go b/builtin/providers/vsphere/resource_vsphere_virtual_disk.go index 7b0eedccb..a17d43d5c 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_disk.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_disk.go @@ -4,12 +4,14 @@ import ( "fmt" "log" + "errors" "github.com/hashicorp/terraform/helper/schema" "github.com/vmware/govmomi" "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/types" "golang.org/x/net/context" + "path" ) type virtualDisk struct { @@ -180,20 +182,66 @@ func resourceVSphereVirtualDiskRead(d *schema.ResourceData, meta interface{}) er return err } - fileInfo, err := ds.Stat(context.TODO(), vDisk.vmdkPath) + ctx := context.TODO() + b, err := ds.Browser(ctx) if err != nil { - log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - stat failed on: %v", vDisk.vmdkPath) - d.SetId("") + return err + } - _, ok := err.(object.DatastoreNoSuchFileError) - if !ok { - return err + // `Datastore.Stat` does not allow to query `VmDiskFileQuery`. Instead, we + // search the datastore manually. + spec := types.HostDatastoreBrowserSearchSpec{ + Query: []types.BaseFileQuery{&types.VmDiskFileQuery{Details: &types.VmDiskFileQueryFlags{ + CapacityKb: true, + DiskType: true, + }}}, + Details: &types.FileQueryFlags{ + FileSize: true, + FileType: true, + Modification: true, + FileOwner: types.NewBool(true), + }, + MatchPattern: []string{path.Base(vDisk.vmdkPath)}, + } + + dsPath := ds.Path(path.Dir(vDisk.vmdkPath)) + task, err := b.SearchDatastore(context.TODO(), dsPath, &spec) + + if err != nil { + log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath) + return err + } + + info, err := task.WaitForResult(context.TODO(), nil) + if err != nil { + if info == nil || info.Error != nil { + _, ok := info.Error.Fault.(*types.FileNotFound) + if ok { + log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath) + d.SetId("") + return nil + } } + + log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not search datastore for: %v", vDisk.vmdkPath) + return err + } + + res := info.Result.(types.HostDatastoreBrowserSearchResults) + log.Printf("[DEBUG] num results: %d", len(res.File)) + if len(res.File) == 0 { + d.SetId("") + log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - could not find: %v", vDisk.vmdkPath) return nil } - fileInfo = fileInfo.GetFileInfo() + + if len(res.File) != 1 { + return errors.New("Datastore search did not return exactly one result") + } + + fileInfo := res.File[0] log.Printf("[DEBUG] resourceVSphereVirtualDiskRead - fileinfo: %#v", fileInfo) - size := fileInfo.(*types.FileInfo).FileSize / 1024 / 1024 / 1024 + size := fileInfo.(*types.VmDiskFileInfo).CapacityKb / 1024 / 1024 d.SetId(vDisk.vmdkPath) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go index c27426ad7..a24ea597e 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go @@ -27,6 +27,8 @@ func TestAccVSphereVirtualDisk_basic(t *testing.T) { } if v := os.Getenv("VSPHERE_INIT_TYPE"); v != "" { initTypeOpt += fmt.Sprintf(" type = \"%s\"\n", v) + } else { + initTypeOpt += fmt.Sprintf(" type = \"%s\"\n", "thin") } if v := os.Getenv("VSPHERE_ADAPTER_TYPE"); v != "" { adapterTypeOpt += fmt.Sprintf(" adapter_type = \"%s\"\n", v) From 15abbe21c00b341c4034da035b4fa1a885083bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Dzurek?= Date: Mon, 27 Mar 2017 14:34:05 +0200 Subject: [PATCH 40/97] fixed modules docs typo (#13087) --- website/source/docs/modules/create.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/modules/create.html.markdown b/website/source/docs/modules/create.html.markdown index 231ad1c12..8320268d2 100644 --- a/website/source/docs/modules/create.html.markdown +++ b/website/source/docs/modules/create.html.markdown @@ -83,7 +83,7 @@ Here we use `${path.module}` to get a module-relative path. ## Nested Modules -You can nest a module within another module. This module will be hidden from your root configuration, so you'll have re-expose any +You can nest a module within another module. This module will be hidden from your root configuration, so you'll have to re-expose any variables and outputs you require. The [get command](/docs/commands/get.html) will automatically get all nested modules. From 3e8a8c5e23b2a5b79cf750c9d91d29f6abd2bc1e Mon Sep 17 00:00:00 2001 From: Adam Dehnel Date: Mon, 27 Mar 2017 07:50:33 -0500 Subject: [PATCH 41/97] Just adding the `id` attribute (#13090) --- website/source/docs/providers/rancher/r/environment.html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/rancher/r/environment.html.md b/website/source/docs/providers/rancher/r/environment.html.md index 498ebda08..cb4c1b8f6 100644 --- a/website/source/docs/providers/rancher/r/environment.html.md +++ b/website/source/docs/providers/rancher/r/environment.html.md @@ -31,7 +31,7 @@ The following arguments are supported: ## Attributes Reference -No further attributes are exported. +* `id` - The ID of the environment (ie `1a11`) that can be used in other Terraform resources such as Rancher Stack definitions. ## Import From e7e35b5c074935d15e5113a6d08dd4f0a9be74e6 Mon Sep 17 00:00:00 2001 From: Doug Neal Date: Mon, 27 Mar 2017 13:56:57 +0100 Subject: [PATCH 42/97] provider/aws: aws_ses_receipt_rule: fix off-by-one errors (#12961) In function `resourceAwsSesReceiptRuleRead` the position of the receipt rules in the rule set was taken directly from the index of the rule's position in the slice returned by the AWS API call. As the slice is zero-based and the ruleset is one-based, this results in an incorrect representation. This manifests as `aws_ses_receipt_rule` resources always showing a diff during plan or apply. --- .../providers/aws/resource_aws_ses_receipt_rule.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/providers/aws/resource_aws_ses_receipt_rule.go b/builtin/providers/aws/resource_aws_ses_receipt_rule.go index 54cb88c34..912620acd 100644 --- a/builtin/providers/aws/resource_aws_ses_receipt_rule.go +++ b/builtin/providers/aws/resource_aws_ses_receipt_rule.go @@ -443,7 +443,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err addHeaderAction := map[string]interface{}{ "header_name": *element.AddHeaderAction.HeaderName, "header_value": *element.AddHeaderAction.HeaderValue, - "position": i, + "position": i + 1, } addHeaderActionList = append(addHeaderActionList, addHeaderAction) } @@ -453,7 +453,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err "message": *element.BounceAction.Message, "sender": *element.BounceAction.Sender, "smtp_reply_code": *element.BounceAction.SmtpReplyCode, - "position": i, + "position": i + 1, } if element.BounceAction.StatusCode != nil { @@ -470,7 +470,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err if element.LambdaAction != nil { lambdaAction := map[string]interface{}{ "function_arn": *element.LambdaAction.FunctionArn, - "position": i, + "position": i + 1, } if element.LambdaAction.InvocationType != nil { @@ -487,7 +487,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err if element.S3Action != nil { s3Action := map[string]interface{}{ "bucket_name": *element.S3Action.BucketName, - "position": i, + "position": i + 1, } if element.S3Action.KmsKeyArn != nil { @@ -508,7 +508,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err if element.SNSAction != nil { snsAction := map[string]interface{}{ "topic_arn": *element.SNSAction.TopicArn, - "position": i, + "position": i + 1, } snsActionList = append(snsActionList, snsAction) @@ -517,7 +517,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err if element.StopAction != nil { stopAction := map[string]interface{}{ "scope": *element.StopAction.Scope, - "position": i, + "position": i + 1, } if element.StopAction.TopicArn != nil { @@ -530,7 +530,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err if element.WorkmailAction != nil { workmailAction := map[string]interface{}{ "organization_arn": *element.WorkmailAction.OrganizationArn, - "position": i, + "position": i + 1, } if element.WorkmailAction.TopicArn != nil { From 3adf6dd4ad48a68012f165254b65ab3b4aaa8e28 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 15:57:24 +0300 Subject: [PATCH 43/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8955c6023..341904373 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ BUG FIXES: * provider/aws: aws_network_acl_rule treat all and -1 for protocol the same [GH-13049] * provider/aws: Only allow 1 value in alb_listener_rule condition [GH-13051] * provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules [GH-12835] + * provider/aws: aws_ses_receipt_rule: fix off-by-one errors [GH-12961] * provider/fastly: Fix issue importing Fastly Services with Backends [GH-12538] * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] From 4d2e28aecbda1d0ee8c605ccbcd07aca6e249e46 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 21 Mar 2017 21:01:18 +0000 Subject: [PATCH 44/97] kubernetes: Use JSON patch for updating config_map --- .../providers/kubernetes/patch_operations.go | 135 ++++++++++++++++++ .../kubernetes/patch_operations_test.go | 126 ++++++++++++++++ .../resource_kubernetes_config_map.go | 23 +-- builtin/providers/kubernetes/structures.go | 16 +++ 4 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 builtin/providers/kubernetes/patch_operations.go create mode 100644 builtin/providers/kubernetes/patch_operations_test.go diff --git a/builtin/providers/kubernetes/patch_operations.go b/builtin/providers/kubernetes/patch_operations.go new file mode 100644 index 000000000..74965b137 --- /dev/null +++ b/builtin/providers/kubernetes/patch_operations.go @@ -0,0 +1,135 @@ +package kubernetes + +import ( + "encoding/json" + "reflect" + "sort" + "strings" +) + +func diffStringMap(pathPrefix string, oldV, newV map[string]interface{}) PatchOperations { + ops := make([]PatchOperation, 0, 0) + + pathPrefix = strings.TrimRight(pathPrefix, "/") + + // This is suboptimal for adding whole new map from scratch + // or deleting the whole map, but it's actually intention. + // There may be some other map items managed outside of TF + // and we don't want to touch these. + + for k, _ := range oldV { + if _, ok := newV[k]; ok { + continue + } + ops = append(ops, &RemoveOperation{Path: pathPrefix + "/" + k}) + } + + for k, v := range newV { + newValue := v.(string) + + if oldValue, ok := oldV[k].(string); ok { + if oldValue == newValue { + continue + } + + ops = append(ops, &ReplaceOperation{ + Path: pathPrefix + "/" + k, + Value: newValue, + }) + continue + } + + ops = append(ops, &AddOperation{ + Path: pathPrefix + "/" + k, + Value: newValue, + }) + } + + return ops +} + +type PatchOperations []PatchOperation + +func (po PatchOperations) MarshalJSON() ([]byte, error) { + var v []PatchOperation = po + return json.Marshal(v) +} + +func (po PatchOperations) Equal(ops []PatchOperation) bool { + var v []PatchOperation = po + + sort.Slice(v, sortByPathAsc(ops)) + sort.Slice(ops, sortByPathAsc(ops)) + + return reflect.DeepEqual(v, ops) +} + +func sortByPathAsc(ops []PatchOperation) func(i, j int) bool { + return func(i, j int) bool { + return ops[i].GetPath() < ops[j].GetPath() + } +} + +type PatchOperation interface { + MarshalJSON() ([]byte, error) + GetPath() string +} + +type ReplaceOperation struct { + Path string `json:"path"` + Value interface{} `json:"value"` + Op string `json:"op"` +} + +func (o *ReplaceOperation) GetPath() string { + return o.Path +} + +func (o *ReplaceOperation) MarshalJSON() ([]byte, error) { + o.Op = "replace" + return json.Marshal(*o) +} + +func (o *ReplaceOperation) String() string { + b, _ := o.MarshalJSON() + return string(b) +} + +type AddOperation struct { + Path string `json:"path"` + Value interface{} `json:"value"` + Op string `json:"op"` +} + +func (o *AddOperation) GetPath() string { + return o.Path +} + +func (o *AddOperation) MarshalJSON() ([]byte, error) { + o.Op = "add" + return json.Marshal(*o) +} + +func (o *AddOperation) String() string { + b, _ := o.MarshalJSON() + return string(b) +} + +type RemoveOperation struct { + Path string `json:"path"` + Op string `json:"op"` +} + +func (o *RemoveOperation) GetPath() string { + return o.Path +} + +func (o *RemoveOperation) MarshalJSON() ([]byte, error) { + o.Op = "remove" + return json.Marshal(*o) +} + +func (o *RemoveOperation) String() string { + b, _ := o.MarshalJSON() + return string(b) +} diff --git a/builtin/providers/kubernetes/patch_operations_test.go b/builtin/providers/kubernetes/patch_operations_test.go new file mode 100644 index 000000000..c60a5e628 --- /dev/null +++ b/builtin/providers/kubernetes/patch_operations_test.go @@ -0,0 +1,126 @@ +package kubernetes + +import ( + "fmt" + "testing" +) + +func TestDiffStringMap(t *testing.T) { + testCases := []struct { + Path string + Old map[string]interface{} + New map[string]interface{} + ExpectedOps PatchOperations + }{ + { + Path: "/parent/", + Old: map[string]interface{}{ + "one": "111", + "two": "222", + }, + New: map[string]interface{}{ + "one": "111", + "two": "222", + "three": "333", + }, + ExpectedOps: []PatchOperation{ + &AddOperation{ + Path: "/parent/three", + Value: "333", + }, + }, + }, + { + Path: "/parent/", + Old: map[string]interface{}{ + "one": "111", + "two": "222", + }, + New: map[string]interface{}{ + "one": "111", + "two": "abcd", + }, + ExpectedOps: []PatchOperation{ + &ReplaceOperation{ + Path: "/parent/two", + Value: "abcd", + }, + }, + }, + { + Path: "/parent/", + Old: map[string]interface{}{ + "one": "111", + "two": "222", + }, + New: map[string]interface{}{ + "two": "abcd", + "three": "333", + }, + ExpectedOps: []PatchOperation{ + &RemoveOperation{Path: "/parent/one"}, + &ReplaceOperation{ + Path: "/parent/two", + Value: "abcd", + }, + &AddOperation{ + Path: "/parent/three", + Value: "333", + }, + }, + }, + { + Path: "/parent/", + Old: map[string]interface{}{ + "one": "111", + "two": "222", + }, + New: map[string]interface{}{ + "two": "222", + }, + ExpectedOps: []PatchOperation{ + &RemoveOperation{Path: "/parent/one"}, + }, + }, + { + Path: "/parent/", + Old: map[string]interface{}{ + "one": "111", + "two": "222", + }, + New: map[string]interface{}{}, + ExpectedOps: []PatchOperation{ + &RemoveOperation{Path: "/parent/one"}, + &RemoveOperation{Path: "/parent/two"}, + }, + }, + { + Path: "/parent/", + Old: map[string]interface{}{}, + New: map[string]interface{}{ + "one": "111", + "two": "222", + }, + ExpectedOps: []PatchOperation{ + &AddOperation{ + Path: "/parent/one", + Value: "111", + }, + &AddOperation{ + Path: "/parent/two", + Value: "222", + }, + }, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + ops := diffStringMap(tc.Path, tc.Old, tc.New) + if !tc.ExpectedOps.Equal(ops) { + t.Fatalf("Operations don't match.\nExpected: %v\nGiven: %v\n", tc.ExpectedOps, ops) + } + }) + } + +} diff --git a/builtin/providers/kubernetes/resource_kubernetes_config_map.go b/builtin/providers/kubernetes/resource_kubernetes_config_map.go index 460ca638e..d24fa1436 100644 --- a/builtin/providers/kubernetes/resource_kubernetes_config_map.go +++ b/builtin/providers/kubernetes/resource_kubernetes_config_map.go @@ -1,9 +1,11 @@ package kubernetes import ( + "fmt" "log" "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" @@ -73,19 +75,22 @@ func resourceKubernetesConfigMapRead(d *schema.ResourceData, meta interface{}) e func resourceKubernetesConfigMapUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*kubernetes.Clientset) - metadata := expandMetadata(d.Get("metadata").([]interface{})) namespace, name := idParts(d.Id()) - // This is necessary in case the name is generated - metadata.Name = name - cfgMap := api.ConfigMap{ - ObjectMeta: metadata, - Data: expandStringMap(d.Get("data").(map[string]interface{})), + ops := patchMetadata("metadata.0.", "/metadata/", d) + if d.HasChange("data") { + oldV, newV := d.GetChange("data") + diffOps := diffStringMap("/data/", oldV.(map[string]interface{}), newV.(map[string]interface{})) + ops = append(ops, diffOps...) } - log.Printf("[INFO] Updating config map: %#v", cfgMap) - out, err := conn.CoreV1().ConfigMaps(namespace).Update(&cfgMap) + data, err := ops.MarshalJSON() if err != nil { - return err + return fmt.Errorf("Failed to marshal update operations: %s", err) + } + log.Printf("[INFO] Updating config map %q: %v", name, string(data)) + out, err := conn.CoreV1().ConfigMaps(namespace).Patch(name, pkgApi.JSONPatchType, data) + if err != nil { + return fmt.Errorf("Failed to update Config Map: %s", err) } log.Printf("[INFO] Submitted updated config map: %#v", out) d.SetId(buildId(out.ObjectMeta)) diff --git a/builtin/providers/kubernetes/structures.go b/builtin/providers/kubernetes/structures.go index 8b98cee32..2772fc40d 100644 --- a/builtin/providers/kubernetes/structures.go +++ b/builtin/providers/kubernetes/structures.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/terraform/helper/schema" api "k8s.io/kubernetes/pkg/api/v1" ) @@ -39,6 +40,21 @@ func expandMetadata(in []interface{}) api.ObjectMeta { return meta } +func patchMetadata(keyPrefix, pathPrefix string, d *schema.ResourceData) PatchOperations { + ops := make([]PatchOperation, 0, 0) + if d.HasChange(keyPrefix + "annotations") { + oldV, newV := d.GetChange(keyPrefix + "annotations") + diffOps := diffStringMap(pathPrefix+"annotations", oldV.(map[string]interface{}), newV.(map[string]interface{})) + ops = append(ops, diffOps...) + } + if d.HasChange(keyPrefix + "labels") { + oldV, newV := d.GetChange(keyPrefix + "labels") + diffOps := diffStringMap(pathPrefix+"labels", oldV.(map[string]interface{}), newV.(map[string]interface{})) + ops = append(ops, diffOps...) + } + return ops +} + func expandStringMap(m map[string]interface{}) map[string]string { result := make(map[string]string) for k, v := range m { From ac878657a5cb9e35372044e530669f31a631bd00 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 21 Mar 2017 21:04:54 +0000 Subject: [PATCH 45/97] kubernetes: Ignore internal K8S annotations --- builtin/providers/kubernetes/structures.go | 21 +++++++++++- .../providers/kubernetes/structures_test.go | 32 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 builtin/providers/kubernetes/structures_test.go diff --git a/builtin/providers/kubernetes/structures.go b/builtin/providers/kubernetes/structures.go index 2772fc40d..58bc49030 100644 --- a/builtin/providers/kubernetes/structures.go +++ b/builtin/providers/kubernetes/structures.go @@ -2,6 +2,7 @@ package kubernetes import ( "fmt" + "net/url" "strings" "github.com/hashicorp/terraform/helper/schema" @@ -65,7 +66,7 @@ func expandStringMap(m map[string]interface{}) map[string]string { func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} { m := make(map[string]interface{}) - m["annotations"] = meta.Annotations + m["annotations"] = filterAnnotations(meta.Annotations) m["generate_name"] = meta.GenerateName m["labels"] = meta.Labels m["name"] = meta.Name @@ -80,3 +81,21 @@ func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} { return []map[string]interface{}{m} } + +func filterAnnotations(m map[string]string) map[string]string { + for k, _ := range m { + if isInternalAnnotationKey(k) { + delete(m, k) + } + } + return m +} + +func isInternalAnnotationKey(annotationKey string) bool { + u, err := url.Parse("//" + annotationKey) + if err == nil && strings.HasSuffix(u.Hostname(), "kubernetes.io") { + return true + } + + return false +} diff --git a/builtin/providers/kubernetes/structures_test.go b/builtin/providers/kubernetes/structures_test.go new file mode 100644 index 000000000..2a0b9003e --- /dev/null +++ b/builtin/providers/kubernetes/structures_test.go @@ -0,0 +1,32 @@ +package kubernetes + +import ( + "fmt" + "testing" +) + +func TestIsInternalAnnotationKey(t *testing.T) { + testCases := []struct { + Key string + Expected bool + }{ + {"", false}, + {"anyKey", false}, + {"any.hostname.io", false}, + {"any.hostname.com/with/path", false}, + {"any.kubernetes.io", true}, + {"kubernetes.io", true}, + {"pv.kubernetes.io/any/path", true}, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + isInternal := isInternalAnnotationKey(tc.Key) + if tc.Expected && isInternal != tc.Expected { + t.Fatalf("Expected %q to be internal", tc.Key) + } + if !tc.Expected && isInternal != tc.Expected { + t.Fatalf("Expected %q not to be internal", tc.Key) + } + }) + } +} From 066eea4b352c79c315e221307acc7a232a35811e Mon Sep 17 00:00:00 2001 From: stack72 Date: Mon, 27 Mar 2017 18:09:12 +0300 Subject: [PATCH 46/97] provider/vsphere: Randomize the vsphere_virtual_disk acceptance test ``` % make testacc TEST=./builtin/providers/vsphere TESTARGS='-run=TestAccVSphereVirtualDisk_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/27 18:08:08 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/vsphere -v -run=TestAccVSphereVirtualDisk_ -timeout 120m === RUN TestAccVSphereVirtualDisk_basic --- PASS: TestAccVSphereVirtualDisk_basic (8.57s) PASS ok github.com/hashicorp/terraform/builtin/providers/vsphere 8.591s ``` --- .../resource_vsphere_virtual_disk_test.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go b/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go index a24ea597e..d3fef92d0 100644 --- a/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go +++ b/builtin/providers/vsphere/resource_vsphere_virtual_disk_test.go @@ -6,6 +6,7 @@ import ( "os" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" "github.com/vmware/govmomi" @@ -19,6 +20,8 @@ func TestAccVSphereVirtualDisk_basic(t *testing.T) { var initTypeOpt string var adapterTypeOpt string + rString := acctest.RandString(5) + if v := os.Getenv("VSPHERE_DATACENTER"); v != "" { datacenterOpt = v } @@ -39,14 +42,8 @@ func TestAccVSphereVirtualDisk_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckVSphereVirtualDiskDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: fmt.Sprintf( - testAccCheckVSphereVirtuaDiskConfig_basic, - initTypeOpt, - adapterTypeOpt, - datacenterOpt, - datastoreOpt, - ), + { + Config: testAccCheckVSphereVirtuaDiskConfig_basic(rString, initTypeOpt, adapterTypeOpt, datacenterOpt, datastoreOpt), Check: resource.ComposeTestCheckFunc( testAccVSphereVirtualDiskExists("vsphere_virtual_disk.foo"), ), @@ -119,13 +116,15 @@ func testAccCheckVSphereVirtualDiskDestroy(s *terraform.State) error { return nil } -const testAccCheckVSphereVirtuaDiskConfig_basic = ` +func testAccCheckVSphereVirtuaDiskConfig_basic(rName, initTypeOpt, adapterTypeOpt, datacenterOpt, datastoreOpt string) string { + return fmt.Sprintf(` resource "vsphere_virtual_disk" "foo" { size = 1 - vmdk_path = "tfTestDisk.vmdk" + vmdk_path = "tfTestDisk-%s.vmdk" %s %s datacenter = "%s" datastore = "%s" } -` +`, rName, initTypeOpt, adapterTypeOpt, datacenterOpt, datastoreOpt) +} From dc81c9a5b21bef13f2345174d2119af79c89d197 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Mon, 27 Mar 2017 16:42:11 +0100 Subject: [PATCH 47/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 341904373..b4aba87d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ IMPROVEMENTS: * provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config [GH-12743] * provider/google: Add local ssd count support for container clusters [GH-12281] * provider/ignition: ignition_filesystem, explicit option to create the filesystem [GH-12980] + * provider/kubernetes: Internal K8S annotations are ignored in `config_map` [GH-12945] * provider/ns1: Ensure provider checks for credentials [GH-12920] * provider/openstack: Adding Timeouts to Blockstorage Resources [GH-12862] * provider/openstack: Adding Timeouts to FWaaS v1 Resources [GH-12863] From d7243112bef382d2ad0839174f2fad1723e260eb Mon Sep 17 00:00:00 2001 From: Neil Calabroso Date: Mon, 27 Mar 2017 23:47:06 +0800 Subject: [PATCH 48/97] Update compute_network.html.markdown (#13077) Include `name` in the exported attributes of `google_compute_network`'s documentation --- .../docs/providers/google/r/compute_network.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/providers/google/r/compute_network.html.markdown b/website/source/docs/providers/google/r/compute_network.html.markdown index 7cea11ad2..cd03ad2bb 100644 --- a/website/source/docs/providers/google/r/compute_network.html.markdown +++ b/website/source/docs/providers/google/r/compute_network.html.markdown @@ -53,4 +53,6 @@ exported: * `gateway_ipv4` - The IPv4 address of the gateway. +* `name` - The unique name of the network. + * `self_link` - The URI of the created resource. From 43b74cfe7974b8667aa86575ea0061049b9cad3a Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 18:49:11 +0300 Subject: [PATCH 49/97] provider/aws: Support Import of iam_server_certificate (#13065) * Adding import to resource_aws_iam_server_certificate. * provider/aws: Update tests for import of aws_iam_server_certificate Builds upon the work of @mrcopper in #12940 Resource: ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSIAMServerCertificate_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/25 00:08:48 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSIAMServerCertificate_ -timeout 120m === RUN TestAccAWSIAMServerCertificate_importBasic --- PASS: TestAccAWSIAMServerCertificate_importBasic (22.81s) === RUN TestAccAWSIAMServerCertificate_basic --- PASS: TestAccAWSIAMServerCertificate_basic (19.68s) === RUN TestAccAWSIAMServerCertificate_name_prefix --- PASS: TestAccAWSIAMServerCertificate_name_prefix (19.88s) === RUN TestAccAWSIAMServerCertificate_disappears --- PASS: TestAccAWSIAMServerCertificate_disappears (13.94s) === RUN TestAccAWSIAMServerCertificate_file --- PASS: TestAccAWSIAMServerCertificate_file (32.67s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 109.062s ``` Data Source: ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSDataSourceIAMServerCertificate_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/25 13:07:10 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSDataSourceIAMServerCertificate_ -timeout 120m === RUN TestAccAWSDataSourceIAMServerCertificate_basic --- PASS: TestAccAWSDataSourceIAMServerCertificate_basic (43.86s) === RUN TestAccAWSDataSourceIAMServerCertificate_matchNamePrefix --- PASS: TestAccAWSDataSourceIAMServerCertificate_matchNamePrefix (2.68s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 46.569s ``` --- ..._source_aws_iam_server_certificate_test.go | 22 ++++++++---- .../import_aws_iam_server_certificate_test.go | 34 +++++++++++++++++++ .../resource_aws_iam_server_certificate.go | 27 +++++++++++---- ...esource_aws_iam_server_certificate_test.go | 21 ++++++------ .../source/docs/import/importability.html.md | 1 + .../d/iam_server_certificate.html.markdown | 7 ++++ 6 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 builtin/providers/aws/import_aws_iam_server_certificate_test.go diff --git a/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go b/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go index fc6de4303..b840ac115 100644 --- a/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go +++ b/builtin/providers/aws/data_source_aws_iam_server_certificate_test.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) @@ -18,15 +19,15 @@ func timePtr(t time.Time) *time.Time { func TestResourceSortByExpirationDate(t *testing.T) { certs := []*iam.ServerCertificateMetadata{ - &iam.ServerCertificateMetadata{ + { ServerCertificateName: aws.String("oldest"), Expiration: timePtr(time.Now()), }, - &iam.ServerCertificateMetadata{ + { ServerCertificateName: aws.String("latest"), Expiration: timePtr(time.Now().Add(3 * time.Hour)), }, - &iam.ServerCertificateMetadata{ + { ServerCertificateName: aws.String("in between"), Expiration: timePtr(time.Now().Add(2 * time.Hour)), }, @@ -38,13 +39,18 @@ func TestResourceSortByExpirationDate(t *testing.T) { } func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) { + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckIAMServerCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccAwsDataIAMServerCertConfig, + Config: testAccIAMServerCertConfig(rInt), + }, + { + Config: testAccAwsDataIAMServerCertConfig(rInt), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"), resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "arn"), @@ -71,12 +77,16 @@ func TestAccAWSDataSourceIAMServerCertificate_matchNamePrefix(t *testing.T) { }) } -var testAccAwsDataIAMServerCertConfig = fmt.Sprintf(`%s +func testAccAwsDataIAMServerCertConfig(rInt int) string { + return fmt.Sprintf(` +%s + data "aws_iam_server_certificate" "test" { name = "${aws_iam_server_certificate.test_cert.name}" latest = true } -`, testAccIAMServerCertConfig) +`, testAccIAMServerCertConfig(rInt)) +} var testAccAwsDataIAMServerCertConfigMatchNamePrefix = ` data "aws_iam_server_certificate" "test" { diff --git a/builtin/providers/aws/import_aws_iam_server_certificate_test.go b/builtin/providers/aws/import_aws_iam_server_certificate_test.go new file mode 100644 index 000000000..6d342ec76 --- /dev/null +++ b/builtin/providers/aws/import_aws_iam_server_certificate_test.go @@ -0,0 +1,34 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAWSIAMServerCertificate_importBasic(t *testing.T) { + resourceName := "aws_iam_server_certificate.test_cert" + rInt := acctest.RandInt() + resourceId := fmt.Sprintf("terraform-test-cert-%d", rInt) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIAMServerCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccIAMServerCertConfig(rInt), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateId: resourceId, + ImportStateVerifyIgnore: []string{ + "private_key"}, + }, + }, + }) +} diff --git a/builtin/providers/aws/resource_aws_iam_server_certificate.go b/builtin/providers/aws/resource_aws_iam_server_certificate.go index 28258ef15..8b29fbf76 100644 --- a/builtin/providers/aws/resource_aws_iam_server_certificate.go +++ b/builtin/providers/aws/resource_aws_iam_server_certificate.go @@ -20,37 +20,41 @@ func resourceAwsIAMServerCertificate() *schema.Resource { Create: resourceAwsIAMServerCertificateCreate, Read: resourceAwsIAMServerCertificateRead, Delete: resourceAwsIAMServerCertificateDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsIAMServerCertificateImport, + }, Schema: map[string]*schema.Schema{ - "certificate_body": &schema.Schema{ + "certificate_body": { Type: schema.TypeString, Required: true, ForceNew: true, StateFunc: normalizeCert, }, - "certificate_chain": &schema.Schema{ + "certificate_chain": { Type: schema.TypeString, Optional: true, ForceNew: true, StateFunc: normalizeCert, }, - "path": &schema.Schema{ + "path": { Type: schema.TypeString, Optional: true, Default: "/", ForceNew: true, }, - "private_key": &schema.Schema{ + "private_key": { Type: schema.TypeString, Required: true, ForceNew: true, StateFunc: normalizeCert, + Sensitive: true, }, - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Optional: true, Computed: true, @@ -66,7 +70,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource { }, }, - "name_prefix": &schema.Schema{ + "name_prefix": { Type: schema.TypeString, Optional: true, ForceNew: true, @@ -80,7 +84,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource { }, }, - "arn": &schema.Schema{ + "arn": { Type: schema.TypeString, Optional: true, Computed: true, @@ -148,6 +152,8 @@ func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s", err) } + d.SetId(*resp.ServerCertificate.ServerCertificateMetadata.ServerCertificateId) + // these values should always be present, and have a default if not set in // configuration, and so safe to reference with nil checks d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody)) @@ -196,6 +202,13 @@ func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interfac return nil } +func resourceAwsIAMServerCertificateImport( + d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set("name", d.Id()) + // private_key can't be fetched from any API call + return []*schema.ResourceData{d}, nil +} + func normalizeCert(cert interface{}) string { if cert == nil || cert == (*string)(nil) { return "" diff --git a/builtin/providers/aws/resource_aws_iam_server_certificate_test.go b/builtin/providers/aws/resource_aws_iam_server_certificate_test.go index 11b109296..1dad7b829 100644 --- a/builtin/providers/aws/resource_aws_iam_server_certificate_test.go +++ b/builtin/providers/aws/resource_aws_iam_server_certificate_test.go @@ -2,10 +2,8 @@ package aws import ( "fmt" - "math/rand" "strings" "testing" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" @@ -16,14 +14,15 @@ import ( func TestAccAWSIAMServerCertificate_basic(t *testing.T) { var cert iam.ServerCertificate + rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckIAMServerCertificateDestroy, Steps: []resource.TestStep{ - resource.TestStep{ - Config: testAccIAMServerCertConfig, + { + Config: testAccIAMServerCertConfig(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), testAccCheckAWSServerCertAttributes(&cert), @@ -41,7 +40,7 @@ func TestAccAWSIAMServerCertificate_name_prefix(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckIAMServerCertificateDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccIAMServerCertConfig_random, Check: resource.ComposeTestCheckFunc( testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), @@ -74,7 +73,7 @@ func TestAccAWSIAMServerCertificate_disappears(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckIAMServerCertificateDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccIAMServerCertConfig_random, Check: resource.ComposeTestCheckFunc( testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), @@ -97,7 +96,7 @@ func TestAccAWSIAMServerCertificate_file(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckIAMServerCertificateDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-unix-line-endings"), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), @@ -105,7 +104,7 @@ func TestAccAWSIAMServerCertificate_file(t *testing.T) { ), }, - resource.TestStep{ + { Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-windows-line-endings"), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert), @@ -202,7 +201,8 @@ CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4 GnSud83VUo9G9w== -----END CERTIFICATE-----`) -var testAccIAMServerCertConfig = fmt.Sprintf(` +func testAccIAMServerCertConfig(rInt int) string { + return fmt.Sprintf(` resource "aws_iam_server_certificate" "test_cert" { name = "terraform-test-cert-%d" certificate_body = < Date: Mon, 27 Mar 2017 18:49:47 +0300 Subject: [PATCH 50/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4aba87d6..ff5751e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ IMPROVEMENTS: * provider/aws: Added aws_api_gateway_api_key value attribute [GH-9462] * provider/aws: Allow aws_alb subnets to change [GH-12850] * provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups [GH-12855] + * provider/aws: Support Import of iam_server_certificate [GH-13065] * provider/azurerm: Add support for setting the primary network interface [GH-11290] * provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource [GH-11306] * provider/consul: Add support for basic auth to the provider [GH-12679] From 316013e2ba0bbf51ea709fde484219945b4bf2b4 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 19:59:09 +0300 Subject: [PATCH 51/97] provider/packet|digitalocean: Randomize the SSH Key acceptance tests (#13096) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` % make testacc TEST=./builtin/providers/packet TESTARGS='-run=TestAccPacketSSHKey_Basic' ✭ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/27 18:56:06 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/packet -v -run=TestAccPacketSSHKey_Basic -timeout 120m === RUN TestAccPacketSSHKey_Basic --- PASS: TestAccPacketSSHKey_Basic (5.30s) PASS ok github.com/hashicorp/terraform/builtin/providers/packet 5.316s ``` ``` % make testacc TEST=./builtin/providers/digitalocean TESTARGS='-run=TestAccDigitalOceanSSHKey_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/27 19:29:01 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/digitalocean -v -run=TestAccDigitalOceanSSHKey_ -timeout 120m === RUN TestAccDigitalOceanSSHKey_importBasic --- PASS: TestAccDigitalOceanSSHKey_importBasic (4.18s) === RUN TestAccDigitalOceanSSHKey_Basic --- PASS: TestAccDigitalOceanSSHKey_Basic (2.77s) PASS ok github.com/hashicorp/terraform/builtin/providers/digitalocean 6.991s ``` --- .../import_digitalocean_ssh_key_test.go | 8 ++++- .../resource_digitalocean_droplet_test.go | 2 ++ .../resource_digitalocean_ssh_key_test.go | 34 +++++++------------ .../packet/resource_packet_ssh_key_test.go | 17 +++++----- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/builtin/providers/digitalocean/import_digitalocean_ssh_key_test.go b/builtin/providers/digitalocean/import_digitalocean_ssh_key_test.go index f579d7ef5..7c93f0076 100644 --- a/builtin/providers/digitalocean/import_digitalocean_ssh_key_test.go +++ b/builtin/providers/digitalocean/import_digitalocean_ssh_key_test.go @@ -3,11 +3,17 @@ package digitalocean import ( "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) { resourceName := "digitalocean_ssh_key.foobar" + rInt := acctest.RandInt() + publicKeyMaterial, _, err := acctest.RandSSHKeyPair("digitalocean@ssh-acceptance-test") + if err != nil { + t.Fatalf("Cannot generate test SSH key pair: %s", err) + } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -15,7 +21,7 @@ func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) { CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckDigitalOceanSSHKeyConfig_basic(testAccValidImportPublicKey), + Config: testAccCheckDigitalOceanSSHKeyConfig_basic(rInt, publicKeyMaterial), }, { diff --git a/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go b/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go index 8b8a90efa..1c3d5601c 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go @@ -583,3 +583,5 @@ resource "digitalocean_droplet" "foobar" { } `, rInt) } + +var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR` diff --git a/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go b/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go index b348a89d1..043f667b7 100644 --- a/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go +++ b/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go @@ -3,16 +3,21 @@ package digitalocean import ( "fmt" "strconv" - "strings" "testing" "github.com/digitalocean/godo" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) func TestAccDigitalOceanSSHKey_Basic(t *testing.T) { var key godo.Key + rInt := acctest.RandInt() + publicKeyMaterial, _, err := acctest.RandSSHKeyPair("digitalocean@ssh-acceptance-test") + if err != nil { + t.Fatalf("Cannot generate test SSH key pair: %s", err) + } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -20,14 +25,13 @@ func TestAccDigitalOceanSSHKey_Basic(t *testing.T) { CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckDigitalOceanSSHKeyConfig_basic(testAccValidPublicKey), + Config: testAccCheckDigitalOceanSSHKeyConfig_basic(rInt, publicKeyMaterial), Check: resource.ComposeTestCheckFunc( testAccCheckDigitalOceanSSHKeyExists("digitalocean_ssh_key.foobar", &key), - testAccCheckDigitalOceanSSHKeyAttributes(&key), resource.TestCheckResourceAttr( - "digitalocean_ssh_key.foobar", "name", "foobar"), + "digitalocean_ssh_key.foobar", "name", fmt.Sprintf("foobar-%d", rInt)), resource.TestCheckResourceAttr( - "digitalocean_ssh_key.foobar", "public_key", strings.TrimSpace(testAccValidPublicKey)), + "digitalocean_ssh_key.foobar", "public_key", publicKeyMaterial), ), }, }, @@ -58,17 +62,6 @@ func testAccCheckDigitalOceanSSHKeyDestroy(s *terraform.State) error { return nil } -func testAccCheckDigitalOceanSSHKeyAttributes(key *godo.Key) resource.TestCheckFunc { - return func(s *terraform.State) error { - - if key.Name != "foobar" { - return fmt.Errorf("Bad name: %s", key.Name) - } - - return nil - } -} - func testAccCheckDigitalOceanSSHKeyExists(n string, key *godo.Key) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -105,13 +98,10 @@ func testAccCheckDigitalOceanSSHKeyExists(n string, key *godo.Key) resource.Test } } -func testAccCheckDigitalOceanSSHKeyConfig_basic(key string) string { +func testAccCheckDigitalOceanSSHKeyConfig_basic(rInt int, key string) string { return fmt.Sprintf(` resource "digitalocean_ssh_key" "foobar" { - name = "foobar" + name = "foobar-%d" public_key = "%s" -}`, key) +}`, rInt, key) } - -var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR` -var testAccValidImportPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwelf/LV8TKOd6ZCcDwU9L8YRdVwfR2q8E+Bzamcxwb1U41vnfyvEZbzx0aeXimdHipOql0SG2tu9Z+bzekROVc13OP/gtGRlWwZ9RoKE8hFHanhi0K2tC6OWagsvmHpW/xptsYAo2k+eRJJo0iy/hLNG2c1v5rrjg6xwnSL3+a7bFM4xNDux5sNYCmxIBfIL+4rQ8XBlxsjMrGoev/uumZ0yc75JtBCOSZbdie936pvVmoAf4nhxNbe5lOxp+18zHhBbO2fjhux4xmf4hLM2gHsdBGqtnphzLh3d1+uMIpv7ZMTKN7pBw53xQxw7hhDYuNKc8FkQ8xK6IL5bu/Ar/` diff --git a/builtin/providers/packet/resource_packet_ssh_key_test.go b/builtin/providers/packet/resource_packet_ssh_key_test.go index cfd85ae1a..5f019d428 100644 --- a/builtin/providers/packet/resource_packet_ssh_key_test.go +++ b/builtin/providers/packet/resource_packet_ssh_key_test.go @@ -2,7 +2,6 @@ package packet import ( "fmt" - "strings" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -14,6 +13,10 @@ import ( func TestAccPacketSSHKey_Basic(t *testing.T) { var key packngo.SSHKey rInt := acctest.RandInt() + publicKeyMaterial, _, err := acctest.RandSSHKeyPair("") + if err != nil { + t.Fatalf("Cannot generate test SSH key pair: %s", err) + } resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,13 +24,13 @@ func TestAccPacketSSHKey_Basic(t *testing.T) { CheckDestroy: testAccCheckPacketSSHKeyDestroy, Steps: []resource.TestStep{ { - Config: testAccCheckPacketSSHKeyConfig_basic(rInt), + Config: testAccCheckPacketSSHKeyConfig_basic(rInt, publicKeyMaterial), Check: resource.ComposeTestCheckFunc( testAccCheckPacketSSHKeyExists("packet_ssh_key.foobar", &key), resource.TestCheckResourceAttr( "packet_ssh_key.foobar", "name", fmt.Sprintf("foobar-%d", rInt)), resource.TestCheckResourceAttr( - "packet_ssh_key.foobar", "public_key", testAccValidPublicKey), + "packet_ssh_key.foobar", "public_key", publicKeyMaterial), ), }, }, @@ -76,14 +79,10 @@ func testAccCheckPacketSSHKeyExists(n string, key *packngo.SSHKey) resource.Test } } -func testAccCheckPacketSSHKeyConfig_basic(rInt int) string { +func testAccCheckPacketSSHKeyConfig_basic(rInt int, publicSshKey string) string { return fmt.Sprintf(` resource "packet_ssh_key" "foobar" { name = "foobar-%d" public_key = "%s" -}`, rInt, testAccValidPublicKey) +}`, rInt, publicSshKey) } - -var testAccValidPublicKey = strings.TrimSpace(` -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR -`) From 7199a58ce7d7aa0a15c2aa836098678a9ec2b0fe Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 Mar 2017 11:09:18 -0600 Subject: [PATCH 52/97] provider/openstack: Adding all_metadata attribute (#13061) This commit adds the all_metadata attribute which contains all instance metadata returned from the OpenStack Compute API. This is useful for gaining access to metadata which was applied out of band of Terraform. This commit also stops setting the original metadata attribute, effectively making metadata an input and all_metadata the output to reference all data from. --- .../resource_openstack_compute_instance_v2.go | 6 +++++- .../resource_openstack_compute_instance_v2_test.go | 10 ++++++++++ .../openstack/r/compute_instance_v2.html.markdown | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go index f3d492162..5a60b55c5 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2.go @@ -338,6 +338,10 @@ func resourceComputeInstanceV2() *schema.Resource { Optional: true, Default: false, }, + "all_metadata": &schema.Schema{ + Type: schema.TypeMap, + Computed: true, + }, }, } } @@ -557,7 +561,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err }) } - d.Set("metadata", server.Metadata) + d.Set("all_metadata", server.Metadata) secGrpNames := []string{} for _, sg := range server.SecurityGroups { diff --git a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go index cfe807e4f..cf43ee4bb 100644 --- a/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_compute_instance_v2_test.go @@ -29,6 +29,8 @@ func TestAccComputeV2Instance_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance), testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), + resource.TestCheckResourceAttr( + "openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"), resource.TestCheckResourceAttr( "openstack_compute_instance_v2.instance_1", "availability_zone", "nova"), ), @@ -607,6 +609,10 @@ func TestAccComputeV2Instance_metadataRemove(t *testing.T) { testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance), testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), testAccCheckComputeV2InstanceMetadata(&instance, "abc", "def"), + resource.TestCheckResourceAttr( + "openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"), + resource.TestCheckResourceAttr( + "openstack_compute_instance_v2.instance_1", "all_metadata.abc", "def"), ), }, resource.TestStep{ @@ -616,6 +622,10 @@ func TestAccComputeV2Instance_metadataRemove(t *testing.T) { testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"), testAccCheckComputeV2InstanceMetadata(&instance, "ghi", "jkl"), testAccCheckComputeV2InstanceNoMetadataKey(&instance, "abc"), + resource.TestCheckResourceAttr( + "openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"), + resource.TestCheckResourceAttr( + "openstack_compute_instance_v2.instance_1", "all_metadata.ghi", "jkl"), ), }, }, diff --git a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown index 5445bf944..7466b4301 100644 --- a/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/compute_instance_v2.html.markdown @@ -449,6 +449,8 @@ The following attributes are exported: * `network/floating_ip` - The Floating IP address of the Instance on that network. * `network/mac` - The MAC address of the NIC on that network. +* `all_metadata` - Contains all instance metadata, even metadata not set + by Terraform. ## Notes From 9848d14bc92bb36d8e0fdb1078249f0bf8e89914 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 20:10:12 +0300 Subject: [PATCH 53/97] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff5751e18..617912f3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ IMPROVEMENTS: * provider/openstack: Don't log the catalog [GH-13075] * provider/openstack: Handle 409/500 Response on Pool Create [GH-13074] * provider/pagerduty: Validate credentials [GH-12854] + * provider/openstack: Adding all_metadata attribute [GH-13061] * provider/profitbricks: Handling missing resources [GH-13053] BUG FIXES: From 77a41ca8596732a72ad79e89c916b22a42d814a2 Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Mon, 27 Mar 2017 11:22:56 -0600 Subject: [PATCH 54/97] provider/openstack: Resolve issues with Port Fixed IPs (#13056) * provider/openstack: Add all_fixed_ips computed attribute to port resource This commit adds the `all_fixed_ips` attribute to the openstack_networking_port_v2 resource. This contains all of the port's Fixed IPs returned by the Networking v2 API. * provider/openstack: Revert Port fixed_ip back to a List This commit reverts the changes made in a8c4e81a6e3f2. This re-enables the ability to reference IP addresses using the numerical-element notation. This commit also makes fixed_ip a non-computed field, meaning Terraform will no longer set fixed_ip with what was returned by the API. This resolves the original issue about ordering. The last use-case is for fixed_ips that received an IP address via DHCP. Because fixed_ip is no longer computed, the DHCP IP will not be set. The workaround for this use-case is to use the new all_fixed_ips attribute. In effect, users should use fixed_ip only as a way of inputting data into Terraform and use all_fixed_ips as the output returned by the API. If use-cases exist where fixed_ip can be used as an output, that's a bonus feature. --- .../resource_openstack_networking_port_v2.go | 24 ++-- ...ource_openstack_networking_port_v2_test.go | 116 ++++++++++++++++++ .../r/networking_port_v2.html.markdown | 4 +- 3 files changed, 132 insertions(+), 12 deletions(-) diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2.go b/builtin/providers/openstack/resource_openstack_networking_port_v2.go index aea9cb8dd..508ebc813 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2.go @@ -85,10 +85,9 @@ func resourceNetworkingPortV2() *schema.Resource { Computed: true, }, "fixed_ip": &schema.Schema{ - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, ForceNew: false, - Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "subnet_id": &schema.Schema{ @@ -98,7 +97,6 @@ func resourceNetworkingPortV2() *schema.Resource { "ip_address": &schema.Schema{ Type: schema.TypeString, Optional: true, - Computed: true, }, }, }, @@ -128,6 +126,11 @@ func resourceNetworkingPortV2() *schema.Resource { Optional: true, ForceNew: true, }, + "all_fixed_ips": &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, } } @@ -202,15 +205,14 @@ func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) erro d.Set("security_group_ids", p.SecurityGroups) d.Set("device_id", p.DeviceID) - // Convert FixedIPs to list of map - var ips []map[string]interface{} + // Create a slice of all returned Fixed IPs. + // This will be in the order returned by the API, + // which is usually alpha-numeric. + var ips []string for _, ipObject := range p.FixedIPs { - ip := make(map[string]interface{}) - ip["subnet_id"] = ipObject.SubnetID - ip["ip_address"] = ipObject.IPAddress - ips = append(ips, ip) + ips = append(ips, ipObject.IPAddress) } - d.Set("fixed_ip", ips) + d.Set("all_fixed_ips", ips) // Convert AllowedAddressPairs to list of map var pairs []map[string]interface{} @@ -309,7 +311,7 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string { } func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} { - rawIP := d.Get("fixed_ip").(*schema.Set).List() + rawIP := d.Get("fixed_ip").([]interface{}) if len(rawIP) == 0 { return nil diff --git a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go index ec731fe7a..28e08bebd 100644 --- a/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go +++ b/builtin/providers/openstack/resource_openstack_networking_port_v2_test.go @@ -50,6 +50,30 @@ func TestAccNetworkingV2Port_noip(t *testing.T) { testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 1), + ), + }, + }, + }) +} + +func TestAccNetworkingV2Port_multipleNoIP(t *testing.T) { + var network networks.Network + var port ports.Port + var subnet subnets.Subnet + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2PortDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Port_multipleNoIP, + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), + testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), + testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 3), ), }, }, @@ -96,6 +120,7 @@ func TestAccNetworkingV2Port_multipleFixedIPs(t *testing.T) { testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet), testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network), testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port), + testAccCheckNetworkingV2PortCountFixedIPs(&port, 3), ), }, }, @@ -124,6 +149,25 @@ func TestAccNetworkingV2Port_timeout(t *testing.T) { }) } +func TestAccNetworkingV2Port_fixedIPs(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkingV2PortDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccNetworkingV2Port_fixedIPs, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "openstack_networking_port_v2.port_1", "all_fixed_ips.0", "192.168.199.23"), + resource.TestCheckResourceAttr( + "openstack_networking_port_v2.port_1", "all_fixed_ips.1", "192.168.199.24"), + ), + }, + }, + }) +} + func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) networkingClient, err := config.networkingV2Client(OS_REGION_NAME) @@ -177,6 +221,16 @@ func testAccCheckNetworkingV2PortExists(n string, port *ports.Port) resource.Tes } } +func testAccCheckNetworkingV2PortCountFixedIPs(port *ports.Port, expected int) resource.TestCheckFunc { + return func(s *terraform.State) error { + if len(port.FixedIPs) != expected { + return fmt.Errorf("Expected %d Fixed IPs, got %d", expected, len(port.FixedIPs)) + } + + return nil + } +} + const testAccNetworkingV2Port_basic = ` resource "openstack_networking_network_v2" "network_1" { name = "network_1" @@ -226,6 +280,38 @@ resource "openstack_networking_port_v2" "port_1" { } ` +const testAccNetworkingV2Port_multipleNoIP = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_networking_port_v2" "port_1" { + name = "port_1" + admin_state_up = "true" + network_id = "${openstack_networking_network_v2.network_1.id}" + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + } +} +` + const testAccNetworkingV2Port_allowedAddressPairs = ` resource "openstack_networking_network_v2" "vrrp_network" { name = "vrrp_network" @@ -356,3 +442,33 @@ resource "openstack_networking_port_v2" "port_1" { } } ` + +const testAccNetworkingV2Port_fixedIPs = ` +resource "openstack_networking_network_v2" "network_1" { + name = "network_1" + admin_state_up = "true" +} + +resource "openstack_networking_subnet_v2" "subnet_1" { + name = "subnet_1" + cidr = "192.168.199.0/24" + ip_version = 4 + network_id = "${openstack_networking_network_v2.network_1.id}" +} + +resource "openstack_networking_port_v2" "port_1" { + name = "port_1" + admin_state_up = "true" + network_id = "${openstack_networking_network_v2.network_1.id}" + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.24" + } + + fixed_ip { + subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}" + ip_address = "192.168.199.23" + } +} +` diff --git a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown index 54a92fb78..9cb62ac40 100644 --- a/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown +++ b/website/source/docs/providers/openstack/r/networking_port_v2.html.markdown @@ -95,7 +95,9 @@ The following attributes are exported: * `device_owner` - See Argument Reference above. * `security_group_ids` - See Argument Reference above. * `device_id` - See Argument Reference above. -* `fixed_ip/ip_address` - See Argument Reference above. +* `fixed_ip` - See Argument Reference above. +* `all fixed_ips` - The collection of Fixed IP addresses on the port in the + order returned by the Network v2 API. ## Import From 7db042a2a6a5849e84a8fbedc8611931fc9fc72f Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Mon, 27 Mar 2017 20:24:15 +0300 Subject: [PATCH 55/97] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 617912f3c..537910a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.9.2 (unreleased) +BACKWARDS IMCOMPATIBILITIES / NOTES: + + * provider/openstack: Port Fixed IPs are able to be read again using the original numerical notation. However, Fixed IP configurations which are obtaining addresses via DHCP must now use the `all_fixed_ips` attribute to reference the returned IP address. + FEATURES: * **New Resource:** `alicloud_db_instance` [GH-12913] @@ -67,6 +71,7 @@ BUG FIXES: * provider/google: turn compute_instance_group.instances into a set [GH-12790] * provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791] * provider/openstack: Fix monitor_id typo in LBaaS v1 Pool [GH-13069] + * provider/openstack: Resolve issues with Port Fixed IPs [GH-13056] * provider/scaleway: work around parallel request limitation [GH-13045] ## 0.9.1 (March 17, 2017) From ae5903a10332ef11b493ed4c6439bfbb1dd4645b Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 11:25:43 -0600 Subject: [PATCH 56/97] Fixes TestAccAWSOpsworksInstance --- .../aws/resource_aws_opsworks_instance.go | 5 ++++- builtin/providers/aws/structure.go | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/builtin/providers/aws/resource_aws_opsworks_instance.go b/builtin/providers/aws/resource_aws_opsworks_instance.go index 0e42e30f0..2b2b51c49 100644 --- a/builtin/providers/aws/resource_aws_opsworks_instance.go +++ b/builtin/providers/aws/resource_aws_opsworks_instance.go @@ -565,6 +565,10 @@ func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) e for _, v := range instance.LayerIds { layerIds = append(layerIds, *v) } + layerIds, err = sortListBasedonTFFile(layerIds, d, "layer_ids") + if err != nil { + return fmt.Errorf("[DEBUG] Error sorting layer_ids attribute: %#v", err) + } if err := d.Set("layer_ids", layerIds); err != nil { return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err) } @@ -820,7 +824,6 @@ func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{}) if v, ok := d.GetOk("layer_ids"); ok { req.LayerIds = expandStringList(v.([]interface{})) - } if v, ok := d.GetOk("os"); ok { diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index 302571c6c..dfe053b9a 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -1490,6 +1490,22 @@ func sortInterfaceSlice(in []interface{}) []interface{} { return b } +// This function sorts List A to look like a list found in the tf file. +func sortListBasedonTFFile(in []string, d *schema.ResourceData, listName string) ([]string, error) { + if attributeCount, ok := d.Get(listName + ".#").(int); ok { + for i := 0; i < attributeCount; i++ { + currAttributeId := d.Get(listName + "." + strconv.Itoa(i)) + for j := 0; j < len(in); j++ { + if currAttributeId == in[j] { + in[i], in[j] = in[j], in[i] + } + } + } + return in, nil + } + return in, fmt.Errorf("Could not find list: %s", listName) +} + func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} { result := make([]map[string]interface{}, 0, 1) From dfb34c85b923b27e4b6ee9dad5a8778971e37ee3 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 14:02:20 -0600 Subject: [PATCH 57/97] 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 7cbbe4232..5a5bd1ec8 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 378e2042c..624ce0c83 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 58/97] 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 f5770fcc4..d39c4c03f 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 072a34d5008c53fedd894c3125cc235fcc4b7610 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Mon, 27 Mar 2017 13:28:26 -0700 Subject: [PATCH 59/97] Fix broken link --- .../source/docs/providers/google/r/google_project.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/source/docs/providers/google/r/google_project.html.markdown b/website/source/docs/providers/google/r/google_project.html.markdown index ad668b672..b90af0aa2 100755 --- a/website/source/docs/providers/google/r/google_project.html.markdown +++ b/website/source/docs/providers/google/r/google_project.html.markdown @@ -12,7 +12,7 @@ Allows creation and management of a Google Cloud Platform project and its associated enabled services/APIs. Projects created with this resource must be associated with an Organization. -See the [Organization documentation](https://cloud.google.com/resource-manager/docs/quickstart) for more details. +See the [Organization documentation](https://cloud.google.com/resource-manager/docs/quickstarts) for more details. The service account used to run Terraform when creating a `google_project` resource must have `roles/resourcemanager.projectCreator`. See the From 8027fe9e08a9ac3d2f63aa093c27c3e18f15059f Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 17:11:26 -0400 Subject: [PATCH 60/97] Don't Validate if we have an execution plan The plan file should contain all data required to execute the apply operation. Validation requires interpolation, and the `file()` interpolation function may fail if the module files are not present. This is the case currently with how TFE executes plans. --- command/meta_backend.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/meta_backend.go b/command/meta_backend.go index 5019c0242..b30659dc0 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -104,9 +104,13 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) { StateBackupPath: m.backupPath, ContextOpts: m.contextOpts(), Input: m.Input(), - Validation: true, } + // Don't validate if we have a plan. Validation is normally harmless here, + // but validation requires interpolation, and `file()` function calls may + // not have the original files in the current execution context. + cliOpts.Validation = opts.Plan == nil + // If the backend supports CLI initialization, do it. if cli, ok := b.(backend.CLI); ok { if err := cli.CLIInit(cliOpts); err != nil { From 5ea834d27fe58ea549a4f985a68413fd31d1b336 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 27 Mar 2017 15:22:58 -0600 Subject: [PATCH 61/97] 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 d39c4c03f..64d04f923 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 9d118325b36764867e94e16e43de39feeb1c04da Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 16:16:09 -0400 Subject: [PATCH 62/97] Reject names that aren't url-safe Environment names can be used in a number of contexts, and should be properly escaped for safety. Since most state names are store in path structures, and often in a URL, use `url.PathEscape` to check for disallowed characters --- command/env_command.go | 17 ++++++++++++++++- command/env_command_test.go | 38 +++++++++++++++++++++++++++++++++++++ command/env_delete.go | 5 +++++ command/env_new.go | 5 +++++ command/env_select.go | 4 ++++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/command/env_command.go b/command/env_command.go index 481c0092c..9548122a1 100644 --- a/command/env_command.go +++ b/command/env_command.go @@ -1,6 +1,9 @@ package command -import "strings" +import ( + "net/url" + "strings" +) // EnvCommand is a Command Implementation that manipulates local state // environments. @@ -39,6 +42,13 @@ func (c *EnvCommand) Synopsis() string { return "Environment management" } +// validEnvName returns true is this name is valid to use as an environment name. +// Since most named states are accessed via a filesystem path or URL, check if +// escaping the name would be required. +func validEnvName(name string) bool { + return name == url.PathEscape(name) +} + const ( envNotSupported = `Backend does not support environments` @@ -81,5 +91,10 @@ Environment %[1]q is your active environment! You cannot delete the currently active environment. Please switch to another environment and try again. +` + + envInvalidName = ` +The environment name %q is not allowed. The name must contain only URL safe +characters, and no path separators. ` ) diff --git a/command/env_command_test.go b/command/env_command_test.go index 0b28beb01..5a04d8db0 100644 --- a/command/env_command_test.go +++ b/command/env_command_test.go @@ -103,6 +103,44 @@ func TestEnv_createAndList(t *testing.T) { } } +// Don't allow names that aren't URL safe +func TestEnv_createInvalid(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + os.MkdirAll(td, 0755) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + newCmd := &EnvNewCommand{} + + envs := []string{"test_a*", "test_b/foo", "../../../test_c", "好_d"} + + // create multiple envs + for _, env := range envs { + ui := new(cli.MockUi) + newCmd.Meta = Meta{Ui: ui} + if code := newCmd.Run([]string{env}); code == 0 { + t.Fatalf("expected failure: \n%s", ui.OutputWriter) + } + } + + // list envs to make sure none were created + listCmd := &EnvListCommand{} + ui := new(cli.MockUi) + listCmd.Meta = Meta{Ui: ui} + + if code := listCmd.Run(nil); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter) + } + + actual := strings.TrimSpace(ui.OutputWriter.String()) + expected := "* default" + + if actual != expected { + t.Fatalf("\nexpected: %q\nactual: %q", expected, actual) + } +} + func TestEnv_createWithState(t *testing.T) { td := tempDir(t) os.MkdirAll(td, 0755) diff --git a/command/env_delete.go b/command/env_delete.go index a9958f8aa..be21b76ed 100644 --- a/command/env_delete.go +++ b/command/env_delete.go @@ -32,6 +32,11 @@ func (c *EnvDeleteCommand) Run(args []string) int { delEnv := args[0] + if !validEnvName(delEnv) { + c.Ui.Error(fmt.Sprintf(envInvalidName, delEnv)) + return 1 + } + configPath, err := ModulePath(args[1:]) if err != nil { c.Ui.Error(err.Error()) diff --git a/command/env_new.go b/command/env_new.go index 5f4999e24..8b0e8fcdb 100644 --- a/command/env_new.go +++ b/command/env_new.go @@ -35,6 +35,11 @@ func (c *EnvNewCommand) Run(args []string) int { newEnv := args[0] + if !validEnvName(newEnv) { + c.Ui.Error(fmt.Sprintf(envInvalidName, newEnv)) + return 1 + } + configPath, err := ModulePath(args[1:]) if err != nil { c.Ui.Error(err.Error()) diff --git a/command/env_select.go b/command/env_select.go index 073f92ac5..e7bc8743e 100644 --- a/command/env_select.go +++ b/command/env_select.go @@ -39,6 +39,10 @@ func (c *EnvSelectCommand) Run(args []string) int { } name := args[0] + if !validEnvName(name) { + c.Ui.Error(fmt.Sprintf(envInvalidName, name)) + return 1 + } states, err := b.States() if err != nil { From 2cffa25235b07ef997821b54634e31c3f6fba5ab Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 18:39:18 -0400 Subject: [PATCH 63/97] Add test to verify that Validation isn't called The apply won't succeed because we don't have a valid plan, but this verifies that providing a plan file prevents Validation. --- command/apply_test.go | 33 +++++++++++++++++++ .../apply-plan-no-module/main.tf | 7 ++++ 2 files changed, 40 insertions(+) create mode 100644 command/test-fixtures/apply-plan-no-module/main.tf diff --git a/command/apply_test.go b/command/apply_test.go index 01c230326..661d88c76 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -802,6 +802,39 @@ func TestApply_planVars(t *testing.T) { } } +// we should be able to apply a plan file with no other file dependencies +func TestApply_planNoModuleFiles(t *testing.T) { + // temprary data directory which we can remove between commands + td, err := ioutil.TempDir("", "tf") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + + defer testChdir(t, td)() + + p := testProvider() + planFile := testPlanFile(t, &terraform.Plan{ + Module: testModule(t, "apply-plan-no-module"), + }) + + contextOpts := testCtxConfig(p) + + apply := &ApplyCommand{ + Meta: Meta{ + ContextOpts: contextOpts, + Ui: new(cli.MockUi), + }, + } + args := []string{ + planFile, + } + apply.Run(args) + if p.ValidateCalled { + t.Fatal("Validate should not be called with a plan") + } +} + func TestApply_refresh(t *testing.T) { originalState := &terraform.State{ Modules: []*terraform.ModuleState{ diff --git a/command/test-fixtures/apply-plan-no-module/main.tf b/command/test-fixtures/apply-plan-no-module/main.tf new file mode 100644 index 000000000..deea30d66 --- /dev/null +++ b/command/test-fixtures/apply-plan-no-module/main.tf @@ -0,0 +1,7 @@ +resource "test_instance" "tmpl" { + foo = "${file("${path.module}/template.txt")}" +} + +output "template" { + value = "${test_instance.tmpl.foo}" +} From 5d734cee4eea6171fbf496cff456aaf80106905d Mon Sep 17 00:00:00 2001 From: James Nugent Date: Wed, 22 Mar 2017 17:53:49 -0500 Subject: [PATCH 64/97] helper/acctest: Add RandIntRange helper function This commit adds a new RandIntRange function to the helper/acctest package for generating random integers in a given range. This is useful when randomizing test spaces with a constrained range (e.g. 0-4095). --- helper/acctest/random.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helper/acctest/random.go b/helper/acctest/random.go index 1a6fc8d19..637b11865 100644 --- a/helper/acctest/random.go +++ b/helper/acctest/random.go @@ -24,6 +24,14 @@ func RandInt() int { return rand.New(rand.NewSource(time.Now().UnixNano())).Int() } +func RandIntRange(min int, max int) int { + reseed() + source := rand.New(rand.NewSource(time.Now().UnixNano())) + rangeMax := max - min + + return int(source.Int31n(int32(rangeMax))) +} + // RandString generates a random alphanumeric string of the length specified func RandString(strlen int) string { return RandStringFromCharSet(strlen, CharSetAlphaNum) From b66abacc805ab8cafa04833409691c18bd7c1edf Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 22:37:59 -0400 Subject: [PATCH 65/97] update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 537910a9d..bd6fb9926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ BACKWARDS IMCOMPATIBILITIES / NOTES: * provider/openstack: Port Fixed IPs are able to be read again using the original numerical notation. However, Fixed IP configurations which are obtaining addresses via DHCP must now use the `all_fixed_ips` attribute to reference the returned IP address. + * Environment names must be safe to use as a URL path segment without escaping, and is enforced by the CLI. FEATURES: @@ -12,9 +13,11 @@ FEATURES: * **New Resource:** `github_repository_webhook` [GH-12924] * **New Resource:** `random_pet` [GH-12903] * **New Interpolation:** `substr` [GH-12870] + * **S3 Environments:** The S3 remote state backend now supports named environments IMPROVEMENTS: + * core: fix interpolation error when referencing computed values from an `aws_instance` `cidr_block` [GH-13046] * core: fix `ignore_changes` causing fields to be removed during apply [GH-12897] * core: add `-force-copy` option to `terraform init` to supress prompts for copying state [GH-12939] * helper/acctest: Add NewSSHKeyPair function [GH-12894] From 5c440b96cff567a8055069b577bafbeb3018323b Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 27 Mar 2017 22:51:55 -0400 Subject: [PATCH 66/97] environment docs List backends supporting environments. Note character restrictions for environment names. --- website/source/docs/state/environments.html.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/website/source/docs/state/environments.html.md b/website/source/docs/state/environments.html.md index 205a9ac08..e4a502609 100644 --- a/website/source/docs/state/environments.html.md +++ b/website/source/docs/state/environments.html.md @@ -20,6 +20,11 @@ Environments are a way to create multiple states that contain their own data so a single set of Terraform configurations can manage multiple distinct sets of resources. +Environments are currently supported by the following backends: + + * [Consul](/docs/backends/types/consul.html) + * [S3](/docs/backends/types/s3.html) + ## Using Environments Terraform starts with a single environment named "default". This @@ -120,7 +125,9 @@ For local state, Terraform stores the state environments in a folder For [remote state](/docs/state/remote.html), the environments are stored directly in the configured [backend](/docs/backends). For example, if you use [Consul](/docs/backends/types/consul.html), the environments are stored -by suffixing the state path with the environment name. +by suffixing the state path with the environment name. To ensure that +environment names are stored correctly and safely in all backends, the name +must be valid to use in a URL path segment without escaping. The important thing about environment internals is that environments are meant to be a shared resource. They aren't a private, local-only notion From ea71adbea9157b27d0fbda75e60520339c273eb8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 27 Mar 2017 23:13:16 -0700 Subject: [PATCH 67/97] website: new terraform webinar (#13111) --- .../images/webinar-Terraform-4-4-2017.jpg | Bin 0 -> 41187 bytes .../images/webinar-Terraform-4-4-2017@2x.jpg | Bin 0 -> 95228 bytes website/source/index.html.erb | 11 ++++++++++- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 website/source/assets/images/webinar-Terraform-4-4-2017.jpg create mode 100644 website/source/assets/images/webinar-Terraform-4-4-2017@2x.jpg diff --git a/website/source/assets/images/webinar-Terraform-4-4-2017.jpg b/website/source/assets/images/webinar-Terraform-4-4-2017.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1bc50497c9815df86f7462fcdeba4808d53edb13 GIT binary patch literal 41187 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 z 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