From 896c7d3d59c5976c83305bed6384f671e9ccb669 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 15 Dec 2020 10:46:35 -0500 Subject: [PATCH] data-source/aws_availability_zone: Add `parent_zone_id`, `parent_zone_name`, and `zone_type` attributes (additional support for Local and Wavelength Zones) (#16770) * d/aws_availability_zone: Add 'zone_type' attribute. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccDataSourceAwsAvailabilityZone_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccDataSourceAwsAvailabilityZone_ -timeout 120m === RUN TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === PAUSE TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === RUN TestAccDataSourceAwsAvailabilityZone_Filter === PAUSE TestAccDataSourceAwsAvailabilityZone_Filter === RUN TestAccDataSourceAwsAvailabilityZone_Name === PAUSE TestAccDataSourceAwsAvailabilityZone_Name === RUN TestAccDataSourceAwsAvailabilityZone_ZoneId === PAUSE TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === CONT TestAccDataSourceAwsAvailabilityZone_Filter === CONT TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_Name --- PASS: TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones (14.09s) --- PASS: TestAccDataSourceAwsAvailabilityZone_Filter (15.90s) --- PASS: TestAccDataSourceAwsAvailabilityZone_ZoneId (16.10s) --- PASS: TestAccDataSourceAwsAvailabilityZone_Name (16.13s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 16.194s * d/aws_availability_zone: Add Local Zone test. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccDataSourceAwsAvailabilityZone_LocalZone' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccDataSourceAwsAvailabilityZone_LocalZone -timeout 120m === RUN TestAccDataSourceAwsAvailabilityZone_LocalZone === PAUSE TestAccDataSourceAwsAvailabilityZone_LocalZone === CONT TestAccDataSourceAwsAvailabilityZone_LocalZone --- PASS: TestAccDataSourceAwsAvailabilityZone_LocalZone (10.55s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 10.625s * d/aws_availability_zone: Add Wavelength Zone test. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccDataSourceAwsAvailabilityZone_WavelengthZone' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccDataSourceAwsAvailabilityZone_WavelengthZone -timeout 120m === RUN TestAccDataSourceAwsAvailabilityZone_WavelengthZone === PAUSE TestAccDataSourceAwsAvailabilityZone_WavelengthZone === CONT TestAccDataSourceAwsAvailabilityZone_WavelengthZone --- PASS: TestAccDataSourceAwsAvailabilityZone_WavelengthZone (10.63s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 10.711s * d/aws_availability_zone: Add 'parent_zone_id' and 'parent_zone_name' attributes. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccDataSourceAwsAvailabilityZone_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccDataSourceAwsAvailabilityZone_ -timeout 120m === RUN TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === PAUSE TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === RUN TestAccDataSourceAwsAvailabilityZone_Filter === PAUSE TestAccDataSourceAwsAvailabilityZone_Filter === RUN TestAccDataSourceAwsAvailabilityZone_LocalZone === PAUSE TestAccDataSourceAwsAvailabilityZone_LocalZone === RUN TestAccDataSourceAwsAvailabilityZone_Name === PAUSE TestAccDataSourceAwsAvailabilityZone_Name === RUN TestAccDataSourceAwsAvailabilityZone_WavelengthZone === PAUSE TestAccDataSourceAwsAvailabilityZone_WavelengthZone === RUN TestAccDataSourceAwsAvailabilityZone_ZoneId === PAUSE TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === CONT TestAccDataSourceAwsAvailabilityZone_WavelengthZone === CONT TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_LocalZone === CONT TestAccDataSourceAwsAvailabilityZone_Name === CONT TestAccDataSourceAwsAvailabilityZone_Filter --- PASS: TestAccDataSourceAwsAvailabilityZone_Filter (19.67s) --- PASS: TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones (21.28s) --- PASS: TestAccDataSourceAwsAvailabilityZone_WavelengthZone (21.55s) --- PASS: TestAccDataSourceAwsAvailabilityZone_Name (21.65s) --- PASS: TestAccDataSourceAwsAvailabilityZone_ZoneId (21.69s) --- PASS: TestAccDataSourceAwsAvailabilityZone_LocalZone (21.70s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 21.778s * d/aws_availability_zone: Remove leading '-' from 'name_suffix' for Local and Wavelength Zones. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccDataSourceAwsAvailabilityZone_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccDataSourceAwsAvailabilityZone_ -timeout 120m === RUN TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === PAUSE TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === RUN TestAccDataSourceAwsAvailabilityZone_Filter === PAUSE TestAccDataSourceAwsAvailabilityZone_Filter === RUN TestAccDataSourceAwsAvailabilityZone_LocalZone === PAUSE TestAccDataSourceAwsAvailabilityZone_LocalZone === RUN TestAccDataSourceAwsAvailabilityZone_Name === PAUSE TestAccDataSourceAwsAvailabilityZone_Name === RUN TestAccDataSourceAwsAvailabilityZone_WavelengthZone === PAUSE TestAccDataSourceAwsAvailabilityZone_WavelengthZone === RUN TestAccDataSourceAwsAvailabilityZone_ZoneId === PAUSE TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones === CONT TestAccDataSourceAwsAvailabilityZone_ZoneId === CONT TestAccDataSourceAwsAvailabilityZone_WavelengthZone === CONT TestAccDataSourceAwsAvailabilityZone_Name === CONT TestAccDataSourceAwsAvailabilityZone_LocalZone === CONT TestAccDataSourceAwsAvailabilityZone_Filter --- PASS: TestAccDataSourceAwsAvailabilityZone_Name (17.92s) --- PASS: TestAccDataSourceAwsAvailabilityZone_ZoneId (19.40s) --- PASS: TestAccDataSourceAwsAvailabilityZone_WavelengthZone (20.22s) --- PASS: TestAccDataSourceAwsAvailabilityZone_LocalZone (20.37s) --- PASS: TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones (20.69s) --- PASS: TestAccDataSourceAwsAvailabilityZone_Filter (20.69s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 20.770s --- aws/data_source_aws_availability_zone.go | 20 ++- aws/data_source_aws_availability_zone_test.go | 147 ++++++++++++++++-- .../docs/d/availability_zone.html.markdown | 5 + 3 files changed, 159 insertions(+), 13 deletions(-) diff --git a/aws/data_source_aws_availability_zone.go b/aws/data_source_aws_availability_zone.go index e611eda7c80f..dfae144d050a 100644 --- a/aws/data_source_aws_availability_zone.go +++ b/aws/data_source_aws_availability_zone.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -40,6 +41,14 @@ func dataSourceAwsAvailabilityZone() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "parent_zone_id": { + Type: schema.TypeString, + Computed: true, + }, + "parent_zone_name": { + Type: schema.TypeString, + Computed: true, + }, "region": { Type: schema.TypeString, Computed: true, @@ -54,6 +63,10 @@ func dataSourceAwsAvailabilityZone() *schema.Resource { Optional: true, Computed: true, }, + "zone_type": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -108,7 +121,9 @@ func dataSourceAwsAvailabilityZoneRead(d *schema.ResourceData, meta interface{}) // the AZ suffix alone, without the region name. // This can be used e.g. to create lookup tables by AZ letter that // work regardless of region. - nameSuffix := (*az.ZoneName)[len(*az.RegionName):] + nameSuffix := aws.StringValue(az.ZoneName)[len(aws.StringValue(az.RegionName)):] + // For Local and Wavelength zones, remove any leading "-". + nameSuffix = strings.TrimLeft(nameSuffix, "-") d.SetId(aws.StringValue(az.ZoneName)) d.Set("group_name", az.GroupName) @@ -116,9 +131,12 @@ func dataSourceAwsAvailabilityZoneRead(d *schema.ResourceData, meta interface{}) d.Set("name_suffix", nameSuffix) d.Set("network_border_group", az.NetworkBorderGroup) d.Set("opt_in_status", az.OptInStatus) + d.Set("parent_zone_id", az.ParentZoneId) + d.Set("parent_zone_name", az.ParentZoneName) d.Set("region", az.RegionName) d.Set("state", az.State) d.Set("zone_id", az.ZoneId) + d.Set("zone_type", az.ZoneType) return nil } diff --git a/aws/data_source_aws_availability_zone_test.go b/aws/data_source_aws_availability_zone_test.go index b018a1a3488c..9f0f28f8f440 100644 --- a/aws/data_source_aws_availability_zone_test.go +++ b/aws/data_source_aws_availability_zone_test.go @@ -1,9 +1,11 @@ package aws import ( + "fmt" "regexp" "testing" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -22,9 +24,12 @@ func TestAccDataSourceAwsAvailabilityZone_AllAvailabilityZones(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z]$`)), resource.TestCheckResourceAttr(dataSourceName, "network_border_group", testAccGetRegion()), - resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", "opt-in-not-required"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptInNotRequired), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_id", ""), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_name", ""), resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "availability-zone"), ), }, }, @@ -46,9 +51,39 @@ func TestAccDataSourceAwsAvailabilityZone_Filter(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z]$`)), resource.TestCheckResourceAttr(dataSourceName, "network_border_group", testAccGetRegion()), - resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", "opt-in-not-required"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptInNotRequired), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_id", ""), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_name", ""), resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "availability-zone"), + ), + }, + }, + }) +} + +func TestAccDataSourceAwsAvailabilityZone_LocalZone(t *testing.T) { + availabilityZonesDataSourceName := "data.aws_availability_zones.available" + dataSourceName := "data.aws_availability_zone.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSLocalZoneAvailable(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsAvailabilityZoneConfigZoneType("local-zone"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "group_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), + resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z0-9][a-z0-9-]+$`)), + resource.TestCheckResourceAttrSet(dataSourceName, "network_border_group"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptedIn), + resource.TestCheckResourceAttrSet(dataSourceName, "parent_zone_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "parent_zone_name"), + resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), + resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "local-zone"), ), }, }, @@ -70,9 +105,39 @@ func TestAccDataSourceAwsAvailabilityZone_Name(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z]$`)), resource.TestCheckResourceAttr(dataSourceName, "network_border_group", testAccGetRegion()), - resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", "opt-in-not-required"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptInNotRequired), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_id", ""), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_name", ""), + resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), + resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "availability-zone"), + ), + }, + }, + }) +} + +func TestAccDataSourceAwsAvailabilityZone_WavelengthZone(t *testing.T) { + availabilityZonesDataSourceName := "data.aws_availability_zones.available" + dataSourceName := "data.aws_availability_zone.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSWavelengthZoneAvailable(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAwsAvailabilityZoneConfigZoneType("wavelength-zone"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "group_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), + resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z0-9][a-z0-9-]+$`)), + resource.TestCheckResourceAttrSet(dataSourceName, "network_border_group"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptedIn), + resource.TestCheckResourceAttrSet(dataSourceName, "parent_zone_id"), + resource.TestCheckResourceAttrSet(dataSourceName, "parent_zone_name"), resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "wavelength-zone"), ), }, }, @@ -94,47 +159,105 @@ func TestAccDataSourceAwsAvailabilityZone_ZoneId(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceName, "name", availabilityZonesDataSourceName, "names.0"), resource.TestMatchResourceAttr(dataSourceName, "name_suffix", regexp.MustCompile(`^[a-z]$`)), resource.TestCheckResourceAttr(dataSourceName, "network_border_group", testAccGetRegion()), - resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", "opt-in-not-required"), + resource.TestCheckResourceAttr(dataSourceName, "opt_in_status", ec2.AvailabilityZoneOptInStatusOptInNotRequired), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_id", ""), + resource.TestCheckResourceAttr(dataSourceName, "parent_zone_name", ""), resource.TestCheckResourceAttr(dataSourceName, "region", testAccGetRegion()), resource.TestCheckResourceAttrPair(dataSourceName, "zone_id", availabilityZonesDataSourceName, "zone_ids.0"), + resource.TestCheckResourceAttr(dataSourceName, "zone_type", "availability-zone"), ), }, }, }) } +func testAccPreCheckAWSLocalZoneAvailable(t *testing.T) { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + input := &ec2.DescribeAvailabilityZonesInput{ + Filters: buildEC2AttributeFilterList(map[string]string{ + "zone-type": "local-zone", + "opt-in-status": "opted-in", + }), + } + + output, err := conn.DescribeAvailabilityZones(input) + + if testAccPreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } + + if output == nil || len(output.AvailabilityZones) == 0 { + t.Skip("skipping since no Local Zones are available") + } +} + func testAccDataSourceAwsAvailabilityZoneConfigAllAvailabilityZones() string { - return testAccAvailableAZsNoOptInConfig() + ` + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + ` data "aws_availability_zone" "test" { all_availability_zones = true name = data.aws_availability_zones.available.names[0] } -` +`) } func testAccDataSourceAwsAvailabilityZoneConfigFilter() string { - return testAccAvailableAZsNoOptInConfig() + ` + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + ` data "aws_availability_zone" "test" { filter { name = "zone-name" values = [data.aws_availability_zones.available.names[0]] } } -` +`) } func testAccDataSourceAwsAvailabilityZoneConfigName() string { - return testAccAvailableAZsNoOptInConfig() + ` + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + ` data "aws_availability_zone" "test" { name = data.aws_availability_zones.available.names[0] } -` +`) } func testAccDataSourceAwsAvailabilityZoneConfigZoneId() string { - return testAccAvailableAZsNoOptInConfig() + ` + return composeConfig( + testAccAvailableAZsNoOptInConfig(), + ` +data "aws_availability_zone" "test" { + zone_id = data.aws_availability_zones.available.zone_ids[0] +} +`) +} + +func testAccDataSourceAwsAvailabilityZoneConfigZoneType(zoneType string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "zone-type" + values = [%[1]q] + } + + filter { + name = "opt-in-status" + values = ["opted-in"] + } +} + data "aws_availability_zone" "test" { zone_id = data.aws_availability_zones.available.zone_ids[0] } -` +`, zoneType) } diff --git a/website/docs/d/availability_zone.html.markdown b/website/docs/d/availability_zone.html.markdown index 4252243f644a..58b160b10749 100644 --- a/website/docs/d/availability_zone.html.markdown +++ b/website/docs/d/availability_zone.html.markdown @@ -93,6 +93,11 @@ In addition to all arguments above, the following attributes are exported: * `group_name` - For Availability Zones, this is the same value as the Region name. For Local Zones, the name of the associated group, for example `us-west-2-lax-1`. * `name_suffix` - The part of the AZ name that appears after the region name, uniquely identifying the AZ within its region. +For Availability Zones this is usually a single letter, for example `a` for the `us-west-2a` zone. +For Local and Wavelength Zones this is a longer string, for example `wl1-sfo-wlz-1` for the `us-west-2-wl1-sfo-wlz-1` zone. * `network_border_group` - The name of the location from which the address is advertised. * `opt_in_status` - For Availability Zones, this always has the value of `opt-in-not-required`. For Local Zones, this is the opt in status. The possible values are `opted-in` and `not-opted-in`. +* `parent_zone_id` - The ID of the zone that handles some of the Local Zone or Wavelength Zone control plane operations, such as API calls. +* `parent_zone_name` - The name of the zone that handles some of the Local Zone or Wavelength Zone control plane operations, such as API calls. * `region` - The region where the selected availability zone resides. This is always the region selected on the provider, since this data source searches only within that region. +* `zone_type` - The type of zone. Values are `availability-zone`, `local-zone`, and `wavelength-zone`.