Merge #7984: Data sources for AWS and Fastly IP address ranges

This commit is contained in:
Martin Atkins 2016-08-09 09:37:43 -07:00
commit c6e8662838
11 changed files with 536 additions and 0 deletions

View File

@ -6,6 +6,8 @@ FEATURES:
* **New Resource:** `aws_load_balancer_policy` [GH-7458] * **New Resource:** `aws_load_balancer_policy` [GH-7458]
* **New Resource:** `aws_load_balancer_backend_server_policy` [GH-7458] * **New Resource:** `aws_load_balancer_backend_server_policy` [GH-7458]
* **New Resource:** `aws_load_balancer_listener_policy` [GH-7458] * **New Resource:** `aws_load_balancer_listener_policy` [GH-7458]
* **New Data Source:** `aws_ip_ranges` [GH-7984]
* **New Data Source:** `fastly_ip_ranges` [GH-7984]
IMPROVEMENTS IMPROVEMENTS
* provider/aws: Introduce `aws_elasticsearch_domain` `elasticsearch_version` field (to specify ES version) [GH-7860] * provider/aws: Introduce `aws_elasticsearch_domain` `elasticsearch_version` field (to specify ES version) [GH-7860]

View File

@ -0,0 +1,151 @@
package aws
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"sort"
"strconv"
"strings"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/terraform/helper/schema"
)
type dataSourceAwsIPRangesResult struct {
CreateDate string
Prefixes []dataSourceAwsIPRangesPrefix
SyncToken string
}
type dataSourceAwsIPRangesPrefix struct {
IpPrefix string `json:"ip_prefix"`
Region string
Service string
}
func dataSourceAwsIPRanges() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsIPRangesRead,
Schema: map[string]*schema.Schema{
"cidr_blocks": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"create_date": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"regions": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
},
"services": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"sync_token": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
},
},
}
}
func dataSourceAwsIPRangesRead(d *schema.ResourceData, meta interface{}) error {
conn := cleanhttp.DefaultClient()
log.Printf("[DEBUG] Reading IP ranges")
res, err := conn.Get("https://ip-ranges.amazonaws.com/ip-ranges.json")
if err != nil {
return fmt.Errorf("Error listing IP ranges: %s", err)
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("Error reading response body: %s", err)
}
result := new(dataSourceAwsIPRangesResult)
if err := json.Unmarshal(data, result); err != nil {
return fmt.Errorf("Error parsing result: %s", err)
}
if err := d.Set("create_date", result.CreateDate); err != nil {
return fmt.Errorf("Error setting create date: %s", err)
}
syncToken, err := strconv.Atoi(result.SyncToken)
if err != nil {
return fmt.Errorf("Error while converting sync token: %s", err)
}
d.SetId(result.SyncToken)
if err := d.Set("sync_token", syncToken); err != nil {
return fmt.Errorf("Error setting sync token: %s", err)
}
get := func(key string) *schema.Set {
set := d.Get(key).(*schema.Set)
for _, e := range set.List() {
s := e.(string)
set.Remove(s)
set.Add(strings.ToLower(s))
}
return set
}
var (
regions = get("regions")
services = get("services")
noRegionFilter = regions.Len() == 0
prefixes []string
)
for _, e := range result.Prefixes {
var (
matchRegion = noRegionFilter || regions.Contains(strings.ToLower(e.Region))
matchService = services.Contains(strings.ToLower(e.Service))
)
if matchRegion && matchService {
prefixes = append(prefixes, e.IpPrefix)
}
}
if len(prefixes) == 0 {
return fmt.Errorf(" No IP ranges result from filters")
}
sort.Strings(prefixes)
if err := d.Set("cidr_blocks", prefixes); err != nil {
return fmt.Errorf("Error setting ip ranges: %s", err)
}
return nil
}

View File

