Skip to content

Commit

Permalink
Add state filter to aws_availability_zones data source. (#7965)
Browse files Browse the repository at this point in the history
* Add state filter to aws_availability_zones data source.

This commit adds an ability to filter Availability Zones based on state, where
by default it would only list available zones.

Be advised that this does not always works reliably for an older accounts which
have been created in the pre-VPC era of EC2. These accounts tends to retrieve
availability zones that are not VPC-enabled, thus creation of a custom subnet
within such Availability Zone would result in a failure.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Update documentation for aws_availability_zones data source.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Do not filter on state by default.

This commit makes the state filter applicable only when set.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>
  • Loading branch information
kwilczynski authored and stack72 committed Aug 5, 2016
1 parent 256190a commit 19800b8
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 14 deletions.
49 changes: 42 additions & 7 deletions builtin/providers/aws/data_source_availability_zones.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,67 @@ func dataSourceAwsAvailabilityZones() *schema.Resource {
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"state": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateStateType,
},
},
}
}

func dataSourceAwsAvailabilityZonesRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

log.Printf("[DEBUG] Reading availability zones")
log.Printf("[DEBUG] Reading Availability Zones.")
d.SetId(time.Now().UTC().String())

req := &ec2.DescribeAvailabilityZonesInput{DryRun: aws.Bool(false)}
azresp, err := conn.DescribeAvailabilityZones(req)
request := &ec2.DescribeAvailabilityZonesInput{}

if v, ok := d.GetOk("state"); ok {
request.Filters = []*ec2.Filter{
&ec2.Filter{
Name: aws.String("state"),
Values: []*string{aws.String(v.(string))},
},
}
}

log.Printf("[DEBUG] Availability Zones request options: %#v", *request)

resp, err := conn.DescribeAvailabilityZones(request)
if err != nil {
return fmt.Errorf("Error listing availability zones: %s", err)
return fmt.Errorf("Error fetching Availability Zones: %s", err)
}

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

sort.Strings(raw)

if err := d.Set("names", raw); err != nil {
return fmt.Errorf("[WARN] Error setting availability zones")
return fmt.Errorf("[WARN] Error setting Availability Zones: %s", err)
}

return nil
}

func validateStateType(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)

validState := map[string]bool{
"available": true,
"information": true,
"impaired": true,
"unavailable": true,
}

if !validState[value] {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Availability Zone state %q. Valid states are: %q, %q, %q and %q.",
k, value, "available", "information", "impaired", "unavailable"))
}
return
}
42 changes: 38 additions & 4 deletions builtin/providers/aws/data_source_availability_zones_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ func TestAccAWSAvailabilityZones_basic(t *testing.T) {
testAccCheckAwsAvailabilityZonesMeta("data.aws_availability_zones.availability_zones"),
),
},
resource.TestStep{
Config: testAccCheckAwsAvailabilityZonesStateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsAvailabilityZoneState("data.aws_availability_zones.state_filter"),
),
},
},
})
}
Expand All @@ -34,7 +40,7 @@ func testAccCheckAwsAvailabilityZonesMeta(n string) resource.TestCheckFunc {
}

if rs.Primary.ID == "" {
return fmt.Errorf("AZ resource ID not set")
return fmt.Errorf("AZ resource ID not set.")
}

actual, err := testAccCheckAwsAvailabilityZonesBuildAvailable(rs.Primary.Attributes)
Expand All @@ -51,10 +57,33 @@ func testAccCheckAwsAvailabilityZonesMeta(n string) resource.TestCheckFunc {
}
}

func testAccCheckAwsAvailabilityZoneState(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Can't find AZ resource: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("AZ resource ID not set.")
}

if _, ok := rs.Primary.Attributes["state"]; !ok {
return fmt.Errorf("AZs state filter is missing, should be set.")
}

_, err := testAccCheckAwsAvailabilityZonesBuildAvailable(rs.Primary.Attributes)
if err != nil {
return err
}
return nil
}
}

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 list is missing.")
}
qty, err := strconv.Atoi(v)
if err != nil {
Expand All @@ -67,14 +96,19 @@ func testAccCheckAwsAvailabilityZonesBuildAvailable(attrs map[string]string) ([]
for n := range zones {
zone, ok := attrs["names."+strconv.Itoa(n)]
if !ok {
return nil, fmt.Errorf("AZ list corrupt, this is definitely a bug")
return nil, fmt.Errorf("AZ list corrupt, this is definitely a bug.")
}
zones[n] = zone
}
return zones, nil
}

const testAccCheckAwsAvailabilityZonesConfig = `
data "aws_availability_zones" "availability_zones" {
data "aws_availability_zones" "availability_zones" { }
`

const testAccCheckAwsAvailabilityZonesStateConfig = `
data "aws_availability_zones" "state_filter" {
state = "available"
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ layout: "aws"
page_title: "AWS: aws_availability_zones"
sidebar_current: "docs-aws-datasource-availability-zones"
description: |-
Provides a list of availability zones which can be used by an AWS account
Provides a list of Availability Zones which can be used by an AWS account.
---

# aws\_availability\_zones
Expand Down Expand Up @@ -35,10 +35,15 @@ resource "aws_subnet" "secondary" {

## Argument Reference

There are no arguments for this data source.
The following arguments are supported:

* `state` - (Optional) Allows to filter list of Availability Zones based on their
current state. Can be either `"available"`, `"information"`, `"impaired"` or
`"unavailable"`. By default the list includes a complete set of Availability Zones
to which the underlying AWS account has access, regardless of their state.

## Attributes Reference

The following attributes are exported:

* `names` - A list of the availability zone names available to the account.
* `names` - A list of the Availability Zone names available to the account.

0 comments on commit 19800b8

Please sign in to comment.