Skip to content

Commit

Permalink
Support Availability Zone ID in data sources.
Browse files Browse the repository at this point in the history
  • Loading branch information
ewbankkit committed Dec 2, 2018
1 parent 7a37b00 commit 932d49a
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 38 deletions.
17 changes: 13 additions & 4 deletions aws/data_source_aws_availability_zone.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func dataSourceAwsAvailabilityZone() *schema.Resource {
Optional: true,
Computed: true,
},

"zone_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}
Expand All @@ -44,10 +50,12 @@ func dataSourceAwsAvailabilityZoneRead(d *schema.ResourceData, meta interface{})

req := &ec2.DescribeAvailabilityZonesInput{}

if name := d.Get("name"); name != "" {
req.ZoneNames = []*string{aws.String(name.(string))}
if v := d.Get("name").(string); v != "" {
req.ZoneNames = []*string{aws.String(v)}
}
if v := d.Get("zone_id").(string); v != "" {
req.ZoneIds = []*string{aws.String(v)}
}

req.Filters = buildEC2AttributeFilterList(
map[string]string{
"state": d.Get("state").(string),
Expand Down Expand Up @@ -78,11 +86,12 @@ func dataSourceAwsAvailabilityZoneRead(d *schema.ResourceData, meta interface{})
// work regardless of region.
nameSuffix := (*az.ZoneName)[len(*az.RegionName):]

d.SetId(*az.ZoneName)
d.SetId(aws.StringValue(az.ZoneName))
d.Set("name", az.ZoneName)
d.Set("name_suffix", nameSuffix)
d.Set("region", az.RegionName)
d.Set("state", az.State)
d.Set("zone_id", az.ZoneId)

return nil
}
42 changes: 16 additions & 26 deletions aws/data_source_aws_availability_zone_test.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,37 @@
package aws

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceAwsAvailabilityZone(t *testing.T) {
ds1ResourceName := "data.aws_availability_zone.by_name"
ds2ResourceName := "data.aws_availability_zone.by_zone_id"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAwsAvailabilityZoneConfig,
Check: resource.ComposeTestCheckFunc(
testAccDataSourceAwsAvailabilityZoneCheck("data.aws_availability_zone.by_name"),
resource.TestCheckResourceAttr(ds1ResourceName, "name", "us-west-2a"),
resource.TestCheckResourceAttr(ds1ResourceName, "name_suffix", "a"),
resource.TestCheckResourceAttr(ds1ResourceName, "region", "us-west-2"),
resource.TestCheckResourceAttrSet(ds1ResourceName, "zone_id"),

resource.TestCheckResourceAttr(ds2ResourceName, "name", "us-west-2a"),
resource.TestCheckResourceAttr(ds2ResourceName, "name_suffix", "a"),
resource.TestCheckResourceAttr(ds2ResourceName, "region", "us-west-2"),
resource.TestCheckResourceAttrPair(ds2ResourceName, "zone_id", ds1ResourceName, "zone_id"),
),
},
},
})
}

func testAccDataSourceAwsAvailabilityZoneCheck(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("root module has no resource called %s", name)
}

attr := rs.Primary.Attributes

if attr["name"] != "us-west-2a" {
return fmt.Errorf("bad name %s", attr["name"])
}
if attr["name_suffix"] != "a" {
return fmt.Errorf("bad name_suffix %s", attr["name_suffix"])
}
if attr["region"] != "us-west-2" {
return fmt.Errorf("bad region %s", attr["region"])
}

return nil
}
}

const testAccDataSourceAwsAvailabilityZoneConfig = `
provider "aws" {
region = "us-west-2"
Expand All @@ -54,4 +40,8 @@ provider "aws" {
data "aws_availability_zone" "by_name" {
name = "us-west-2a"
}
data "aws_availability_zone" "by_zone_id" {
zone_id = "${data.aws_availability_zone.by_name.zone_id}"
}
`
41 changes: 34 additions & 7 deletions aws/data_source_aws_availability_zones.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ func dataSourceAwsAvailabilityZones() *schema.Resource {
ec2.AvailabilityZoneStateUnavailable,
}, false),
},
"zone_ids": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
Expand Down Expand Up @@ -59,16 +64,38 @@ func dataSourceAwsAvailabilityZonesRead(d *schema.ResourceData, meta interface{}
return fmt.Errorf("Error fetching Availability Zones: %s", err)
}

raw := make([]string, len(resp.AvailabilityZones))
for i, v := range resp.AvailabilityZones {
raw[i] = *v.ZoneName
}
azs := resp.AvailabilityZones
sort.Sort(availabilityZones(azs))

sort.Strings(raw)
names := []string{}
zoneIds := []string{}
for _, v := range azs {
names = append(names, aws.StringValue(v.ZoneName))
zoneIds = append(zoneIds, aws.StringValue(v.ZoneId))
}