@ -0,0 +1,128 @@
package aws
import (
"fmt"
"net"
"regexp"
"sort"
"strconv"
"testing"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSIPRanges(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSIPRangesConfig,
Check: resource.ComposeTestCheckFunc(
testAccAWSIPRanges("data.aws_ip_ranges.some"),
),
},
},
})
}
func testAccAWSIPRanges(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
r := s.RootModule().Resources[n]
a := r.Primary.Attributes
var (
cidrBlockSize int
createDate time.Time
err error
syncToken int
)
if cidrBlockSize, err = strconv.Atoi(a["cidr_blocks.#"]); err != nil {
return err
}
if cidrBlockSize < 10 {
return fmt.Errorf("cidr_blocks for eu-west-1 seem suspiciously low: %d", cidrBlockSize)
}
if createDate, err = time.Parse("2006-01-02-15-04-05", a["create_date"]); err != nil {
return err
}
if syncToken, err = strconv.Atoi(a["sync_token"]); err != nil {
return err
}
if syncToken != int(createDate.Unix()) {
return fmt.Errorf("sync_token %d does not match create_date %s", syncToken, createDate)
}
var cidrBlocks sort.StringSlice = make([]string, cidrBlockSize)
for i := range make([]string, cidrBlockSize) {
block := a[fmt.Sprintf("cidr_blocks.%d", i)]
if _, _, err := net.ParseCIDR(block); err != nil {
return fmt.Errorf("malformed CIDR block %s: %s", block, err)
}
cidrBlocks[i] = block
}
if !sort.IsSorted(cidrBlocks) {
return fmt.Errorf("unexpected order of cidr_blocks: %s", cidrBlocks)
}
var (
regionMember = regexp.MustCompile(`regions\.\d+`)
regions, services int
serviceMember = regexp.MustCompile(`services\.\d+`)
)
for k, v := range a {
if regionMember.MatchString(k) {
if !(v == "eu-west-1" || v == "EU-central-1") {
return fmt.Errorf("unexpected region %s", v)
}
regions = regions + 1
}
if serviceMember.MatchString(k) {
if v != "EC2" {
return fmt.Errorf("unexpected service %s", v)
}
services = services + 1
}
}
if regions != 2 {
return fmt.Errorf("unexpected number of regions: %d", regions)
}
if services != 1 {
return fmt.Errorf("unexpected number of services: %d", services)
}
return nil
}
}
const testAccAWSIPRangesConfig = `
data "aws_ip_ranges" "some" {
regions = [ "eu-west-1", "EU-central-1" ]
services = [ "EC2" ]
}
`

View File

@ -114,6 +114,7 @@ func Provider() terraform.ResourceProvider {
"aws_ami": dataSourceAwsAmi(), "aws_ami": dataSourceAwsAmi(),
"aws_availability_zones": dataSourceAwsAvailabilityZones(), "aws_availability_zones": dataSourceAwsAvailabilityZones(),
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(), "aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
"aws_ip_ranges": dataSourceAwsIPRanges(),
"aws_s3_bucket_object": dataSourceAwsS3BucketObject(), "aws_s3_bucket_object": dataSourceAwsS3BucketObject(),
"aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(), "aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(),
}, },

View File

@ -0,0 +1,70 @@
package fastly
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"sort"
"strconv"
"github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)
type dataSourceFastlyIPRangesResult struct {
Addresses []string
}
func dataSourceFastlyIPRanges() *schema.Resource {
return &schema.Resource{
Read: dataSourceFastlyIPRangesRead,
Schema: map[string]*schema.Schema{
"cidr_blocks": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
func dataSourceFastlyIPRangesRead(d *schema.ResourceData, meta interface{}) error {
conn := cleanhttp.DefaultClient()
log.Printf("[DEBUG] Reading IP ranges")
res, err := conn.Get("https://api.fastly.com/public-ip-list")
if err != nil {
return fmt.Errorf("Error listing IP ranges: %s", err)
}
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("Error reading response body: %s", err)
}
d.SetId(strconv.Itoa(hashcode.String(string(data))))
result := new(dataSourceFastlyIPRangesResult)
if err := json.Unmarshal(data, result); err != nil {
return fmt.Errorf("Error parsing result: %s", err)
}
sort.Strings(result.Addresses)
if err := d.Set("cidr_blocks", result.Addresses); err != nil {
return fmt.Errorf("Error setting ip ranges: %s", err)
}
return nil
}

View File

@ -0,0 +1,73 @@
package fastly
import (
"fmt"
"net"
"sort"
"strconv"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccFastlyIPRanges(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccFastlyIPRangesConfig,
Check: resource.ComposeTestCheckFunc(
testAccFastlyIPRanges("data.fastly_ip_ranges.some"),
),
},
},
})
}
func testAccFastlyIPRanges(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
r := s.RootModule().Resources[n]
a := r.Primary.Attributes
var (
cidrBlockSize int
err error
)
if cidrBlockSize, err = strconv.Atoi(a["cidr_blocks.#"]); err != nil {
return err
}
if cidrBlockSize < 10 {
return fmt.Errorf("cidr_blocks seem suspiciously low: %d", cidrBlockSize)
}
var cidrBlocks sort.StringSlice = make([]string, cidrBlockSize)
for i := range make([]string, cidrBlockSize) {
block := a[fmt.Sprintf("cidr_blocks.%d", i)]
if _, _, err := net.ParseCIDR(block); err != nil {
return fmt.Errorf("malformed CIDR block %s: %s", block, err)
}
cidrBlocks[i] = block
}
if !sort.IsSorted(cidrBlocks) {
return fmt.Errorf("unexpected order of cidr_blocks: %s", cidrBlocks)
}
return nil
}
}
const testAccFastlyIPRangesConfig = `
data "fastly_ip_ranges" "some" {
}
`

