Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data-source/aws_region: Remove EC2 API call, default to current if no endpoint or name specified #3157

Merged
merged 3 commits into from
Feb 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 64 additions & 36 deletions aws/data_source_aws_region.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package aws

import (
"fmt"
"log"
"strings"

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

Expand All @@ -21,9 +20,10 @@ func dataSourceAwsRegion() *schema.Resource {
},

"current": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
Type: schema.TypeBool,
Optional: true,
Computed: true,
Deprecated: "Defaults to current provider region if no other filtering is enabled",
},

"endpoint": {
Expand All @@ -36,48 +36,76 @@ func dataSourceAwsRegion() *schema.Resource {
}

func dataSourceAwsRegionRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
currentRegion := meta.(*AWSClient).region
providerRegion := meta.(*AWSClient).region

req := &ec2.DescribeRegionsInput{}
var region *endpoints.Region

req.RegionNames = make([]*string, 0, 2)
if name := d.Get("name").(string); name != "" {
req.RegionNames = append(req.RegionNames, aws.String(name))
if v, ok := d.GetOk("endpoint"); ok {
endpoint := v.(string)
matchingRegion, err := findRegionByEc2Endpoint(endpoint)
if err != nil {
return err
}
region = matchingRegion
}

if d.Get("current").(bool) {
req.RegionNames = append(req.RegionNames, aws.String(currentRegion))
if v, ok := d.GetOk("name"); ok {
name := v.(string)
matchingRegion, err := findRegionByName(name)
if err != nil {
return err
}
if region != nil && region.ID() != matchingRegion.ID() {
return fmt.Errorf("multiple regions matched; use additional constraints to reduce matches to a single region")
}
region = matchingRegion
}

req.Filters = buildEC2AttributeFilterList(
map[string]string{
"endpoint": d.Get("endpoint").(string),
},
)
if len(req.Filters) == 0 {
// Don't send an empty filters list; the EC2 API won't accept it.
req.Filters = nil
// Default to provider current region if no other filters matched
if region == nil {
matchingRegion, err := findRegionByName(providerRegion)
if err != nil {
return err
}
region = matchingRegion
}

log.Printf("[DEBUG] Reading Region: %s", req)
resp, err := conn.DescribeRegions(req)
d.SetId(region.ID())
d.Set("current", region.ID() == providerRegion)

regionEndpointEc2, err := region.ResolveEndpoint(endpoints.Ec2ServiceID)
if err != nil {
return err
}
if resp == nil || len(resp.Regions) == 0 {
return fmt.Errorf("no matching regions found")
}
if len(resp.Regions) > 1 {
return fmt.Errorf("multiple regions matched; use additional constraints to reduce matches to a single region")
}

region := resp.Regions[0]
d.Set("endpoint", strings.TrimPrefix(regionEndpointEc2.URL, "https://"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity: What is the reason for the trimming here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, the EC2 API response was returning just the hostname (see example here: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRegions.html), however the SDK ResolvedEndpoint.URL prepends https:// to the front. We weren't previously testing the old behavior, but the acceptance testing shows it doing the required trimming (I think).


d.SetId(*region.RegionName)
d.Set("name", region.RegionName)
d.Set("endpoint", region.Endpoint)
d.Set("current", *region.RegionName == currentRegion)
d.Set("name", region.ID())

return nil
}

func findRegionByEc2Endpoint(endpoint string) (*endpoints.Region, error) {
for _, partition := range endpoints.DefaultPartitions() {
for _, region := range partition.Regions() {
regionEndpointEc2, err := region.ResolveEndpoint(endpoints.Ec2ServiceID)
if err != nil {
return nil, err
}
if strings.TrimPrefix(regionEndpointEc2.URL, "https://") == endpoint {
return &region, nil
}
}
}
return nil, fmt.Errorf("region not found for endpoint: %s", endpoint)
}

func findRegionByName(name string) (*endpoints.Region, error) {
for _, partition := range endpoints.DefaultPartitions() {
for _, region := range partition.Regions() {
if region.ID() == name {
return &region, nil
}
}
}
return nil, fmt.Errorf("region not found for name: %s", name)
}
Loading