if err := d.Set("names", raw); err != nil {
return fmt.Errorf("Error setting Availability Zones: %s", err)
if err := d.Set("names", names); err != nil {
return fmt.Errorf("Error setting Availability Zone names: %s", err)
}
if err := d.Set("zone_ids", zoneIds); err != nil {
return fmt.Errorf("Error setting Availability Zone IDs: %s", err)
}

return nil
}

// Ensure that indexes of returned AZ names and zone IDs correspond.
type availabilityZones []*ec2.AvailabilityZone

func (azs availabilityZones) Len() int {
return len(azs)
}

func (azs availabilityZones) Swap(i, j int) {
azs[i], azs[j] = azs[j], azs[i]
}

func (azs availabilityZones) Less(i, j int) bool {
// Sort by AZ name.
return aws.StringValue(azs[i].ZoneName) < aws.StringValue(azs[j].ZoneName)
}
66 changes: 65 additions & 1 deletion aws/data_source_aws_availability_zones_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,70 @@ import (
"strconv"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAvailabilityZonesSort(t *testing.T) {
azs := []*ec2.AvailabilityZone{
{
ZoneName: aws.String("name_YYY"),
ZoneId: aws.String("id_YYY"),
},
{
ZoneName: aws.String("name_AAA"),
ZoneId: aws.String("id_AAA"),
},
{
ZoneName: aws.String("name_ZZZ"),
ZoneId: aws.String("id_ZZZ"),
},
{
ZoneName: aws.String("name_BBB"),
ZoneId: aws.String("id_BBB"),
},
}
sort.Sort(availabilityZones(azs))

cases := []struct {
Index int
ZoneName string
ZoneId string
}{
{
Index: 0,
ZoneName: "name_AAA",
ZoneId: "id_AAA",
},
{
Index: 1,
ZoneName: "name_BBB",
ZoneId: "id_BBB",
},
{
Index: 2,
ZoneName: "name_YYY",
ZoneId: "id_YYY",
},
{
Index: 3,
ZoneName: "name_ZZZ",
ZoneId: "id_ZZZ",
},
}
for _, tc := range cases {
az := azs[tc.Index]
if aws.StringValue(az.ZoneName) != tc.ZoneName {
t.Fatalf("AvailabilityZones index %d got zone name %s, expected %s", tc.Index, aws.StringValue(az.ZoneName), tc.ZoneName)
}
if aws.StringValue(az.ZoneId) != tc.ZoneId {
t.Fatalf("AvailabilityZones index %d got zone ID %s, expected %s", tc.Index, aws.StringValue(az.ZoneId), tc.ZoneId)
}
}
}

func TestAccAWSAvailabilityZones_basic(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Expand Down Expand Up @@ -92,7 +152,7 @@ func testAccCheckAwsAvailabilityZoneState(n string) resource.TestCheckFunc {
func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]string, error) {
v, ok := attrs["names.#"]
if !ok {
return nil, fmt.Errorf("Available AZ list is missing.")
return nil, fmt.Errorf("Available AZ name list is missing.")
}
qty, err := strconv.Atoi(v)
if err != nil {
Expand All @@ -101,6 +161,10 @@ func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]
if qty < 1 {
return nil, fmt.Errorf("No AZs found in region, this is probably a bug.")
}
_, ok = attrs["zone_ids.#"]
if !ok {
return nil, fmt.Errorf("Available AZ ID list is missing.")
}
zones := make([]string, qty)
for n := range zones {
zone, ok := attrs["names."+strconv.Itoa(n)]
Expand Down
4 changes: 4 additions & 0 deletions website/docs/d/availability_zone.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ zone whose data will be exported as attributes.
* `state` - (Optional) A specific availability zone state to require. May
be any of `"available"`, `"information"` or `"impaired"`.

* `zone_id` - (Optional) The zone ID of the availability zone to select.

All reasonable uses of this data source will specify `name`, since `state`
alone would match a single AZ only in a region that itself has only one AZ.

Expand All @@ -96,3 +98,5 @@ In addition to all arguments above, the following attributes are exported:
uniquely identifying the AZ within its region.

* `state` - The current state of the AZ.

* `zone_id` - (Optional) The zone ID of the selected availability zone.
3 changes: 3 additions & 0 deletions website/docs/d/availability_zones.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ to which the underlying AWS account has access, regardless of their state.
In addition to all arguments above, the following attributes are exported:

* `names` - A list of the Availability Zone names available to the account.
* `zone_ids` - A list of the Availability Zone IDs available to the account.

Note that the indexes of Availability Zone names and IDs correspond.

0 comments on commit 932d49a

Please sign in to comment.