View File

@ -18,6 +18,9 @@ func Provider() terraform.ResourceProvider {
Description: "Fastly API Key from https://app.fastly.com/#account", Description: "Fastly API Key from https://app.fastly.com/#account",
}, },
}, },
DataSourcesMap: map[string]*schema.Resource{
"fastly_ip_ranges": dataSourceFastlyIPRanges(),
},
ResourcesMap: map[string]*schema.Resource{ ResourcesMap: map[string]*schema.Resource{
"fastly_service_v1": resourceServiceV1(), "fastly_service_v1": resourceServiceV1(),
}, },

View File

@ -0,0 +1,59 @@
---
layout: "aws"
page_title: "AWS: aws_ip_ranges"
sidebar_current: "docs-aws-datasource-ip_ranges"
description: |-
Get information on AWS IP ranges.
---
# aws\_ip_ranges
Use this data source to get the [IP ranges][1] of various AWS products and services.
## Example Usage
```
data "aws_ip_ranges" "european_ec2" {
regions = [ "eu-west-1", "eu-central-1" ]
services = [ "ec2" ]
}
resource "aws_security_group" "from_europe" {
name = "from_europe"
ingress {
from_port = "443"
to_port = "443"
protocol = "tcp"
cidr_blocks = [ "${data.aws_ip_ranges.european_ec2.blocks}" ]
}
tags {
CreateDate = "${data.aws_ip_ranges.european_ec2.create_date}"
SyncToken = "${data.aws_ip_ranges.european_ec2.sync_token}"
}
}
```
## Argument Reference
* `regions` - (Optional) Filter IP ranges by regions (or include all regions, if
omitted). Valid items are `global` (for `cloudfront`) as well as all AWS regions
(e.g. `eu-central-1`)
* `services` - (Required) Filter IP ranges by services. Valid items are `amazon`
(for amazon.com), `cloudfront`, `ec2`, `route53` and `route53_healthchecks`.
~> **NOTE:** If the specified combination of regions and services does not yield any
CIDR blocks, Terraform will fail.
## Attributes Reference
* `cidr_blocks` - The lexically ordered list of CIDR blocks.
* `create_date` - The publication time of the IP ranges (e.g. `2016-08-03-23-46-05`).
* `sync_token` - The publication time of the IP ranges, in Unix epoch time format
(e.g. `1470267965`).
[1]: http://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html

View File

@ -0,0 +1,37 @@
---
layout: "fastly"
page_title: "Fastly: fastly_ip_ranges"
sidebar_current: "docs-fastly-datasource-ip_ranges"
description: |-
Get information on Fastly IP ranges.
---
# fastly\_ip_ranges
Use this data source to get the [IP ranges][1] of Fastly edge nodes.
## Example Usage
```
data "fastly_ip_ranges" "fastly" {
}
resource "aws_security_group" "from_fastly" {
name = "from_fastly"
ingress {
from_port = "443"
to_port = "443"
protocol = "tcp"
cidr_blocks = [ "${data.fastly_ip_ranges.fastly.cidr_blocks}" ]
}
}
```
## Attributes Reference
* `cidr_blocks` - The lexically ordered list of CIDR blocks.
[1]: https://docs.fastly.com/guides/securing-communications/accessing-fastlys-ip-ranges

View File

@ -25,6 +25,9 @@
<li<%= sidebar_current("docs-aws-datasource-iam-policy-document") %>> <li<%= sidebar_current("docs-aws-datasource-iam-policy-document") %>>
<a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a> <a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a>
</li> </li>
<li<%= sidebar_current("docs-aws-datasource-ip_ranges") %>>
<a href="/docs/providers/aws/d/ip_ranges.html">aws_ip_ranges</a>
</li>
<li<%= sidebar_current("docs-aws-datasource-s3-bucket-object") %>> <li<%= sidebar_current("docs-aws-datasource-s3-bucket-object") %>>
<a href="/docs/providers/aws/d/s3_bucket_object.html">aws_s3_bucket_object</a> <a href="/docs/providers/aws/d/s3_bucket_object.html">aws_s3_bucket_object</a>
</li> </li>

View File

@ -10,6 +10,15 @@
<a href="/docs/providers/fastly/index.html">Fastly Provider</a> <a href="/docs/providers/fastly/index.html">Fastly Provider</a>
</li> </li>
<li<%= sidebar_current(/^docs-fastly-datasource/) %>>
<a href="#">Data Sources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-fastly-datasource-ip_ranges") %>>
<a href="/docs/providers/fastly/d/ip_ranges.html">fastly_ip_ranges</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-fastly-resource/) %>> <li<%= sidebar_current(/^docs-fastly-resource/) %>>
<a href="#">Resources</a> <a href="#">Resources</a>