Merge branch 'master' into brandontosch/GH-11874
This commit is contained in:
commit
380f55b8a9
|
@ -4,8 +4,10 @@ language: go
|
|||
go:
|
||||
- 1.8
|
||||
|
||||
# add TF_CONSUL_TEST=1 to run consul tests
|
||||
# they were causing timouts in travis
|
||||
env:
|
||||
- CONSUL_VERSION=0.7.5 TF_CONSUL_TEST=1 GOMAXPROCS=4
|
||||
- CONSUL_VERSION=0.7.5 GOMAXPROCS=4
|
||||
|
||||
# Fetch consul for the backend and provider tests
|
||||
before_install:
|
||||
|
|
126
CHANGELOG.md
126
CHANGELOG.md
|
@ -1,55 +1,95 @@
|
|||
## 0.9.2 (unreleased)
|
||||
## 0.9.3 (unreleased)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080]
|
||||
* helper/resource: Allow unknown "pending" states [GH-13099]
|
||||
* provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547]
|
||||
* provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125]
|
||||
* provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125]
|
||||
* provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130]
|
||||
|
||||
|
||||
## 0.9.2 (March 28, 2017)
|
||||
|
||||
BACKWARDS IMCOMPATIBILITIES / NOTES:
|
||||
|
||||
* provider/openstack: Port Fixed IPs are able to be read again using the original numerical notation. However, Fixed IP configurations which are obtaining addresses via DHCP must now use the `all_fixed_ips` attribute to reference the returned IP address.
|
||||
* Environment names must be safe to use as a URL path segment without escaping, and is enforced by the CLI.
|
||||
|
||||
FEATURES:
|
||||
|
||||
* **New Resource:** `aws_api_gateway_usage_plan` [GH-12542]
|
||||
* **New Resource:** `aws_api_gateway_usage_plan_key` [GH-12851]
|
||||
* **New Resource:** `github_repository_webhook` [GH-12924]
|
||||
* **New Interpolation:** `substr` [GH-12870]
|
||||
* **New Resource:** `alicloud_db_instance` ([#12913](https://github.com/hashicorp/terraform/issues/12913))
|
||||
* **New Resource:** `aws_api_gateway_usage_plan` ([#12542](https://github.com/hashicorp/terraform/issues/12542))
|
||||
* **New Resource:** `aws_api_gateway_usage_plan_key` ([#12851](https://github.com/hashicorp/terraform/issues/12851))
|
||||
* **New Resource:** `github_repository_webhook` ([#12924](https://github.com/hashicorp/terraform/issues/12924))
|
||||
* **New Resource:** `random_pet` ([#12903](https://github.com/hashicorp/terraform/issues/12903))
|
||||
* **New Interpolation:** `substr` ([#12870](https://github.com/hashicorp/terraform/issues/12870))
|
||||
* **S3 Environments:** The S3 remote state backend now supports named environments
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* core: fix `ignore_changes` causing fields to be removed during apply [GH-12897]
|
||||
* core: add `-force-copy` option to `terraform init` to supress prompts for copying state [GH-12939]
|
||||
* helper/acctest: Add NewSSHKeyPair function [GH-12894]
|
||||
* provider/alicloud: simplify validators [GH-12982]
|
||||
* provider/aws: Added support for EMR AutoScalingRole [GH-12823]
|
||||
* provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources [GH-12629]
|
||||
* provider/aws: Updated default configuration manager version in `aws_opsworks_stack` [GH-12979]
|
||||
* provider/aws: Added aws_api_gateway_api_key value attribute [GH-9462]
|
||||
* provider/aws: Allow aws_alb subnets to change [GH-12850]
|
||||
* provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups [GH-12855]
|
||||
* provider/azurerm: Add support for setting the primary network interface [GH-11290]
|
||||
* provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource [GH-11306]
|
||||
* provider/consul: Add support for basic auth to the provider [GH-12679]
|
||||
* provider/dnsimple: Allow dnsimple_record.priority attribute to be set [GH-12843]
|
||||
* provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config [GH-12743]
|
||||
* provider/google: Add local ssd count support for container clusters [GH-12281]
|
||||
* provider/ignition: ignition_filesystem, explicit option to create the filesystem [GH-12980]
|
||||
* provider/ns1: Ensure provider checks for credentials [GH-12920]
|
||||
* provider/openstack: Adding Timeouts to Blockstorage Resources [GH-12862]
|
||||
* provider/openstack: Adding Timeouts to FWaaS v1 Resources [GH-12863]
|
||||
* provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources [GH-12865]
|
||||
* provider/openstack: Adding Timeouts to Network Resources [GH-12866]
|
||||
* provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867]
|
||||
* provider/pagerduty: Validate credentials [GH-12854]
|
||||
* core: fix interpolation error when referencing computed values from an `aws_instance` `cidr_block` ([#13046](https://github.com/hashicorp/terraform/issues/13046))
|
||||
* core: fix `ignore_changes` causing fields to be removed during apply ([#12897](https://github.com/hashicorp/terraform/issues/12897))
|
||||
* core: add `-force-copy` option to `terraform init` to supress prompts for copying state ([#12939](https://github.com/hashicorp/terraform/issues/12939))
|
||||
* helper/acctest: Add NewSSHKeyPair function ([#12894](https://github.com/hashicorp/terraform/issues/12894))
|
||||
* provider/alicloud: simplify validators ([#12982](https://github.com/hashicorp/terraform/issues/12982))
|
||||
* provider/aws: Added support for EMR AutoScalingRole ([#12823](https://github.com/hashicorp/terraform/issues/12823))
|
||||
* provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources ([#12629](https://github.com/hashicorp/terraform/issues/12629))
|
||||
* provider/aws: Updated default configuration manager version in `aws_opsworks_stack` ([#12979](https://github.com/hashicorp/terraform/issues/12979))
|
||||
* provider/aws: Added aws_api_gateway_api_key value attribute ([#9462](https://github.com/hashicorp/terraform/issues/9462))
|
||||
* provider/aws: Allow aws_alb subnets to change ([#12850](https://github.com/hashicorp/terraform/issues/12850))
|
||||
* provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups ([#12855](https://github.com/hashicorp/terraform/issues/12855))
|
||||
* provider/aws: Support Import of iam_server_certificate ([#13065](https://github.com/hashicorp/terraform/issues/13065))
|
||||
* provider/azurerm: Add support for setting the primary network interface ([#11290](https://github.com/hashicorp/terraform/issues/11290))
|
||||
* provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource ([#11306](https://github.com/hashicorp/terraform/issues/11306))
|
||||
* provider/consul: Add support for basic auth to the provider ([#12679](https://github.com/hashicorp/terraform/issues/12679))
|
||||
* provider/digitalocean: Support disk only resize ([#13059](https://github.com/hashicorp/terraform/issues/13059))
|
||||
* provider/dnsimple: Allow dnsimple_record.priority attribute to be set ([#12843](https://github.com/hashicorp/terraform/issues/12843))
|
||||
* provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config ([#12743](https://github.com/hashicorp/terraform/issues/12743))
|
||||
* provider/google: Add local ssd count support for container clusters ([#12281](https://github.com/hashicorp/terraform/issues/12281))
|
||||
* provider/ignition: ignition_filesystem, explicit option to create the filesystem ([#12980](https://github.com/hashicorp/terraform/issues/12980))
|
||||
* provider/kubernetes: Internal K8S annotations are ignored in `config_map` ([#12945](https://github.com/hashicorp/terraform/issues/12945))
|
||||
* provider/ns1: Ensure provider checks for credentials ([#12920](https://github.com/hashicorp/terraform/issues/12920))
|
||||
* provider/openstack: Adding Timeouts to Blockstorage Resources ([#12862](https://github.com/hashicorp/terraform/issues/12862))
|
||||
* provider/openstack: Adding Timeouts to FWaaS v1 Resources ([#12863](https://github.com/hashicorp/terraform/issues/12863))
|
||||
* provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources ([#12865](https://github.com/hashicorp/terraform/issues/12865))
|
||||
* provider/openstack: Adding Timeouts to Network Resources ([#12866](https://github.com/hashicorp/terraform/issues/12866))
|
||||
* provider/openstack: Adding Timeouts to LBaaS v1 Resources ([#12867](https://github.com/hashicorp/terraform/issues/12867))
|
||||
* provider/openstack: Deprecating Instance Volume attribute ([#13062](https://github.com/hashicorp/terraform/issues/13062))
|
||||
* provider/openstack: Decprecating Instance Floating IP attribute ([#13063](https://github.com/hashicorp/terraform/issues/13063))
|
||||
* provider/openstack: Don't log the catalog ([#13075](https://github.com/hashicorp/terraform/issues/13075))
|
||||
* provider/openstack: Handle 409/500 Response on Pool Create ([#13074](https://github.com/hashicorp/terraform/issues/13074))
|
||||
* provider/pagerduty: Validate credentials ([#12854](https://github.com/hashicorp/terraform/issues/12854))
|
||||
* provider/openstack: Adding all_metadata attribute ([#13061](https://github.com/hashicorp/terraform/issues/13061))
|
||||
* provider/profitbricks: Handling missing resources ([#13053](https://github.com/hashicorp/terraform/issues/13053))
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. [GH-12888]
|
||||
* provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) [GH-12849]
|
||||
* provider/aws: Fix flattened cloudfront lambda function associations to be a set not a slice [GH-11984]
|
||||
* provider/aws: Consider ACTIVE as pending state during ECS svc deletion [GH-12986]
|
||||
* provider/aws: Deprecate the usage of Api Gateway Key Stages in favor of Usage Plans [GH-12883]
|
||||
* provider/aws: prevent panic in resourceAwsSsmDocumentRead [GH-12891]
|
||||
* provider/aws: Prevent panic when setting AWS CodeBuild Source to state [GH-12915]
|
||||
* provider/aws: Only call replace Iam Instance Profile on existing machines [GH-12922]
|
||||
* provider/aws: Increase AWS AMI Destroy timeout [GH-12943]
|
||||
* provider/aws: Set aws_vpc ipv6 for associated only [GH-12899]
|
||||
* provider/aws: Fix AWS ECS placement strategy spread fields [GH-12998]
|
||||
* provider/aws: Specify that aws_network_acl_rule requires a cidr block [GH-13013]
|
||||
* provider/google: turn compute_instance_group.instances into a set [GH-12790]
|
||||
* provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791]
|
||||
* core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. ([#12888](https://github.com/hashicorp/terraform/issues/12888))
|
||||
* provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) ([#12849](https://github.com/hashicorp/terraform/issues/12849))
|
||||
* provider/aws: Fix flattened cloudfront lambda function associations to be a set not a slice ([#11984](https://github.com/hashicorp/terraform/issues/11984))
|
||||
* provider/aws: Consider ACTIVE as pending state during ECS svc deletion ([#12986](https://github.com/hashicorp/terraform/issues/12986))
|
||||
* provider/aws: Deprecate the usage of Api Gateway Key Stages in favor of Usage Plans ([#12883](https://github.com/hashicorp/terraform/issues/12883))
|
||||
* provider/aws: prevent panic in resourceAwsSsmDocumentRead ([#12891](https://github.com/hashicorp/terraform/issues/12891))
|
||||
* provider/aws: Prevent panic when setting AWS CodeBuild Source to state ([#12915](https://github.com/hashicorp/terraform/issues/12915))
|
||||
* provider/aws: Only call replace Iam Instance Profile on existing machines ([#12922](https://github.com/hashicorp/terraform/issues/12922))
|
||||
* provider/aws: Increase AWS AMI Destroy timeout ([#12943](https://github.com/hashicorp/terraform/issues/12943))
|
||||
* provider/aws: Set aws_vpc ipv6 for associated only ([#12899](https://github.com/hashicorp/terraform/issues/12899))
|
||||
* provider/aws: Fix AWS ECS placement strategy spread fields ([#12998](https://github.com/hashicorp/terraform/issues/12998))
|
||||
* provider/aws: Specify that aws_network_acl_rule requires a cidr block ([#13013](https://github.com/hashicorp/terraform/issues/13013))
|
||||
* provider/aws: aws_network_acl_rule treat all and -1 for protocol the same ([#13049](https://github.com/hashicorp/terraform/issues/13049))
|
||||
* provider/aws: Only allow 1 value in alb_listener_rule condition ([#13051](https://github.com/hashicorp/terraform/issues/13051))
|
||||
* provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules ([#12835](https://github.com/hashicorp/terraform/issues/12835))
|
||||
* provider/aws: aws_ses_receipt_rule: fix off-by-one errors ([#12961](https://github.com/hashicorp/terraform/issues/12961))
|
||||
* provider/aws: Fix issue upgrading to Terraform v0.9+ with AWS OpsWorks Stacks ([#13024](https://github.com/hashicorp/terraform/issues/13024))
|
||||
* provider/fastly: Fix issue importing Fastly Services with Backends ([#12538](https://github.com/hashicorp/terraform/issues/12538))
|
||||
* provider/google: turn compute_instance_group.instances into a set ([#12790](https://github.com/hashicorp/terraform/issues/12790))
|
||||
* provider/mysql: recreate user/grant if user/grant got deleted manually ([#12791](https://github.com/hashicorp/terraform/issues/12791))
|
||||
* provider/openstack: Fix monitor_id typo in LBaaS v1 Pool ([#13069](https://github.com/hashicorp/terraform/issues/13069))
|
||||
* provider/openstack: Resolve issues with Port Fixed IPs ([#13056](https://github.com/hashicorp/terraform/issues/13056))
|
||||
* provider/rancher: error when no api_url is provided ([#13086](https://github.com/hashicorp/terraform/issues/13086))
|
||||
* provider/scaleway: work around parallel request limitation ([#13045](https://github.com/hashicorp/terraform/issues/13045))
|
||||
|
||||
## 0.9.1 (March 17, 2017)
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -152,3 +152,13 @@ $ tree ./pkg/ -P "terraform|*.zip"
|
|||
```
|
||||
|
||||
_Note: Cross-compilation uses [gox](https://github.com/mitchellh/gox), which requires toolchains to be built with versions of Go prior to 1.5. In order to successfully cross-compile with older versions of Go, you will need to run `gox -build-toolchain` before running the commands detailed above._
|
||||
|
||||
#### Docker
|
||||
|
||||
When using docker you don't need to have any of the Go development tools installed and you can clone terraform to any location on disk (doesn't have to be in your $GOPATH). This is useful for users who want to build `master` or a specific branch for testing without setting up a proper Go environment.
|
||||
|
||||
For example, run the following command to build terraform in a linux-based container for macOS.
|
||||
|
||||
```sh
|
||||
docker run --rm -v $(pwd):/go/src/github.com/hashicorp/terraform -w /go/src/github.com/hashicorp/terraform -e XC_OS=darwin -e XC_ARCH=amd64 golang:latest bash -c "apt-get update && apt-get install -y zip && make bin"
|
||||
```
|
||||
|
|
|
@ -2,6 +2,7 @@ package alicloud
|
|||
|
||||
import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
|
@ -12,8 +13,12 @@ const (
|
|||
VpcNet = InstanceNetWork("vpc")
|
||||
)
|
||||
|
||||
// timeout for common product, ecs e.g.
|
||||
const defaultTimeout = 120
|
||||
|
||||
// timeout for long time progerss product, rds e.g.
|
||||
const defaultLongTimeout = 800
|
||||
|
||||
func getRegion(d *schema.ResourceData, meta interface{}) common.Region {
|
||||
return meta.(*AliyunClient).Region
|
||||
}
|
||||
|
@ -50,3 +55,26 @@ func isProtocalValid(value string) bool {
|
|||
}
|
||||
return res
|
||||
}
|
||||
|
||||
var DefaultBusinessInfo = ecs.BusinessInfo{
|
||||
Pack: "terraform",
|
||||
}
|
||||
|
||||
// default region for all resource
|
||||
const DEFAULT_REGION = "cn-beijing"
|
||||
|
||||
// default security ip for db
|
||||
const DEFAULT_DB_SECURITY_IP = "127.0.0.1"
|
||||
|
||||
// we the count of create instance is only one
|
||||
const DEFAULT_INSTANCE_COUNT = 1
|
||||
|
||||
// symbol of multiIZ
|
||||
const MULTI_IZ_SYMBOL = "MAZ"
|
||||
|
||||
// default connect port of db
|
||||
const DB_DEFAULT_CONNECT_PORT = "3306"
|
||||
|
||||
const COMMA_SEPARATED = ","
|
||||
|
||||
const LOCAL_HOST_IP = "127.0.0.1"
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
)
|
||||
|
||||
|
@ -19,8 +20,11 @@ type Config struct {
|
|||
type AliyunClient struct {
|
||||
Region common.Region
|
||||
ecsconn *ecs.Client
|
||||
vpcconn *ecs.Client
|
||||
slbconn *slb.Client
|
||||
rdsconn *rds.Client
|
||||
// use new version
|
||||
ecsNewconn *ecs.Client
|
||||
vpcconn *ecs.Client
|
||||
slbconn *slb.Client
|
||||
}
|
||||
|
||||
// Client for AliyunClient
|
||||
|
@ -35,6 +39,17 @@ func (c *Config) Client() (*AliyunClient, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ecsNewconn, err := c.ecsConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecsNewconn.SetVersion(EcsApiVersion20160314)
|
||||
|
||||
rdsconn, err := c.rdsConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slbconn, err := c.slbConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -46,13 +61,17 @@ func (c *Config) Client() (*AliyunClient, error) {
|
|||
}
|
||||
|
||||
return &AliyunClient{
|
||||
Region: c.Region,
|
||||
ecsconn: ecsconn,
|
||||
vpcconn: vpcconn,
|
||||
slbconn: slbconn,
|
||||
Region: c.Region,
|
||||
ecsconn: ecsconn,
|
||||
ecsNewconn: ecsNewconn,
|
||||
vpcconn: vpcconn,
|
||||
slbconn: slbconn,
|
||||
rdsconn: rdsconn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
const BusinessInfoKey = "Terraform"
|
||||
|
||||
func (c *Config) loadAndValidate() error {
|
||||
err := c.validateRegion()
|
||||
if err != nil {
|
||||
|
@ -74,7 +93,9 @@ func (c *Config) validateRegion() error {
|
|||
}
|
||||
|
||||
func (c *Config) ecsConn() (*ecs.Client, error) {
|
||||
client := ecs.NewClient(c.AccessKey, c.SecretKey)
|
||||
client := ecs.NewECSClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
|
||||
_, err := client.DescribeRegions()
|
||||
|
||||
if err != nil {
|
||||
|
@ -84,20 +105,21 @@ func (c *Config) ecsConn() (*ecs.Client, error) {
|
|||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) slbConn() (*slb.Client, error) {
|
||||
client := slb.NewClient(c.AccessKey, c.SecretKey)
|
||||
func (c *Config) rdsConn() (*rds.Client, error) {
|
||||
client := rds.NewRDSClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) slbConn() (*slb.Client, error) {
|
||||
client := slb.NewSLBClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) vpcConn() (*ecs.Client, error) {
|
||||
_, err := c.ecsConn()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &ecs.Client{}
|
||||
client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey)
|
||||
client := ecs.NewVPCClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"time"
|
||||
)
|
||||
|
||||
func dataSourceAlicloudImages() *schema.Resource {
|
||||
|
@ -175,15 +175,28 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
|||
params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string))
|
||||
}
|
||||
|
||||
resp, _, err := conn.DescribeImages(params)
|
||||
if err != nil {
|
||||
return err
|
||||
var allImages []ecs.ImageType
|
||||
|
||||
for {
|
||||
images, paginationResult, err := conn.DescribeImages(params)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
allImages = append(allImages, images...)
|
||||
|
||||
pagination := paginationResult.NextPage()
|
||||
if pagination == nil {
|
||||
break
|
||||
}
|
||||
|
||||
params.Pagination = *pagination
|
||||
}
|
||||
|
||||
var filteredImages []ecs.ImageType
|
||||
if nameRegexOk {
|
||||
r := regexp.MustCompile(nameRegex.(string))
|
||||
for _, image := range resp {
|
||||
for _, image := range allImages {
|
||||
// Check for a very rare case where the response would include no
|
||||
// image name. No name means nothing to attempt a match against,
|
||||
// therefore we are skipping such image.
|
||||
|
@ -198,7 +211,7 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
|||
}
|
||||
}
|
||||
} else {
|
||||
filteredImages = resp[:]
|
||||
filteredImages = allImages[:]
|
||||
}
|
||||
|
||||
var images []ecs.ImageType
|
||||
|
|
|
@ -97,6 +97,22 @@ func TestAccAlicloudImagesDataSource_nameRegexFilter(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudImagesDataSource_imageNotInFirstPage(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_images.name_regex_filtered_image"),
|
||||
resource.TestMatchResourceAttr("data.alicloud_images.name_regex_filtered_image", "images.0.image_id", regexp.MustCompile("^ubuntu_14")),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Instance store test - using centos images
|
||||
const testAccCheckAlicloudImagesDataSourceImagesConfig = `
|
||||
data "alicloud_images" "multi_image" {
|
||||
|
@ -128,3 +144,12 @@ data "alicloud_images" "name_regex_filtered_image" {
|
|||
name_regex = "^centos_6\\w{1,5}[64]{1}.*"
|
||||
}
|
||||
`
|
||||
|
||||
// Testing image not in first page response
|
||||
const testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig = `
|
||||
data "alicloud_images" "name_regex_filtered_image" {
|
||||
most_recent = true
|
||||
owners = "system"
|
||||
name_regex = "^ubuntu_14.*_64"
|
||||
}
|
||||
`
|
||||
|
|
|
@ -17,8 +17,6 @@ func TestAccAlicloudInstanceTypesDataSource_basic(t *testing.T) {
|
|||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "4"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"),
|
||||
|
|
|
@ -71,11 +71,6 @@ func TestAccAlicloudRegionsDataSource_empty(t *testing.T) {
|
|||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "name", ""),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "current", ""),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.#", "13"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.id", "cn-shenzhen"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.region_id", "cn-shenzhen"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"),
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package alicloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -23,6 +26,7 @@ func TestAccAlicloudZonesDataSource_basic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
|
@ -33,7 +37,7 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
|||
Config: testAccCheckAlicloudZonesDataSourceFilter,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "2"),
|
||||
testCheckZoneLength("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
|
||||
|
@ -41,13 +45,59 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
|||
Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "1"),
|
||||
testCheckZoneLength("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudZonesDataSource_unitRegion(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckAlicloudZonesDataSource_unitRegion,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// the zone length changed occasionally
|
||||
// check by range to avoid test case failure
|
||||
func testCheckZoneLength(name string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
ms := s.RootModule()
|
||||
rs, ok := ms.Resources[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", name)
|
||||
}
|
||||
|
||||
is := rs.Primary
|
||||
if is == nil {
|
||||
return fmt.Errorf("No primary instance: %s", name)
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(is.Attributes["zones.#"])
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("convert zone length err: %#v", err)
|
||||
}
|
||||
|
||||
if i <= 0 {
|
||||
return fmt.Errorf("zone length expected greater than 0 got err: %d", i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccCheckAlicloudZonesDataSourceBasicConfig = `
|
||||
data "alicloud_zones" "foo" {
|
||||
}
|
||||
|
@ -55,16 +105,28 @@ data "alicloud_zones" "foo" {
|
|||
|
||||
const testAccCheckAlicloudZonesDataSourceFilter = `
|
||||
data "alicloud_zones" "foo" {
|
||||
"available_instance_type"= "ecs.c2.xlarge"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
available_instance_type= "ecs.c2.xlarge"
|
||||
available_resource_creation= "VSwitch"
|
||||
available_disk_category= "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = `
|
||||
data "alicloud_zones" "foo" {
|
||||
"available_instance_type"= "ecs.c2.xlarge"
|
||||
"available_resource_creation"= "IoOptimized"
|
||||
"available_disk_category"= "cloud"
|
||||
available_instance_type= "ecs.c2.xlarge"
|
||||
available_resource_creation= "IoOptimized"
|
||||
available_disk_category= "cloud"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCheckAlicloudZonesDataSource_unitRegion = `
|
||||
provider "alicloud" {
|
||||
alias = "northeast"
|
||||
region = "ap-northeast-1"
|
||||
}
|
||||
|
||||
data "alicloud_zones" "foo" {
|
||||
provider = "alicloud.northeast"
|
||||
available_resource_creation= "VSwitch"
|
||||
}
|
||||
`
|
||||
|
|
|
@ -9,6 +9,7 @@ const (
|
|||
DiskIncorrectStatus = "IncorrectDiskStatus"
|
||||
DiskCreatingSnapshot = "DiskCreatingSnapshot"
|
||||
InstanceLockedForSecurity = "InstanceLockedForSecurity"
|
||||
SystemDiskNotFound = "SystemDiskNotFound"
|
||||
// eip
|
||||
EipIncorrectStatus = "IncorrectEipStatus"
|
||||
InstanceIncorrectStatus = "IncorrectInstanceStatus"
|
||||
|
|
|
@ -30,3 +30,8 @@ const (
|
|||
GroupRulePolicyAccept = GroupRulePolicy("accept")
|
||||
GroupRulePolicyDrop = GroupRulePolicy("drop")
|
||||
)
|
||||
|
||||
const (
|
||||
EcsApiVersion20160314 = "2016-03-14"
|
||||
EcsApiVersion20140526 = "2014-05-26"
|
||||
)
|
||||
|
|
|
@ -8,13 +8,41 @@ import (
|
|||
)
|
||||
|
||||
type Listener struct {
|
||||
slb.HTTPListenerType
|
||||
|
||||
InstancePort int
|
||||
LoadBalancerPort int
|
||||
Protocol string
|
||||
//tcp & udp
|
||||
PersistenceTimeout int
|
||||
|
||||
//https
|
||||
SSLCertificateId string
|
||||
Bandwidth int
|
||||
|
||||
//tcp
|
||||
HealthCheckType slb.HealthCheckType
|
||||
|
||||
//api interface: http & https is HealthCheckTimeout, tcp & udp is HealthCheckConnectTimeout
|
||||
HealthCheckConnectTimeout int
|
||||
}
|
||||
|
||||
type ListenerErr struct {
|
||||
ErrType string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ListenerErr) Error() string {
|
||||
return e.ErrType + " " + e.Err.Error()
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
HealthCheckErrType = "healthCheckErrType"
|
||||
StickySessionErrType = "stickySessionErrType"
|
||||
CookieTimeOutErrType = "cookieTimeoutErrType"
|
||||
CookieErrType = "cookieErrType"
|
||||
)
|
||||
|
||||
// Takes the result of flatmap.Expand for an array of listeners and
|
||||
// returns ELB API compatible objects
|
||||
func expandListeners(configured []interface{}) ([]*Listener, error) {
|
||||
|
@ -31,13 +59,78 @@ func expandListeners(configured []interface{}) ([]*Listener, error) {
|
|||
InstancePort: ip,
|
||||
LoadBalancerPort: lp,
|
||||
Protocol: data["lb_protocol"].(string),
|
||||
Bandwidth: data["bandwidth"].(int),
|
||||
}
|
||||
|
||||
l.Bandwidth = data["bandwidth"].(int)
|
||||
|
||||
if v, ok := data["scheduler"]; ok {
|
||||
l.Scheduler = slb.SchedulerType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["ssl_certificate_id"]; ok {
|
||||
l.SSLCertificateId = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["sticky_session"]; ok {
|
||||
l.StickySession = slb.FlagType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["sticky_session_type"]; ok {
|
||||
l.StickySessionType = slb.StickySessionType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["cookie_timeout"]; ok {
|
||||
l.CookieTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["cookie"]; ok {
|
||||
l.Cookie = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["persistence_timeout"]; ok {
|
||||
l.PersistenceTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check"]; ok {
|
||||
l.HealthCheck = slb.FlagType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_type"]; ok {
|
||||
l.HealthCheckType = slb.HealthCheckType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_domain"]; ok {
|
||||
l.HealthCheckDomain = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_uri"]; ok {
|
||||
l.HealthCheckURI = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_connect_port"]; ok {
|
||||
l.HealthCheckConnectPort = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["healthy_threshold"]; ok {
|
||||
l.HealthyThreshold = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["unhealthy_threshold"]; ok {
|
||||
l.UnhealthyThreshold = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_timeout"]; ok {
|
||||
l.HealthCheckTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_interval"]; ok {
|
||||
l.HealthCheckInterval = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_http_code"]; ok {
|
||||
l.HealthCheckHttpCode = slb.HealthCheckHttpCodeType(v.(string))
|
||||
}
|
||||
|
||||
var valid bool
|
||||
if l.SSLCertificateId != "" {
|
||||
// validate the protocol is correct
|
||||
|
|
|
@ -26,7 +26,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"),
|
||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", DEFAULT_REGION),
|
||||
Description: descriptions["region"],
|
||||
},
|
||||
},
|
||||
|
@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"alicloud_disk_attachment": resourceAliyunDiskAttachment(),
|
||||
"alicloud_security_group": resourceAliyunSecurityGroup(),
|
||||
"alicloud_security_group_rule": resourceAliyunSecurityGroupRule(),
|
||||
"alicloud_db_instance": resourceAlicloudDBInstance(),
|
||||
"alicloud_vpc": resourceAliyunVpc(),
|
||||
"alicloud_nat_gateway": resourceAliyunNatGateway(),
|
||||
//both subnet and vswith exists,cause compatible old version, and compatible aws habit.
|
||||
|
|
|
@ -0,0 +1,545 @@
|
|||
package alicloud
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func resourceAlicloudDBInstance() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAlicloudDBInstanceCreate,
|
||||
Read: resourceAlicloudDBInstanceRead,
|
||||
Update: resourceAlicloudDBInstanceUpdate,
|
||||
Delete: resourceAlicloudDBInstanceDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"engine": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{"MySQL", "SQLServer", "PostgreSQL", "PPAS"}),
|
||||
ForceNew: true,
|
||||
Required: true,
|
||||
},
|
||||
"engine_version": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{"5.5", "5.6", "5.7", "2008r2", "2012", "9.4", "9.3"}),
|
||||
ForceNew: true,
|
||||
Required: true,
|
||||
},
|
||||
"db_instance_class": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"db_instance_storage": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"instance_charge_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{string(rds.Postpaid), string(rds.Prepaid)}),
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: rds.Postpaid,
|
||||
},
|
||||
"period": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateAllowedIntValue([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 24, 36}),
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: 1,
|
||||
},
|
||||
|
||||
"zone_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"multi_az": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"db_instance_net_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{string(common.Internet), string(common.Intranet)}),
|
||||
Optional: true,
|
||||
},
|
||||
"allocate_public_connection": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"instance_network_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{string(common.VPC), string(common.Classic)}),
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"vswitch_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ForceNew: true,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"master_user_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ForceNew: true,
|
||||
Optional: true,
|
||||
},
|
||||
"master_user_password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ForceNew: true,
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
|
||||
"preferred_backup_period": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
// terraform does not support ValidateFunc of TypeList attr
|
||||
// ValidateFunc: validateAllowedStringValue([]string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}),
|
||||
Optional: true,
|
||||
},
|
||||
"preferred_backup_time": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue(rds.BACKUP_TIME),
|
||||
Optional: true,
|
||||
},
|
||||
"backup_retention_period": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(7, 730),
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"security_ips": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"port": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"connections": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"connection_string": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"ip_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"ip_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"db_mappings": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"db_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"character_set_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue(rds.CHARACTER_SET_NAME),
|
||||
Required: true,
|
||||
},
|
||||
"db_description": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Optional: true,
|
||||
Set: resourceAlicloudDatabaseHash,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceAlicloudDatabaseHash(v interface{}) int {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
buf.WriteString(fmt.Sprintf("%s-", m["db_name"].(string)))
|
||||
buf.WriteString(fmt.Sprintf("%s-", m["character_set_name"].(string)))
|
||||
buf.WriteString(fmt.Sprintf("%s-", m["db_description"].(string)))
|
||||
|
||||
return hashcode.String(buf.String())
|
||||
}
|
||||
|
||||
func resourceAlicloudDBInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.rdsconn
|
||||
|
||||
args, err := buildDBCreateOrderArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := conn.CreateOrder(args)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Alicloud db instance: %#v", err)
|
||||
}
|
||||
|
||||
instanceId := resp.DBInstanceId
|
||||
if instanceId == "" {
|
||||
return fmt.Errorf("Error get Alicloud db instance id")
|
||||
}
|
||||
|
||||
d.SetId(instanceId)
|
||||
d.Set("instance_charge_type", d.Get("instance_charge_type"))
|
||||
d.Set("period", d.Get("period"))
|
||||
d.Set("period_type", d.Get("period_type"))
|
||||
|
||||
// wait instance status change from Creating to running
|
||||
if err := conn.WaitForInstance(d.Id(), rds.Running, defaultLongTimeout); err != nil {
|
||||
log.Printf("[DEBUG] WaitForInstance %s got error: %#v", rds.Running, err)
|
||||
}
|
||||
|
||||
if err := modifySecurityIps(d.Id(), d.Get("security_ips"), meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
masterUserName := d.Get("master_user_name").(string)
|
||||
masterUserPwd := d.Get("master_user_password").(string)
|
||||
if masterUserName != "" && masterUserPwd != "" {
|
||||
if err := client.CreateAccountByInfo(d.Id(), masterUserName, masterUserPwd); err != nil {
|
||||
return fmt.Errorf("Create db account %s error: %v", masterUserName, err)
|
||||
}
|
||||
}
|
||||
|
||||
if d.Get("allocate_public_connection").(bool) {
|
||||
if err := client.AllocateDBPublicConnection(d.Id(), DB_DEFAULT_CONNECT_PORT); err != nil {
|
||||
return fmt.Errorf("Allocate public connection error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return resourceAlicloudDBInstanceUpdate(d, meta)
|
||||
}
|
||||
|
||||
func modifySecurityIps(id string, ips interface{}, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
ipList := expandStringList(ips.([]interface{}))
|
||||
|
||||
ipstr := strings.Join(ipList[:], COMMA_SEPARATED)
|
||||
// default disable connect from outside
|
||||
if ipstr == "" {
|
||||
ipstr = LOCAL_HOST_IP
|
||||
}
|
||||
|
||||
if err := client.ModifyDBSecurityIps(id, ipstr); err != nil {
|
||||
return fmt.Errorf("Error modify security ips %s: %#v", ipstr, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAlicloudDBInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.rdsconn
|
||||
d.Partial(true)
|
||||
|
||||
if d.HasChange("db_mappings") {
|
||||
o, n := d.GetChange("db_mappings")
|
||||
os := o.(*schema.Set)
|
||||
ns := n.(*schema.Set)
|
||||
|
||||
var allDbs []string
|
||||
remove := os.Difference(ns).List()
|
||||
add := ns.Difference(os).List()
|
||||
|
||||
if len(remove) > 0 && len(add) > 0 {
|
||||
return fmt.Errorf("Failure modify database, we neither support create and delete database simultaneous nor modify database attributes.")
|
||||
}
|
||||
|
||||
if len(remove) > 0 {
|
||||
for _, db := range remove {
|
||||
dbm, _ := db.(map[string]interface{})
|
||||
if err := conn.DeleteDatabase(d.Id(), dbm["db_name"].(string)); err != nil {
|
||||
return fmt.Errorf("Failure delete database %s: %#v", dbm["db_name"].(string), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(add) > 0 {
|
||||
for _, db := range add {
|
||||
dbm, _ := db.(map[string]interface{})
|
||||
dbName := dbm["db_name"].(string)
|
||||
allDbs = append(allDbs, dbName)
|
||||
|
||||
if err := client.CreateDatabaseByInfo(d.Id(), dbName, dbm["character_set_name"].(string), dbm["db_description"].(string)); err != nil {
|
||||
return fmt.Errorf("Failure create database %s: %#v", dbName, err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if err := conn.WaitForAllDatabase(d.Id(), allDbs, rds.Running, 600); err != nil {
|
||||
return fmt.Errorf("Failure create database %#v", err)
|
||||
}
|
||||
|
||||
if user := d.Get("master_user_name").(string); user != "" {
|
||||
for _, dbName := range allDbs {
|
||||
if err := client.GrantDBPrivilege2Account(d.Id(), user, dbName); err != nil {
|
||||
return fmt.Errorf("Failed to grant database %s readwrite privilege to account %s: %#v", dbName, user, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.SetPartial("db_mappings")
|
||||
}
|
||||
|
||||
if d.HasChange("preferred_backup_period") || d.HasChange("preferred_backup_time") || d.HasChange("backup_retention_period") {
|
||||
period := d.Get("preferred_backup_period").([]interface{})
|
||||
periodList := expandStringList(period)
|
||||
time := d.Get("preferred_backup_time").(string)
|
||||
retention := d.Get("backup_retention_period").(int)
|
||||
|
||||
if time == "" || retention == 0 || len(periodList) < 1 {
|
||||
return fmt.Errorf("Both backup_time, backup_period and retention_period are required to set backup policy.")
|
||||
}
|
||||
|
||||
ps := strings.Join(periodList[:], COMMA_SEPARATED)
|
||||
|
||||
if err := client.ConfigDBBackup(d.Id(), time, ps, retention); err != nil {
|
||||
return fmt.Errorf("Error set backup policy: %#v", err)
|
||||
}
|
||||
d.SetPartial("preferred_backup_period")
|
||||
d.SetPartial("preferred_backup_time")
|
||||
d.SetPartial("backup_retention_period")
|
||||
}
|
||||
|
||||
if d.HasChange("security_ips") {
|
||||
if err := modifySecurityIps(d.Id(), d.Get("security_ips"), meta); err != nil {
|
||||
return err
|
||||
}
|
||||
d.SetPartial("security_ips")
|
||||
}
|
||||
|
||||
if d.HasChange("db_instance_class") || d.HasChange("db_instance_storage") {
|
||||
co, cn := d.GetChange("db_instance_class")
|
||||
so, sn := d.GetChange("db_instance_storage")
|
||||
classOld := co.(string)
|
||||
classNew := cn.(string)
|
||||
storageOld := so.(int)
|
||||
storageNew := sn.(int)
|
||||
|
||||
// update except the first time, because we will do it in create function
|
||||
if classOld != "" && storageOld != 0 {
|
||||
chargeType := d.Get("instance_charge_type").(string)
|
||||
if chargeType == string(rds.Prepaid) {
|
||||
return fmt.Errorf("Prepaid db instance does not support modify db_instance_class or db_instance_storage")
|
||||
}
|
||||
|
||||
if err := client.ModifyDBClassStorage(d.Id(), classNew, strconv.Itoa(storageNew)); err != nil {
|
||||
return fmt.Errorf("Error modify db instance class or storage error: %#v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.Partial(false)
|
||||
return resourceAlicloudDBInstanceRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAlicloudDBInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.rdsconn
|
||||
|
||||
instance, err := client.DescribeDBInstanceById(d.Id())
|
||||
if err != nil {
|
||||
if notFoundError(err) {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error Describe DB InstanceAttribute: %#v", err)
|
||||
}
|
||||
|
||||
args := rds.DescribeDatabasesArgs{
|
||||
DBInstanceId: d.Id(),
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeDatabases(&args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Set("db_mappings", flattenDatabaseMappings(resp.Databases.Database))
|
||||
|
||||
argn := rds.DescribeDBInstanceNetInfoArgs{
|
||||
DBInstanceId: d.Id(),
|
||||
}
|
||||
|
||||
resn, err := conn.DescribeDBInstanceNetInfo(&argn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Set("connections", flattenDBConnections(resn.DBInstanceNetInfos.DBInstanceNetInfo))
|
||||
|
||||
ips, err := client.GetSecurityIps(d.Id())
|
||||
if err != nil {
|
||||
log.Printf("Describe DB security ips error: %#v", err)
|
||||
}
|
||||
d.Set("security_ips", ips)
|
||||
|
||||
d.Set("engine", instance.Engine)
|
||||
d.Set("engine_version", instance.EngineVersion)
|
||||
d.Set("db_instance_class", instance.DBInstanceClass)
|
||||
d.Set("port", instance.Port)
|
||||
d.Set("db_instance_storage", instance.DBInstanceStorage)
|
||||
d.Set("zone_id", instance.ZoneId)
|
||||
d.Set("db_instance_net_type", instance.DBInstanceNetType)
|
||||
d.Set("instance_network_type", instance.InstanceNetworkType)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAlicloudDBInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).rdsconn
|
||||
|
||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
err := conn.DeleteInstance(d.Id())
|
||||
|
||||
if err != nil {
|
||||
return resource.RetryableError(fmt.Errorf("DB Instance in use - trying again while it is deleted."))
|
||||
}
|
||||
|
||||
args := &rds.DescribeDBInstancesArgs{
|
||||
DBInstanceId: d.Id(),
|
||||
}
|
||||
resp, err := conn.DescribeDBInstanceAttribute(args)
|
||||
if err != nil {
|
||||
return resource.NonRetryableError(err)
|
||||
} else if len(resp.Items.DBInstanceAttribute) < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return resource.RetryableError(fmt.Errorf("DB in use - trying again while it is deleted."))
|
||||
})
|
||||
}
|
||||
|
||||
func buildDBCreateOrderArgs(d *schema.ResourceData, meta interface{}) (*rds.CreateOrderArgs, error) {
|
||||
client := meta.(*AliyunClient)
|
||||
args := &rds.CreateOrderArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
// we does not expose this param to user,
|
||||
// because create prepaid instance progress will be stopped when set auto_pay to false,
|
||||
// then could not get instance info, cause timeout error
|
||||
AutoPay: "true",
|
||||
EngineVersion: d.Get("engine_version").(string),
|
||||
Engine: rds.Engine(d.Get("engine").(string)),
|
||||
DBInstanceStorage: d.Get("db_instance_storage").(int),
|
||||
DBInstanceClass: d.Get("db_instance_class").(string),
|
||||
Quantity: DEFAULT_INSTANCE_COUNT,
|
||||
Resource: rds.DefaultResource,
|
||||
}
|
||||
|
||||
bussStr, err := json.Marshal(DefaultBusinessInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo)
|
||||
}
|
||||
|
||||
args.BusinessInfo = string(bussStr)
|
||||
|
||||
zoneId := d.Get("zone_id").(string)
|
||||
args.ZoneId = zoneId
|
||||
|
||||
multiAZ := d.Get("multi_az").(bool)
|
||||
if multiAZ {
|
||||
if zoneId != "" {
|
||||
return nil, fmt.Errorf("You cannot set the ZoneId parameter when the MultiAZ parameter is set to true")
|
||||
}
|
||||
izs, err := client.DescribeMultiIZByRegion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Get multiAZ id error")
|
||||
}
|
||||
|
||||
if len(izs) < 1 {
|
||||
return nil, fmt.Errorf("Current region does not support MultiAZ.")
|
||||
}
|
||||
|
||||
args.ZoneId = izs[0]
|
||||
}
|
||||
|
||||
vswitchId := d.Get("vswitch_id").(string)
|
||||
|
||||
networkType := d.Get("instance_network_type").(string)
|
||||
args.InstanceNetworkType = common.NetworkType(networkType)
|
||||
|
||||
if vswitchId != "" {
|
||||
args.VSwitchId = vswitchId
|
||||
|
||||
// check InstanceNetworkType with vswitchId
|
||||
if networkType == string(common.Classic) {
|
||||
return nil, fmt.Errorf("When fill vswitchId, you shold set instance_network_type to VPC")
|
||||
} else if networkType == "" {
|
||||
args.InstanceNetworkType = common.VPC
|
||||
}
|
||||
|
||||
// get vpcId
|
||||
vpcId, err := client.GetVpcIdByVSwitchId(vswitchId)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("VswitchId %s is not valid of current region", vswitchId)
|
||||
}
|
||||
// fill vpcId by vswitchId
|
||||
args.VPCId = vpcId
|
||||
|
||||
// check vswitchId in zone
|
||||
vsw, err := client.QueryVswitchById(vpcId, vswitchId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("VswitchId %s is not valid of current region", vswitchId)
|
||||
}
|
||||
|
||||
if zoneId == "" {
|
||||
args.ZoneId = vsw.ZoneId
|
||||
} else if vsw.ZoneId != zoneId {
|
||||
return nil, fmt.Errorf("VswitchId %s is not belong to the zone %s", vswitchId, zoneId)
|
||||
}
|
||||
}
|
||||
|
||||
if v := d.Get("db_instance_net_type").(string); v != "" {
|
||||
args.DBInstanceNetType = common.NetType(v)
|
||||
}
|
||||
|
||||
chargeType := d.Get("instance_charge_type").(string)
|
||||
if chargeType != "" {
|
||||
args.PayType = rds.DBPayType(chargeType)
|
||||
} else {
|
||||
args.PayType = rds.Postpaid
|
||||
}
|
||||
|
||||
// if charge type is postpaid, the commodity code must set to bards
|
||||
if chargeType == string(rds.Postpaid) {
|
||||
args.CommodityCode = rds.Bards
|
||||
} else {
|
||||
args.CommodityCode = rds.Rds
|
||||
}
|
||||
|
||||
period := d.Get("period").(int)
|
||||
args.UsedTime, args.TimeType = TransformPeriod2Time(period, chargeType)
|
||||
|
||||
return args, nil
|
||||
}
|
|
@ -0,0 +1,765 @@
|
|||
package alicloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccAlicloudDBInstance_basic(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstanceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"Classic"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_vpc(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_vpc,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"VPC"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestC2CAlicloudDBInstance_prepaid_order(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_prepaid_order,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"VPC"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_multiIZ(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_multiIZ,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
testAccCheckDBInstanceMultiIZ(&instance),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_database(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_database,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_database_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "3"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_account(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_grantDatabasePrivilege2Account,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"),
|
||||
testAccCheckAccountHasPrivilege2Database("alicloud_db_instance.foo", "tester", "foo", "ReadWrite"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_allocatePublicConnection(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_allocatePublicConnection,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "connections.#", "2"),
|
||||
testAccCheckHasPublicConnection("alicloud_db_instance.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_backupPolicy(t *testing.T) {
|
||||
var policies []map[string]interface{}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_backup,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckBackupPolicyExists(
|
||||
"alicloud_db_instance.foo", policies),
|
||||
testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_period", "Wednesday,Thursday"),
|
||||
testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_time", "00:00Z-01:00Z"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_securityIps(t *testing.T) {
|
||||
var ips []map[string]interface{}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_securityIps,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityIpExists(
|
||||
"alicloud_db_instance.foo", ips),
|
||||
testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "127.0.0.1"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_securityIpsConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityIpExists(
|
||||
"alicloud_db_instance.foo", ips),
|
||||
testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "10.168.1.12,100.69.7.112"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_upgradeClass(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_class,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.t1.small"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_classUpgrade,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.s1.small"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckSecurityIpExists(n string, ips []map[string]interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
args := rds.DescribeDBInstanceIPsArgs{
|
||||
DBInstanceId: rs.Primary.ID,
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeDBInstanceIPs(&args)
|
||||
log.Printf("[DEBUG] check instance %s security ip %#v", rs.Primary.ID, resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := resp.Items.DBInstanceIPArray
|
||||
|
||||
if len(p) < 1 {
|
||||
return fmt.Errorf("DB security ip not found")
|
||||
}
|
||||
|
||||
ips = flattenDBSecurityIPs(p)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceMultiIZ(i *rds.DBInstanceAttribute) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if !strings.Contains(i.ZoneId, MULTI_IZ_SYMBOL) {
|
||||
return fmt.Errorf("Current region does not support multiIZ.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAccountHasPrivilege2Database(n, accountName, dbName, privilege string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
if err := conn.WaitForAccountPrivilege(rs.Primary.ID, accountName, dbName, rds.AccountPrivilege(privilege), 50); err != nil {
|
||||
return fmt.Errorf("Failed to grant database %s privilege to account %s: %v", dbName, accountName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHasPublicConnection(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
if err := conn.WaitForPublicConnection(rs.Primary.ID, 50); err != nil {
|
||||
return fmt.Errorf("Failed to allocate public connection: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceExists(n string, d *rds.DBInstanceAttribute) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
attr, err := client.DescribeDBInstanceById(rs.Primary.ID)
|
||||
log.Printf("[DEBUG] check instance %s attribute %#v", rs.Primary.ID, attr)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if attr == nil {
|
||||
return fmt.Errorf("DB Instance not found")
|
||||
}
|
||||
|
||||
*d = *attr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckBackupPolicyExists(n string, ps []map[string]interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Backup policy not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
|
||||
args := rds.DescribeBackupPolicyArgs{
|
||||
DBInstanceId: rs.Primary.ID,
|
||||
}
|
||||
resp, err := conn.DescribeBackupPolicy(&args)
|
||||
log.Printf("[DEBUG] check instance %s backup policy %#v", rs.Primary.ID, resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bs []rds.BackupPolicy
|
||||
bs = append(bs, resp.BackupPolicy)
|
||||
ps = flattenDBBackup(bs)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckKeyValueInMaps(ps []map[string]interface{}, propName, key, value string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
for _, policy := range ps {
|
||||
if policy[key].(string) != value {
|
||||
return fmt.Errorf("DB %s attribute '%s' expected %#v, got %#v", propName, key, value, policy[key])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "alicloud_db_instance.foo" {
|
||||
continue
|
||||
}
|
||||
|
||||
ins, err := client.DescribeDBInstanceById(rs.Primary.ID)
|
||||
|
||||
if ins != nil {
|
||||
return fmt.Errorf("Error DB Instance still exist")
|
||||
}
|
||||
|
||||
// Verify the error is what we want
|
||||
if err != nil {
|
||||
// Verify the error is what we want
|
||||
e, _ := err.(*common.Error)
|
||||
if e.ErrorResponse.Code == InstanceNotfound {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const testAccDBInstanceConfig = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_vpc = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_multiIZ = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
multi_az = true
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_prepaid_order = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Prepaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_database = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_database_update = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "zzz"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_grantDatabasePrivilege2Account = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
master_user_name = "tester"
|
||||
master_user_password = "Test12345"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_allocatePublicConnection = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
master_user_name = "tester"
|
||||
master_user_password = "Test12345"
|
||||
|
||||
allocate_public_connection = true
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_backup = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
preferred_backup_period = ["Wednesday","Thursday"]
|
||||
preferred_backup_time = "00:00Z-01:00Z"
|
||||
backup_retention_period = 9
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_securityIps = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_securityIpsConfig = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
security_ips = ["10.168.1.12", "100.69.7.112"]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_class = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_classUpgrade = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.s1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
|
@ -151,4 +151,5 @@ resource "alicloud_security_group" "group" {
|
|||
name = "terraform-test-group"
|
||||
description = "New security group"
|
||||
}
|
||||
|
||||
`
|
||||
|
|
|
@ -136,9 +136,13 @@ func testAccCheckDiskDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
const testAccDiskConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
}
|
||||
|
||||
resource "alicloud_disk" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
name = "New-disk"
|
||||
description = "Hello ecs disk."
|
||||
category = "cloud_efficiency"
|
||||
|
@ -146,10 +150,15 @@ resource "alicloud_disk" "foo" {
|
|||
}
|
||||
`
|
||||
const testAccDiskConfigWithTags = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
}
|
||||
|
||||
resource "alicloud_disk" "bar" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
size = "10"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
category = "cloud_efficiency"
|
||||
size = "20"
|
||||
tags {
|
||||
Name = "TerraformTest"
|
||||
}
|
||||
|
|
|
@ -108,6 +108,10 @@ func testAccCheckEIPAssociationDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
const testAccEIPAssociationConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "main" {
|
||||
cidr_block = "10.1.0.0/21"
|
||||
}
|
||||
|
@ -115,19 +119,23 @@ resource "alicloud_vpc" "main" {
|
|||
resource "alicloud_vswitch" "main" {
|
||||
vpc_id = "${alicloud_vpc.main.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-a"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
depends_on = [
|
||||
"alicloud_vpc.main"]
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "instance" {
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_type = "ecs.s1.small"
|
||||
availability_zone = "cn-beijing-a"
|
||||
security_groups = ["${alicloud_security_group.group.id}"]
|
||||
# cn-beijing
|
||||
vswitch_id = "${alicloud_vswitch.main.id}"
|
||||
instance_name = "hello"
|
||||
io_optimized = "none"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
|
||||
security_groups = ["${alicloud_security_group.group.id}"]
|
||||
instance_name = "test_foo"
|
||||
|
||||
tags {
|
||||
Name = "TerraformTest-instance"
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"log"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
@ -21,8 +22,9 @@ func resourceAliyunInstance() *schema.Resource {
|
|||
Schema: map[string]*schema.Schema{
|
||||
"availability_zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"image_id": &schema.Schema{
|
||||
|
@ -60,11 +62,6 @@ func resourceAliyunInstance() *schema.Resource {
|
|||
ValidateFunc: validateInstanceDescription,
|
||||
},
|
||||
|
||||
"instance_network_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"internet_charge_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -104,11 +101,19 @@ func resourceAliyunInstance() *schema.Resource {
|
|||
Default: "cloud",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(ecs.DiskCategoryCloud),
|
||||
string(ecs.DiskCategoryCloudSSD),
|
||||
string(ecs.DiskCategoryCloudEfficiency),
|
||||
string(ecs.DiskCategoryEphemeralSSD),
|
||||
}),
|
||||
},
|
||||
"system_disk_size": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateIntegerInRange(40, 500),
|
||||
},
|
||||
|
||||
//subnet_id and vswitch_id both exists, cause compatible old version, and aws habit.
|
||||
|
@ -145,7 +150,6 @@ func resourceAliyunInstance() *schema.Resource {
|
|||
|
||||
"private_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
|
@ -168,6 +172,11 @@ func resourceAliyunInstance() *schema.Resource {
|
|||
func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
|
||||
// create postpaid instance by runInstances API
|
||||
if v := d.Get("instance_charge_type").(string); v != string(common.PrePaid) {
|
||||
return resourceAliyunRunInstance(d, meta)
|
||||
}
|
||||
|
||||
args, err := buildAliyunInstanceArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -181,7 +190,8 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
|||
d.SetId(instanceID)
|
||||
|
||||
d.Set("password", d.Get("password"))
|
||||
d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
//d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
//d.Set("system_disk_size", d.Get("system_disk_size"))
|
||||
|
||||
if d.Get("allocate_public_ip").(bool) {
|
||||
_, err := conn.AllocatePublicIpAddress(d.Id())
|
||||
|
@ -207,11 +217,56 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
|||
return resourceAliyunInstanceUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceAliyunRunInstance(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
newConn := meta.(*AliyunClient).ecsNewconn
|
||||
|
||||
args, err := buildAliyunInstanceArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runArgs, err := buildAliyunRunInstancesArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runArgs.CreateInstanceArgs = *args
|
||||
|
||||
// runInstances is support in version 2016-03-14
|
||||
instanceIds, err := newConn.RunInstances(runArgs)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err)
|
||||
}
|
||||
|
||||
d.SetId(instanceIds[0])
|
||||
|
||||
d.Set("password", d.Get("password"))
|
||||
d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
d.Set("system_disk_size", d.Get("system_disk_size"))
|
||||
|
||||
if d.Get("allocate_public_ip").(bool) {
|
||||
_, err := conn.AllocatePublicIpAddress(d.Id())
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// after instance created, its status change from pending, starting to running
|
||||
if err := conn.WaitForInstanceAsyn(d.Id(), ecs.Running, defaultTimeout); err != nil {
|
||||
log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err)
|
||||
}
|
||||
|
||||
return resourceAliyunInstanceUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.ecsconn
|
||||
|
||||
instance, err := client.QueryInstancesById(d.Id())
|
||||
|
||||
if err != nil {
|
||||
if notFoundError(err) {
|
||||
d.SetId("")
|
||||
|
@ -220,7 +275,15 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||
return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeInstanceAttribute for instance: %#v", instance)
|
||||
disk, diskErr := client.QueryInstanceSystemDisk(d.Id())
|
||||
|
||||
if diskErr != nil {
|
||||
if notFoundError(diskErr) {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error DescribeSystemDisk: %#v", err)
|
||||
}
|
||||
|
||||
d.Set("instance_name", instance.InstanceName)
|
||||
d.Set("description", instance.Description)
|
||||
|
@ -229,6 +292,8 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||
d.Set("host_name", instance.HostName)
|
||||
d.Set("image_id", instance.ImageId)
|
||||
d.Set("instance_type", instance.InstanceType)
|
||||
d.Set("system_disk_category", disk.Category)
|
||||
d.Set("system_disk_size", disk.Size)
|
||||
|
||||
// In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'.
|
||||
// In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'.
|
||||
|
@ -244,10 +309,6 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
|||
d.Set("io_optimized", "none")
|
||||
}
|
||||
|
||||
log.Printf("instance.InternetChargeType: %#v", instance.InternetChargeType)
|
||||
|
||||
d.Set("instance_network_type", instance.InstanceNetworkType)
|
||||
|
||||
if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" {
|
||||
ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
d.Set("private_ip", ipAddress)
|
||||
|
@ -414,33 +475,71 @@ func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) erro
|
|||
|
||||
return nil
|
||||
}
|
||||
func buildAliyunRunInstancesArgs(d *schema.ResourceData, meta interface{}) (*ecs.RunInstanceArgs, error) {
|
||||
args := &ecs.RunInstanceArgs{
|
||||
MaxAmount: DEFAULT_INSTANCE_COUNT,
|
||||
MinAmount: DEFAULT_INSTANCE_COUNT,
|
||||
}
|
||||
|
||||
bussStr, err := json.Marshal(DefaultBusinessInfo)
|
||||
if err != nil {
|
||||
log.Printf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo)
|
||||
}
|
||||
|
||||
args.BusinessInfo = string(bussStr)
|
||||
|
||||
subnetValue := d.Get("subnet_id").(string)
|
||||
vswitchValue := d.Get("vswitch_id").(string)
|
||||
//networkValue := d.Get("instance_network_type").(string)
|
||||
|
||||
// because runInstance is not compatible with createInstance, force NetworkType value to classic
|
||||
if subnetValue == "" && vswitchValue == "" {
|
||||
args.NetworkType = string(ClassicNet)
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) {
|
||||
client := meta.(*AliyunClient)
|
||||
|
||||
args := &ecs.CreateInstanceArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
InstanceType: d.Get("instance_type").(string),
|
||||
PrivateIpAddress: d.Get("private_ip").(string),
|
||||
RegionId: getRegion(d, meta),
|
||||
InstanceType: d.Get("instance_type").(string),
|
||||
}
|
||||
|
||||
imageID := d.Get("image_id").(string)
|
||||
|
||||
args.ImageId = imageID
|
||||
|
||||
systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string))
|
||||
systemDiskSize := d.Get("system_disk_size").(int)
|
||||
|
||||
zoneID := d.Get("availability_zone").(string)
|
||||
// check instanceType and systemDiskCategory, when zoneID is not empty
|
||||
if zoneID != "" {
|
||||
zone, err := client.DescribeZone(zoneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args.ZoneId = zoneID
|
||||
|
||||
zone, err := client.DescribeZone(zoneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil {
|
||||
return nil, err
|
||||
args.SystemDisk = ecs.SystemDiskType{
|
||||
Category: systemDiskCategory,
|
||||
Size: systemDiskSize,
|
||||
}
|
||||
|
||||
args.ZoneId = zoneID
|
||||
|
||||
sgs, ok := d.GetOk("security_groups")
|
||||
|
||||
if ok {
|
||||
|
@ -451,17 +550,6 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
|||
if err == nil {
|
||||
args.SecurityGroupId = sg0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string))
|
||||
|
||||
if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args.SystemDisk = ecs.SystemDiskType{
|
||||
Category: systemDiskCategory,
|
||||
}
|
||||
|
||||
if v := d.Get("instance_name").(string); v != "" {
|
||||
|
@ -472,7 +560,7 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
|||
args.Description = v
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] internet_charge_type is %s", d.Get("internet_charge_type").(string))
|
||||
log.Printf("[DEBUG] SystemDisk is %d", systemDiskSize)
|
||||
if v := d.Get("internet_charge_type").(string); v != "" {
|
||||
args.InternetChargeType = common.InternetChargeType(v)
|
||||
}
|
||||
|
@ -490,7 +578,11 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
|||
}
|
||||
|
||||
if v := d.Get("io_optimized").(string); v != "" {
|
||||
args.IoOptimized = ecs.IoOptimized(v)
|
||||
if v == "optimized" {
|
||||
args.IoOptimized = ecs.IoOptimized("true")
|
||||
} else {
|
||||
args.IoOptimized = ecs.IoOptimized("false")
|
||||
}
|
||||
}
|
||||
|
||||
vswitchValue := d.Get("subnet_id").(string)
|
||||
|
|
|
@ -56,6 +56,7 @@ func TestAccAlicloudInstance_basic(t *testing.T) {
|
|||
"alicloud_instance.foo",
|
||||
"internet_charge_type",
|
||||
"PayByBandwidth"),
|
||||
testAccCheckSystemDiskSize("alicloud_instance.foo", 80),
|
||||
),
|
||||
},
|
||||
|
||||
|
@ -355,10 +356,6 @@ func TestAccAlicloudInstance_tags(t *testing.T) {
|
|||
Config: testAccCheckInstanceConfigTagsUpdate,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_instance.foo",
|
||||
"tags.foo",
|
||||
""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_instance.foo",
|
||||
"tags.bar",
|
||||
|
@ -418,8 +415,8 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) {
|
|||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
if privateIP != "172.16.0.229" {
|
||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
||||
if privateIP == "" {
|
||||
return fmt.Errorf("can't get private IP")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -445,14 +442,14 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||
func TestAccAlicloudInstance_associatePublicIP(t *testing.T) {
|
||||
var instance ecs.InstanceAttributesType
|
||||
|
||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
if privateIP != "172.16.0.229" {
|
||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
||||
if privateIP == "" {
|
||||
return fmt.Errorf("can't get private IP")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -468,7 +465,7 @@ func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
|||
CheckDestroy: testAccCheckInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP,
|
||||
Config: testAccInstanceConfigAssociatePublicIP,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||
testCheckPrivateIP(),
|
||||
|
@ -597,6 +594,36 @@ func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schem
|
|||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckSystemDiskSize(n string, size int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
providers := []*schema.Provider{testAccProvider}
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
for _, provider := range providers {
|
||||
if provider.Meta() == nil {
|
||||
continue
|
||||
}
|
||||
client := provider.Meta().(*AliyunClient)
|
||||
systemDisk, err := client.QueryInstanceSystemDisk(rs.Primary.ID)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR]get system disk size error: %#v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if systemDisk.Size != size {
|
||||
return fmt.Errorf("system disk size not equal %d, the instance system size is %d",
|
||||
size, systemDisk.Size)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccInstanceConfig = `
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
name = "tf_test_foo"
|
||||
|
@ -609,11 +636,10 @@ resource "alicloud_security_group" "tf_test_bar" {
|
|||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
system_disk_category = "cloud_ssd"
|
||||
system_disk_size = 80
|
||||
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
|
@ -628,15 +654,20 @@ resource "alicloud_instance" "foo" {
|
|||
}
|
||||
`
|
||||
const testAccInstanceConfigVPC = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
@ -647,7 +678,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
|
@ -666,15 +696,20 @@ resource "alicloud_instance" "foo" {
|
|||
`
|
||||
|
||||
const testAccInstanceConfigUserData = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
@ -685,7 +720,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
# series II
|
||||
|
@ -725,24 +759,22 @@ resource "alicloud_security_group" "tf_test_bar" {
|
|||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
provider = "alicloud.beijing"
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
# cn-beijing
|
||||
provider = "alicloud.beijing"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "bar" {
|
||||
# cn-shanghai
|
||||
provider = "alicloud.shanghai"
|
||||
availability_zone = "cn-shanghai-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
|
@ -768,7 +800,6 @@ resource "alicloud_security_group" "tf_test_bar" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
|
@ -776,6 +807,7 @@ resource "alicloud_instance" "foo" {
|
|||
security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}`
|
||||
|
||||
const testAccInstanceConfig_multiSecurityGroup_add = `
|
||||
|
@ -796,7 +828,6 @@ resource "alicloud_security_group" "tf_test_add_sg" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
|
@ -805,6 +836,7 @@ resource "alicloud_instance" "foo" {
|
|||
"${alicloud_security_group.tf_test_add_sg.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
|
@ -814,9 +846,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
|
@ -824,6 +877,7 @@ resource "alicloud_instance" "foo" {
|
|||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
|
@ -836,27 +890,32 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "none"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccInstanceNetworkInstanceSecurityGroups = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
@ -867,7 +926,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
|
@ -892,7 +950,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
@ -918,7 +975,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
@ -941,9 +997,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
@ -965,9 +1042,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
@ -984,15 +1082,20 @@ resource "alicloud_instance" "foo" {
|
|||
`
|
||||
|
||||
const testAccInstanceConfigPrivateIP = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
@ -1003,11 +1106,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
private_ip = "172.16.0.229"
|
||||
|
||||
# series II
|
||||
instance_type = "ecs.n1.medium"
|
||||
|
@ -1017,16 +1118,21 @@ resource "alicloud_instance" "foo" {
|
|||
instance_name = "test_foo"
|
||||
}
|
||||
`
|
||||
const testAccInstanceConfigAssociatePublicIPAndPrivateIP = `
|
||||
const testAccInstanceConfigAssociatePublicIP = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
@ -1037,11 +1143,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
private_ip = "172.16.0.229"
|
||||
allocate_public_ip = "true"
|
||||
internet_max_bandwidth_out = 5
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
|
@ -1055,52 +1159,56 @@ resource "alicloud_instance" "foo" {
|
|||
}
|
||||
`
|
||||
const testAccVpcInstanceWithSecurityRule = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-c"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
name = "tf_test_foo"
|
||||
description = "foo"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
name = "tf_test_foo"
|
||||
description = "foo"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ingress" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-c"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
# cn-beijing
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
allocate_public_ip = true
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
allocate_public_ip = true
|
||||
|
||||
# series II
|
||||
instance_charge_type = "PostPaid"
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_max_bandwidth_out = 5
|
||||
# series II
|
||||
instance_charge_type = "PostPaid"
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_max_bandwidth_out = 5
|
||||
|
||||
system_disk_category = "cloud_efficiency"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
}
|
||||
|
||||
`
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
|
@ -71,7 +72,7 @@ func resourceAliyunNatGateway() *schema.Resource {
|
|||
func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).vpcconn
|
||||
|
||||
args := &CreateNatGatewayArgs{
|
||||
args := &ecs.CreateNatGatewayArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
VpcId: d.Get("vpc_id").(string),
|
||||
Spec: d.Get("spec").(string),
|
||||
|
@ -79,11 +80,11 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
bandwidthPackages := d.Get("bandwidth_packages").([]interface{})
|
||||
|
||||
bandwidthPackageTypes := []BandwidthPackageType{}
|
||||
bandwidthPackageTypes := []ecs.BandwidthPackageType{}
|
||||
|
||||
for _, e := range bandwidthPackages {
|
||||
pack := e.(map[string]interface{})
|
||||
bandwidthPackage := BandwidthPackageType{
|
||||
bandwidthPackage := ecs.BandwidthPackageType{
|
||||
IpCount: pack["ip_count"].(int),
|
||||
Bandwidth: pack["bandwidth"].(int),
|
||||
}
|
||||
|
@ -106,8 +107,7 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
|||
if v, ok := d.GetOk("description"); ok {
|
||||
args.Description = v.(string)
|
||||
}
|
||||
|
||||
resp, err := CreateNatGateway(conn, args)
|
||||
resp, err := conn.CreateNatGateway(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreateNatGateway got error: %#v", err)
|
||||
}
|
||||
|
@ -142,6 +142,7 @@ func resourceAliyunNatGatewayRead(d *schema.ResourceData, meta interface{}) erro
|
|||
func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.vpcconn
|
||||
|
||||
natGateway, err := client.DescribeNatGateway(d.Id())
|
||||
if err != nil {
|
||||
|
@ -150,7 +151,7 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
d.Partial(true)
|
||||
attributeUpdate := false
|
||||
args := &ModifyNatGatewayAttributeArgs{
|
||||
args := &ecs.ModifyNatGatewayAttributeArgs{
|
||||
RegionId: natGateway.RegionId,
|
||||
NatGatewayId: natGateway.NatGatewayId,
|
||||
}
|
||||
|
@ -183,28 +184,28 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
}
|
||||
|
||||
if attributeUpdate {
|
||||
if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil {
|
||||
if err := conn.ModifyNatGatewayAttribute(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("spec") {
|
||||
d.SetPartial("spec")
|
||||
var spec NatGatewaySpec
|
||||
var spec ecs.NatGatewaySpec
|
||||
if v, ok := d.GetOk("spec"); ok {
|
||||
spec = NatGatewaySpec(v.(string))
|
||||
spec = ecs.NatGatewaySpec(v.(string))
|
||||
} else {
|
||||
// set default to small spec
|
||||
spec = NatGatewaySmallSpec
|
||||
spec = ecs.NatGatewaySmallSpec
|
||||
}
|
||||
|
||||
args := &ModifyNatGatewaySpecArgs{
|
||||
args := &ecs.ModifyNatGatewaySpecArgs{
|
||||
RegionId: natGateway.RegionId,
|
||||
NatGatewayId: natGateway.NatGatewayId,
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
err := ModifyNatGatewaySpec(client.vpcconn, args)
|
||||
err := conn.ModifyNatGatewaySpec(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%#v %#v", err, *args)
|
||||
}
|
||||
|
@ -218,10 +219,11 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.vpcconn
|
||||
|
||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
|
||||
packages, err := DescribeBandwidthPackages(client.vpcconn, &DescribeBandwidthPackagesArgs{
|
||||
packages, err := conn.DescribeBandwidthPackages(&ecs.DescribeBandwidthPackagesArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
NatGatewayId: d.Id(),
|
||||
})
|
||||
|
@ -232,7 +234,7 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
retry := false
|
||||
for _, pack := range packages {
|
||||
err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{
|
||||
err = conn.DeleteBandwidthPackage(&ecs.DeleteBandwidthPackageArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
BandwidthPackageId: pack.BandwidthPackageId,
|
||||
})
|
||||
|
@ -251,12 +253,12 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
|||
return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted."))
|
||||
}
|
||||
|
||||
args := &DeleteNatGatewayArgs{
|
||||
args := &ecs.DeleteNatGatewayArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: d.Id(),
|
||||
}
|
||||
|
||||
err = DeleteNatGateway(client.vpcconn, args)
|
||||
err = conn.DeleteNatGateway(args)
|
||||
if err != nil {
|
||||
er, _ := err.(*common.Error)
|
||||
if er.ErrorResponse.Code == DependencyViolationBandwidthPackages {
|
||||
|
@ -264,11 +266,11 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
|||
}
|
||||
}
|
||||
|
||||
describeArgs := &DescribeNatGatewaysArgs{
|
||||
describeArgs := &ecs.DescribeNatGatewaysArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: d.Id(),
|
||||
}
|
||||
gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs)
|
||||
gw, _, gwErr := conn.DescribeNatGateways(describeArgs)
|
||||
|
||||
if gwErr != nil {
|
||||
log.Printf("[ERROR] Describe NatGateways failed.")
|
||||
|
|
|
@ -3,13 +3,14 @@ package alicloud
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
||||
var nat NatGatewaySetType
|
||||
var nat ecs.NatGatewaySetType
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
if nat.BusinessStatus != "Normal" {
|
||||
|
@ -55,7 +56,7 @@ func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
||||
var nat NatGatewaySetType
|
||||
var nat ecs.NatGatewaySetType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
|
@ -95,7 +96,7 @@ func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func testAccCheckNatGatewayExists(n string, nat *NatGatewaySetType) resource.TestCheckFunc {
|
||||
func testAccCheckNatGatewayExists(n string, nat *ecs.NatGatewaySetType) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -151,6 +152,10 @@ func testAccCheckNatGatewayDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
const testAccNatGatewayConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
|
@ -159,7 +164,7 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
|
@ -169,11 +174,11 @@ resource "alicloud_nat_gateway" "foo" {
|
|||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
|
@ -181,6 +186,10 @@ resource "alicloud_nat_gateway" "foo" {
|
|||
`
|
||||
|
||||
const testAccNatGatewayConfigSpec = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
|
@ -189,7 +198,7 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
|
@ -199,11 +208,11 @@ resource "alicloud_nat_gateway" "foo" {
|
|||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
|
@ -211,6 +220,10 @@ resource "alicloud_nat_gateway" "foo" {
|
|||
`
|
||||
|
||||
const testAccNatGatewayConfigSpecUpgrade = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
|
@ -219,7 +232,7 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
|
@ -229,11 +242,11 @@ resource "alicloud_nat_gateway" "foo" {
|
|||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"time"
|
||||
)
|
||||
|
@ -145,6 +144,7 @@ func resourceAliyunSecurityGroupDelete(d *schema.ResourceData, meta interface{})
|
|||
|
||||
return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted."))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) {
|
||||
|
|
|
@ -34,6 +34,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
|||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validateSecurityRuleNicType,
|
||||
},
|
||||
|
||||
|
@ -67,7 +68,6 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
|||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "0.0.0.0/0",
|
||||
},
|
||||
|
||||
"source_security_group_id": &schema.Schema{
|
||||
|
@ -86,15 +86,17 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
|||
}
|
||||
|
||||
func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.ecsconn
|
||||
|
||||
ruleType := d.Get("type").(string)
|
||||
direction := d.Get("type").(string)
|
||||
sgId := d.Get("security_group_id").(string)
|
||||
ptl := d.Get("ip_protocol").(string)
|
||||
port := d.Get("port_range").(string)
|
||||
nicType := d.Get("nic_type").(string)
|
||||
|
||||
var autherr error
|
||||
switch GroupRuleDirection(ruleType) {
|
||||
switch GroupRuleDirection(direction) {
|
||||
case GroupRuleIngress:
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
if err != nil {
|
||||
|
@ -114,10 +116,11 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac
|
|||
if autherr != nil {
|
||||
return fmt.Errorf(
|
||||
"Error authorizing security group rule type %s: %s",
|
||||
ruleType, autherr)
|
||||
direction, autherr)
|
||||
}
|
||||
|
||||
d.SetId(sgId + ":" + ruleType + ":" + ptl + ":" + port)
|
||||
d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType)
|
||||
|
||||
return resourceAliyunSecurityGroupRuleRead(d, meta)
|
||||
}
|
||||
|
||||
|
@ -125,10 +128,11 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
|||
client := meta.(*AliyunClient)
|
||||
parts := strings.Split(d.Id(), ":")
|
||||
sgId := parts[0]
|
||||
types := parts[1]
|
||||
direction := parts[1]
|
||||
ip_protocol := parts[2]
|
||||
port_range := parts[3]
|
||||
rule, err := client.DescribeSecurityGroupRule(sgId, types, ip_protocol, port_range)
|
||||
nic_type := parts[4]
|
||||
rule, err := client.DescribeSecurityGroupRule(sgId, direction, nic_type, ip_protocol, port_range)
|
||||
|
||||
if err != nil {
|
||||
if notFoundError(err) {
|
||||
|
@ -137,7 +141,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
|||
}
|
||||
return fmt.Errorf("Error SecurityGroup rule: %#v", err)
|
||||
}
|
||||
log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, types, ip_protocol, port_range, rule)
|
||||
log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, direction, ip_protocol, port_range, rule)
|
||||
d.Set("type", rule.Direction)
|
||||
d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol)))
|
||||
d.Set("nic_type", rule.NicType)
|
||||
|
@ -146,7 +150,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
|||
d.Set("priority", rule.Priority)
|
||||
d.Set("security_group_id", sgId)
|
||||
//support source and desc by type
|
||||
if GroupRuleDirection(types) == GroupRuleIngress {
|
||||
if GroupRuleDirection(direction) == GroupRuleIngress {
|
||||
d.Set("cidr_ip", rule.SourceCidrIp)
|
||||
d.Set("source_security_group_id", rule.SourceGroupId)
|
||||
d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount)
|
||||
|
@ -161,17 +165,41 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
|||
|
||||
func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
ruleType := d.Get("type").(string)
|
||||
|
||||
if GroupRuleDirection(ruleType) == GroupRuleIngress {
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokeArgs := &ecs.RevokeSecurityGroupArgs{
|
||||
AuthorizeSecurityGroupArgs: *args,
|
||||
}
|
||||
return client.RevokeSecurityGroup(revokeArgs)
|
||||
}
|
||||
|
||||
args, err := buildAliyunSecurityEgressArgs(d, meta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokeArgs := &ecs.RevokeSecurityGroupArgs{
|
||||
AuthorizeSecurityGroupArgs: *args,
|
||||
revokeArgs := &ecs.RevokeSecurityGroupEgressArgs{
|
||||
AuthorizeSecurityGroupEgressArgs: *args,
|
||||
}
|
||||
return client.RevokeSecurityGroup(revokeArgs)
|
||||
return client.RevokeSecurityGroupEgress(revokeArgs)
|
||||
|
||||
}
|
||||
|
||||
func checkCidrAndSourceGroupId(cidrIp, sourceGroupId string) error {
|
||||
if cidrIp == "" && sourceGroupId == "" {
|
||||
return fmt.Errorf("Either cidr_ip or source_security_group_id is required.")
|
||||
}
|
||||
|
||||
if cidrIp != "" && sourceGroupId != "" {
|
||||
return fmt.Errorf("You should set only one value of cidr_ip or source_security_group_id.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
|
||||
|
@ -199,12 +227,17 @@ func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*
|
|||
args.NicType = ecs.NicType(v)
|
||||
}
|
||||
|
||||
if v := d.Get("cidr_ip").(string); v != "" {
|
||||
args.SourceCidrIp = v
|
||||
cidrIp := d.Get("cidr_ip").(string)
|
||||
sourceGroupId := d.Get("source_security_group_id").(string)
|
||||
if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cidrIp != "" {
|
||||
args.SourceCidrIp = cidrIp
|
||||
}
|
||||
|
||||
if v := d.Get("source_security_group_id").(string); v != "" {
|
||||
args.SourceGroupId = v
|
||||
if sourceGroupId != "" {
|
||||
args.SourceGroupId = sourceGroupId
|
||||
}
|
||||
|
||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
||||
|
@ -255,12 +288,17 @@ func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*e
|
|||
args.NicType = ecs.NicType(v)
|
||||
}
|
||||
|
||||
if v := d.Get("cidr_ip").(string); v != "" {
|
||||
args.DestCidrIp = v
|
||||
cidrIp := d.Get("cidr_ip").(string)
|
||||
sourceGroupId := d.Get("source_security_group_id").(string)
|
||||
if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cidrIp != "" {
|
||||
args.DestCidrIp = cidrIp
|
||||
}
|
||||
|
||||
if v := d.Get("source_security_group_id").(string); v != "" {
|
||||
args.DestGroupId = v
|
||||
if sourceGroupId != "" {
|
||||
args.DestGroupId = sourceGroupId
|
||||
}
|
||||
|
||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -81,6 +82,39 @@ func TestAccAlicloudSecurityGroupRule_Egress(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_EgressDefaultNicType(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.egress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRuleEgress_emptyNicType,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.egress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"port_range",
|
||||
"80/80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"nic_type",
|
||||
"internet"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
|
@ -114,6 +148,80 @@ func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_MissParameterSourceCidrIp(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.egress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRule_missingSourceCidrIp,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.egress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"port_range",
|
||||
"80/80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"nic_type",
|
||||
"internet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"ip_protocol",
|
||||
"udp"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_SourceSecurityGroup(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.ingress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRuleSourceSecurityGroup,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.ingress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"port_range",
|
||||
"3306/3306"),
|
||||
resource.TestMatchResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"source_security_group_id",
|
||||
regexp.MustCompile("^sg-[a-zA-Z0-9_]+")),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"cidr_ip",
|
||||
""),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -128,7 +236,8 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour
|
|||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
log.Printf("[WARN]get sg rule %s", rs.Primary.ID)
|
||||
parts := strings.Split(rs.Primary.ID, ":")
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3])
|
||||
// securityGroupId, direction, nicType, ipProtocol, portRange
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -152,7 +261,7 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
parts := strings.Split(rs.Primary.ID, ":")
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3])
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3])
|
||||
|
||||
if rule != nil {
|
||||
return fmt.Errorf("Error SecurityGroup Rule still exist")
|
||||
|
@ -210,6 +319,23 @@ resource "alicloud_security_group_rule" "egress" {
|
|||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleEgress_emptyNicType = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "egress" {
|
||||
type = "egress"
|
||||
ip_protocol = "udp"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "10.159.6.18/12"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleVpcIngress = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
vpc_id = "${alicloud_vpc.vpc.id}"
|
||||
|
@ -231,6 +357,22 @@ resource "alicloud_security_group_rule" "ingress" {
|
|||
cidr_ip = "10.159.6.18/12"
|
||||
}
|
||||
|
||||
`
|
||||
const testAccSecurityGroupRule_missingSourceCidrIp = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "egress" {
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
type = "egress"
|
||||
cidr_ip= "0.0.0.0/0"
|
||||
policy = "accept"
|
||||
ip_protocol= "udp"
|
||||
port_range= "80/80"
|
||||
priority= 1
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleMultiIngress = `
|
||||
|
@ -260,4 +402,27 @@ resource "alicloud_security_group_rule" "ingress2" {
|
|||
cidr_ip = "127.0.1.18/16"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleSourceSecurityGroup = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "bar" {
|
||||
name = "sg_bar"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ingress" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "3306/3306"
|
||||
priority = 50
|
||||
security_group_id = "${alicloud_security_group.bar.id}"
|
||||
source_security_group_id = "${alicloud_security_group.foo.id}"
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
|
@ -83,40 +84,124 @@ func resourceAliyunSlb() *schema.Resource {
|
|||
ValidateFunc: validateSlbListenerBandwidth,
|
||||
Required: true,
|
||||
},
|
||||
//http
|
||||
"scheduler": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerScheduler,
|
||||
Optional: true,
|
||||
Default: "wrr",
|
||||
Default: slb.WRRScheduler,
|
||||
},
|
||||
|
||||
//http & https
|
||||
"sticky_session": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerStickySession,
|
||||
Optional: true,
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.OnFlag),
|
||||
string(slb.OffFlag)}),
|
||||
Optional: true,
|
||||
Default: slb.OffFlag,
|
||||
},
|
||||
//http & https
|
||||
"sticky_session_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerStickySessionType,
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.InsertStickySessionType),
|
||||
string(slb.ServerStickySessionType)}),
|
||||
Optional: true,
|
||||
},
|
||||
//http & https
|
||||
"cookie_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerCookieTimeout,
|
||||
Optional: true,
|
||||
},
|
||||
//http & https
|
||||
"cookie": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerCookie,
|
||||
Optional: true,
|
||||
},
|
||||
"PersistenceTimeout": &schema.Schema{
|
||||
//tcp & udp
|
||||
"persistence_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerPersistenceTimeout,
|
||||
Optional: true,
|
||||
Default: 0,
|
||||
},
|
||||
//http & https
|
||||
"health_check": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.OnFlag),
|
||||
string(slb.OffFlag)}),
|
||||
Optional: true,
|
||||
Default: slb.OffFlag,
|
||||
},
|
||||
//tcp
|
||||
"health_check_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.TCPHealthCheckType),
|
||||
string(slb.HTTPHealthCheckType)}),
|
||||
Optional: true,
|
||||
Default: slb.TCPHealthCheckType,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_domain": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerHealthCheckDomain,
|
||||
Optional: true,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_uri": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerHealthCheckUri,
|
||||
Optional: true,
|
||||
},
|
||||
"health_check_connect_port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerHealthCheckConnectPort,
|
||||
Optional: true,
|
||||
},
|
||||
"healthy_threshold": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 10),
|
||||
Optional: true,
|
||||
},
|
||||
"unhealthy_threshold": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 10),
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"health_check_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 50),
|
||||
Optional: true,
|
||||
},
|
||||
"health_check_interval": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 5),
|
||||
Optional: true,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_http_code": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedSplitStringValue([]string{
|
||||
string(slb.HTTP_2XX),
|
||||
string(slb.HTTP_3XX),
|
||||
string(slb.HTTP_4XX),
|
||||
string(slb.HTTP_5XX)}, ","),
|
||||
Optional: true,
|
||||
},
|
||||
//https
|
||||
"ssl_certificate_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
//https
|
||||
//"ca_certificate_id": &schema.Schema{
|
||||
// Type: schema.TypeString,
|
||||
// Optional: true,
|
||||
//},
|
||||
},
|
||||
},
|
||||
Set: resourceAliyunSlbListenerHash,
|
||||
|
@ -349,44 +434,53 @@ func resourceAliyunSlbListenerHash(v interface{}) int {
|
|||
}
|
||||
|
||||
func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
|
||||
|
||||
errTypeJudge := func(err error) error {
|
||||
if err != nil {
|
||||
if listenerType, ok := err.(*ListenerErr); ok {
|
||||
if listenerType.ErrType == HealthCheckErrType {
|
||||
return fmt.Errorf("When the HealthCheck is %s, then related HealthCheck parameter "+
|
||||
"must have.", slb.OnFlag)
|
||||
} else if listenerType.ErrType == StickySessionErrType {
|
||||
return fmt.Errorf("When the StickySession is %s, then StickySessionType parameter "+
|
||||
"must have.", slb.OnFlag)
|
||||
} else if listenerType.ErrType == CookieTimeOutErrType {
|
||||
return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
|
||||
"then CookieTimeout parameter must have.", slb.OnFlag, slb.InsertStickySessionType)
|
||||
} else if listenerType.ErrType == CookieErrType {
|
||||
return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
|
||||
"then Cookie parameter must have.", slb.OnFlag, slb.ServerStickySessionType)
|
||||
}
|
||||
return fmt.Errorf("slb listener check errtype not found.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("tcp") {
|
||||
args := &slb.CreateLoadBalancerTCPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
}
|
||||
if err := conn.CreateLoadBalancerTCPListener(args); err != nil {
|
||||
|
||||
args := getTcpListenerArgs(loadBalancerId, listener)
|
||||
|
||||
if err := conn.CreateLoadBalancerTCPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("http") {
|
||||
args := &slb.CreateLoadBalancerHTTPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
StickySession: slb.OffFlag,
|
||||
HealthCheck: slb.OffFlag,
|
||||
} else if listener.Protocol == strings.ToLower("http") {
|
||||
args, argsErr := getHttpListenerArgs(loadBalancerId, listener)
|
||||
if paramErr := errTypeJudge(argsErr); paramErr != nil {
|
||||
return paramErr
|
||||
}
|
||||
|
||||
if err := conn.CreateLoadBalancerHTTPListener(args); err != nil {
|
||||
if err := conn.CreateLoadBalancerHTTPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if listener.Protocol == strings.ToLower("https") {
|
||||
listenerType, err := getHttpListenerType(loadBalancerId, listener)
|
||||
if paramErr := errTypeJudge(err); paramErr != nil {
|
||||
return paramErr
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("https") {
|
||||
args := &slb.CreateLoadBalancerHTTPSListenerArgs{
|
||||
|
||||
HTTPListenerType: slb.HTTPListenerType{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
StickySession: slb.OffFlag,
|
||||
HealthCheck: slb.OffFlag,
|
||||
},
|
||||
HTTPListenerType: listenerType,
|
||||
}
|
||||
if listener.SSLCertificateId == "" {
|
||||
return fmt.Errorf("Server Certificated Id cann't be null")
|
||||
|
@ -397,17 +491,10 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener)
|
|||
if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if listener.Protocol == strings.ToLower("udp") {
|
||||
args := getUdpListenerArgs(loadBalancerId, listener)
|
||||
|
||||
if listener.Protocol == strings.ToLower("udp") {
|
||||
args := &slb.CreateLoadBalancerUDPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
}
|
||||
|
||||
if err := conn.CreateLoadBalancerUDPListener(args); err != nil {
|
||||
if err := conn.CreateLoadBalancerUDPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -418,3 +505,102 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener)
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTcpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerTCPListenerArgs {
|
||||
args := slb.CreateLoadBalancerTCPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
Scheduler: listener.Scheduler,
|
||||
PersistenceTimeout: listener.PersistenceTimeout,
|
||||
HealthCheckType: listener.HealthCheckType,
|
||||
HealthCheckDomain: listener.HealthCheckDomain,
|
||||
HealthCheckURI: listener.HealthCheckURI,
|
||||
HealthCheckConnectPort: listener.HealthCheckConnectPort,
|
||||
HealthyThreshold: listener.HealthyThreshold,
|
||||
UnhealthyThreshold: listener.UnhealthyThreshold,
|
||||
HealthCheckConnectTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
HealthCheckHttpCode: listener.HealthCheckHttpCode,
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func getUdpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerUDPListenerArgs {
|
||||
args := slb.CreateLoadBalancerUDPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
PersistenceTimeout: listener.PersistenceTimeout,
|
||||
HealthCheckConnectTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func getHttpListenerType(loadBalancerId string, listener *Listener) (listenType slb.HTTPListenerType, err error) {
|
||||
|
||||
if listener.HealthCheck == slb.OnFlag {
|
||||
if listener.HealthCheckURI == "" || listener.HealthCheckDomain == "" || listener.HealthCheckConnectPort == 0 ||
|
||||
listener.HealthyThreshold == 0 || listener.UnhealthyThreshold == 0 || listener.HealthCheckTimeout == 0 ||
|
||||
listener.HealthCheckHttpCode == "" || listener.HealthCheckInterval == 0 {
|
||||
|
||||
errMsg := errors.New("err: HealthCheck empty.")
|
||||
return listenType, &ListenerErr{HealthCheckErrType, errMsg}
|
||||
}
|
||||
}
|
||||
|
||||
if listener.StickySession == slb.OnFlag {
|
||||
if listener.StickySessionType == "" {
|
||||
errMsg := errors.New("err: stickySession empty.")
|
||||
return listenType, &ListenerErr{StickySessionErrType, errMsg}
|
||||
}
|
||||
|
||||
if listener.StickySessionType == slb.InsertStickySessionType {
|
||||
if listener.CookieTimeout == 0 {
|
||||
errMsg := errors.New("err: cookieTimeout empty.")
|
||||
return listenType, &ListenerErr{CookieTimeOutErrType, errMsg}
|
||||
}
|
||||
} else if listener.StickySessionType == slb.ServerStickySessionType {
|
||||
if listener.Cookie == "" {
|
||||
errMsg := errors.New("err: cookie empty.")
|
||||
return listenType, &ListenerErr{CookieErrType, errMsg}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpListenertType := slb.HTTPListenerType{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
Scheduler: listener.Scheduler,
|
||||
HealthCheck: listener.HealthCheck,
|
||||
StickySession: listener.StickySession,
|
||||
StickySessionType: listener.StickySessionType,
|
||||
CookieTimeout: listener.CookieTimeout,
|
||||
Cookie: listener.Cookie,
|
||||
HealthCheckDomain: listener.HealthCheckDomain,
|
||||
HealthCheckURI: listener.HealthCheckURI,
|
||||
HealthCheckConnectPort: listener.HealthCheckConnectPort,
|
||||
HealthyThreshold: listener.HealthyThreshold,
|
||||
UnhealthyThreshold: listener.UnhealthyThreshold,
|
||||
HealthCheckTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
HealthCheckHttpCode: listener.HealthCheckHttpCode,
|
||||
}
|
||||
|
||||
return httpListenertType, err
|
||||
}
|
||||
|
||||
func getHttpListenerArgs(loadBalancerId string, listener *Listener) (listenType slb.CreateLoadBalancerHTTPListenerArgs, err error) {
|
||||
httpListenerType, err := getHttpListenerType(loadBalancerId, listener)
|
||||
if err != nil {
|
||||
return listenType, err
|
||||
}
|
||||
|
||||
httpArgs := slb.CreateLoadBalancerHTTPListenerArgs(httpListenerType)
|
||||
return httpArgs, err
|
||||
}
|
||||
|
|
|
@ -79,9 +79,30 @@ resource "alicloud_security_group" "foo" {
|
|||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
|
|
@ -85,7 +85,7 @@ func TestAccAlicloudSlb_listener(t *testing.T) {
|
|||
testListener := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
listenerPorts := slb.ListenerPorts.ListenerPort[0]
|
||||
if listenerPorts != 161 {
|
||||
if listenerPorts != 2001 {
|
||||
return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts)
|
||||
}
|
||||
|
||||
|
@ -260,21 +260,49 @@ resource "alicloud_slb" "listener" {
|
|||
"lb_port" = "21"
|
||||
"lb_protocol" = "tcp"
|
||||
"bandwidth" = 1
|
||||
"persistence_timeout" = 500
|
||||
"health_check_type" = "http"
|
||||
},{
|
||||
"instance_port" = "8000"
|
||||
"lb_port" = "80"
|
||||
"lb_protocol" = "http"
|
||||
"sticky_session" = "on"
|
||||
"sticky_session_type" = "insert"
|
||||
"cookie_timeout" = 800
|
||||
"bandwidth" = 1
|
||||
},{
|
||||
"instance_port" = "1611"
|
||||
"lb_port" = "161"
|
||||
"instance_port" = "8001"
|
||||
"lb_port" = "81"
|
||||
"lb_protocol" = "http"
|
||||
"sticky_session" = "on"
|
||||
"sticky_session_type" = "server"
|
||||
"cookie" = "testslblistenercookie"
|
||||
"cookie_timeout" = 1800
|
||||
"health_check" = "on"
|
||||
"health_check_domain" = "$_ip"
|
||||
"health_check_uri" = "/console"
|
||||
"health_check_connect_port" = 20
|
||||
"healthy_threshold" = 8
|
||||
"unhealthy_threshold" = 8
|
||||
"health_check_timeout" = 8
|
||||
"health_check_interval" = 4
|
||||
"health_check_http_code" = "http_2xx"
|
||||
"bandwidth" = 1
|
||||
},{
|
||||
"instance_port" = "2001"
|
||||
"lb_port" = "2001"
|
||||
"lb_protocol" = "udp"
|
||||
"bandwidth" = 1
|
||||
"persistence_timeout" = 700
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccSlb4Vpc = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
|
@ -283,7 +311,7 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_slb" "vpc" {
|
||||
|
|
|
@ -124,6 +124,10 @@ func testAccCheckRouteEntryDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
const testAccRouteEntryConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
|
@ -132,7 +136,7 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-c"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_route_entry" "foo" {
|
||||
|
@ -162,7 +166,6 @@ resource "alicloud_security_group_rule" "ingress" {
|
|||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-c"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
|
|
|
@ -92,6 +92,10 @@ func testAccCheckVswitchDestroy(s *terraform.State) error {
|
|||
}
|
||||
|
||||
const testAccVswitchConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
|
@ -100,6 +104,6 @@ resource "alicloud_vpc" "foo" {
|
|||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
`
|
||||
|
|
|
@ -84,6 +84,24 @@ func (client *AliyunClient) DescribeZone(zoneID string) (*ecs.ZoneType, error) {
|
|||
return zone, nil
|
||||
}
|
||||
|
||||
// return multiIZ list of current region
|
||||
func (client *AliyunClient) DescribeMultiIZByRegion() (izs []string, err error) {
|
||||
resp, err := client.rdsconn.DescribeRegions()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error to list regions not found")
|
||||
}
|
||||
regions := resp.Regions.RDSRegion
|
||||
|
||||
zoneIds := []string{}
|
||||
for _, r := range regions {
|
||||
if r.RegionId == string(client.Region) && strings.Contains(r.ZoneId, MULTI_IZ_SYMBOL) {
|
||||
zoneIds = append(zoneIds, r.ZoneId)
|
||||
}
|
||||
}
|
||||
|
||||
return zoneIds, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) {
|
||||
idsStr, jerr := json.Marshal(ids)
|
||||
if jerr != nil {
|
||||
|
@ -119,6 +137,23 @@ func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.Instanc
|
|||
return &instances[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) QueryInstanceSystemDisk(id string) (disk *ecs.DiskItemType, err error) {
|
||||
args := ecs.DescribeDisksArgs{
|
||||
RegionId: client.Region,
|
||||
InstanceId: string(id),
|
||||
DiskType: ecs.DiskTypeAllSystem,
|
||||
}
|
||||
disks, _, err := client.ecsconn.DescribeDisks(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(disks) == 0 {
|
||||
return nil, common.GetClientErrorFromString(SystemDiskNotFound)
|
||||
}
|
||||
|
||||
return &disks[0], nil
|
||||
}
|
||||
|
||||
// ResourceAvailable check resource available for zone
|
||||
func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error {
|
||||
available := false
|
||||
|
@ -186,15 +221,26 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr
|
|||
return client.ecsconn.DescribeSecurityGroupAttribute(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip_protocol, port_range string) (*ecs.PermissionType, error) {
|
||||
func (client *AliyunClient) DescribeSecurityByAttr(securityGroupId, direction, nicType string) (*ecs.DescribeSecurityGroupAttributeResponse, error) {
|
||||
|
||||
sg, err := client.DescribeSecurity(securityGroupId)
|
||||
args := &ecs.DescribeSecurityGroupAttributeArgs{
|
||||
RegionId: client.Region,
|
||||
SecurityGroupId: securityGroupId,
|
||||
Direction: direction,
|
||||
NicType: ecs.NicType(nicType),
|
||||
}
|
||||
|
||||
return client.ecsconn.DescribeSecurityGroupAttribute(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, direction, nicType, ipProtocol, portRange string) (*ecs.PermissionType, error) {
|
||||
sg, err := client.DescribeSecurityByAttr(securityGroupId, direction, nicType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range sg.Permissions.Permission {
|
||||
if strings.ToLower(string(p.IpProtocol)) == ip_protocol && p.PortRange == port_range {
|
||||
if strings.ToLower(string(p.IpProtocol)) == ipProtocol && p.PortRange == portRange {
|
||||
return &p, nil
|
||||
}
|
||||
}
|
||||
|
@ -203,6 +249,11 @@ func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip
|
|||
}
|
||||
|
||||
func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error {
|
||||
//todo: handle the specal err
|
||||
//when the rule is not exist, api will return success(200)
|
||||
return client.ecsconn.RevokeSecurityGroup(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) RevokeSecurityGroupEgress(args *ecs.RevokeSecurityGroupEgressArgs) error {
|
||||
//when the rule is not exist, api will return success(200)
|
||||
return client.ecsconn.RevokeSecurityGroupEgress(args)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
package alicloud
|
||||
|
||||
import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// when getInstance is empty, then throw InstanceNotfound error
|
||||
func (client *AliyunClient) DescribeDBInstanceById(id string) (instance *rds.DBInstanceAttribute, err error) {
|
||||
arrtArgs := rds.DescribeDBInstancesArgs{
|
||||
DBInstanceId: id,
|
||||
}
|
||||
resp, err := client.rdsconn.DescribeDBInstanceAttribute(&arrtArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attr := resp.Items.DBInstanceAttribute
|
||||
|
||||
if len(attr) <= 0 {
|
||||
return nil, common.GetClientErrorFromString(InstanceNotfound)
|
||||
}
|
||||
|
||||
return &attr[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) CreateAccountByInfo(instanceId, username, pwd string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.CreateAccountArgs{
|
||||
DBInstanceId: instanceId,
|
||||
AccountName: username,
|
||||
AccountPassword: pwd,
|
||||
}
|
||||
|
||||
if _, err := conn.CreateAccount(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForAccount(instanceId, username, rds.Available, 200); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) CreateDatabaseByInfo(instanceId, dbName, charset, desp string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.CreateDatabaseArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBName: dbName,
|
||||
CharacterSetName: charset,
|
||||
DBDescription: desp,
|
||||
}
|
||||
_, err := conn.CreateDatabase(&args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeDatabaseByName(instanceId, dbName string) (ds []rds.Database, err error) {
|
||||
conn := client.rdsconn
|
||||
args := rds.DescribeDatabasesArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBName: dbName,
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeDatabases(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Databases.Database, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GrantDBPrivilege2Account(instanceId, username, dbName string) error {
|
||||
conn := client.rdsconn
|
||||
pargs := rds.GrantAccountPrivilegeArgs{
|
||||
DBInstanceId: instanceId,
|
||||
AccountName: username,
|
||||
DBName: dbName,
|
||||
AccountPrivilege: rds.ReadWrite,
|
||||
}
|
||||
if _, err := conn.GrantAccountPrivilege(&pargs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForAccountPrivilege(instanceId, username, dbName, rds.ReadWrite, 200); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) AllocateDBPublicConnection(instanceId, port string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.AllocateInstancePublicConnectionArgs{
|
||||
DBInstanceId: instanceId,
|
||||
ConnectionStringPrefix: instanceId + "o",
|
||||
Port: port,
|
||||
}
|
||||
|
||||
if _, err := conn.AllocateInstancePublicConnection(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForPublicConnection(instanceId, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ConfigDBBackup(instanceId, backupTime, backupPeriod string, retentionPeriod int) error {
|
||||
bargs := rds.BackupPolicy{
|
||||
PreferredBackupTime: backupTime,
|
||||
PreferredBackupPeriod: backupPeriod,
|
||||
BackupRetentionPeriod: retentionPeriod,
|
||||
}
|
||||
args := rds.ModifyBackupPolicyArgs{
|
||||
DBInstanceId: instanceId,
|
||||
BackupPolicy: bargs,
|
||||
}
|
||||
|
||||
if _, err := client.rdsconn.ModifyBackupPolicy(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ModifyDBSecurityIps(instanceId, ips string) error {
|
||||
sargs := rds.DBInstanceIPArray{
|
||||
SecurityIps: ips,
|
||||
}
|
||||
|
||||
args := rds.ModifySecurityIpsArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBInstanceIPArray: sargs,
|
||||
}
|
||||
|
||||
if _, err := client.rdsconn.ModifySecurityIps(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeDBSecurityIps(instanceId string) (ips []rds.DBInstanceIPList, err error) {
|
||||
args := rds.DescribeDBInstanceIPsArgs{
|
||||
DBInstanceId: instanceId,
|
||||
}
|
||||
|
||||
resp, err := client.rdsconn.DescribeDBInstanceIPs(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Items.DBInstanceIPArray, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GetSecurityIps(instanceId string) ([]string, error) {
|
||||
arr, err := client.DescribeDBSecurityIps(instanceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ips := ""
|
||||
for i, ip := range arr {
|
||||
if i == 0 {
|
||||
ips += ip.SecurityIPList
|
||||
} else {
|
||||
ips += COMMA_SEPARATED + ip.SecurityIPList
|
||||
}
|
||||
}
|
||||
return strings.Split(ips, COMMA_SEPARATED), nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ModifyDBClassStorage(instanceId, class, storage string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.ModifyDBInstanceSpecArgs{
|
||||
DBInstanceId: instanceId,
|
||||
PayType: rds.Postpaid,
|
||||
DBInstanceClass: class,
|
||||
DBInstanceStorage: storage,
|
||||
}
|
||||
|
||||
if _, err := conn.ModifyDBInstanceSpec(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// turn period to TimeType
|
||||
func TransformPeriod2Time(period int, chargeType string) (ut int, tt common.TimeType) {
|
||||
if chargeType == string(rds.Postpaid) {
|
||||
return 1, common.Day
|
||||
}
|
||||
|
||||
if period >= 1 && period <= 9 {
|
||||
return period, common.Month
|
||||
}
|
||||
|
||||
if period == 12 {
|
||||
return 1, common.Year
|
||||
}
|
||||
|
||||
if period == 24 {
|
||||
return 2, common.Year
|
||||
}
|
||||
return 0, common.Day
|
||||
|
||||
}
|
||||
|
||||
// turn TimeType to Period
|
||||
func TransformTime2Period(ut int, tt common.TimeType) (period int) {
|
||||
if tt == common.Year {
|
||||
return 12 * ut
|
||||
}
|
||||
|
||||
return ut
|
||||
|
||||
}
|
||||
|
||||
// Flattens an array of databases into a []map[string]interface{}
|
||||
func flattenDatabaseMappings(list []rds.Database) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"db_name": i.DBName,
|
||||
"character_set_name": i.CharacterSetName,
|
||||
"db_description": i.DBDescription,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenDBBackup(list []rds.BackupPolicy) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"preferred_backup_period": i.PreferredBackupPeriod,
|
||||
"preferred_backup_time": i.PreferredBackupTime,
|
||||
"backup_retention_period": i.LogBackupRetentionPeriod,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenDBSecurityIPs(list []rds.DBInstanceIPList) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"security_ips": i.SecurityIPList,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Flattens an array of databases connection into a []map[string]interface{}
|
||||
func flattenDBConnections(list []rds.DBInstanceNetInfo) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"connection_string": i.ConnectionString,
|
||||
"ip_type": i.IPType,
|
||||
"ip_address": i.IPAddress,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -24,14 +24,14 @@ func (client *AliyunClient) DescribeEipAddress(allocationId string) (*ecs.EipAdd
|
|||
return &eips[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*NatGatewaySetType, error) {
|
||||
func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*ecs.NatGatewaySetType, error) {
|
||||
|
||||
args := &DescribeNatGatewaysArgs{
|
||||
args := &ecs.DescribeNatGatewaysArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: natGatewayId,
|
||||
}
|
||||
|
||||
natGateways, _, err := DescribeNatGateways(client.ecsconn, args)
|
||||
natGateways, _, err := client.vpcconn.DescribeNatGateways(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -132,3 +132,23 @@ func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType
|
|||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GetVpcIdByVSwitchId(vswitchId string) (vpcId string, err error) {
|
||||
|
||||
vs, _, err := client.ecsconn.DescribeVpcs(&ecs.DescribeVpcsArgs{
|
||||
RegionId: client.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, v := range vs {
|
||||
for _, sw := range v.VSwitchIds.VSwitchId {
|
||||
if sw == vswitchId {
|
||||
return v.VpcId, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", &common.Error{ErrorResponse: common.ErrorResponse{Message: Notfound}}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,27 @@ package alicloud
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// common
|
||||
func validateInstancePort(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.IntBetween(1, 65535)(v, k)
|
||||
value := v.(int)
|
||||
if value < 1 || value > 65535 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid instance port between 1 and 65535",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
@ -28,11 +38,12 @@ func validateInstanceProtocol(v interface{}, k string) (ws []string, errors []er
|
|||
|
||||
// ecs
|
||||
func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(ecs.DiskCategoryCloud),
|
||||
string(ecs.DiskCategoryCloudEfficiency),
|
||||
string(ecs.DiskCategoryCloudSSD),
|
||||
}, false)(v, k)
|
||||
category := ecs.DiskCategory(v.(string))
|
||||
if category != ecs.DiskCategoryCloud && category != ecs.DiskCategoryCloudEfficiency && category != ecs.DiskCategoryCloudSSD {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s %s", k, ecs.DiskCategoryCloud, ecs.DiskCategoryCloudEfficiency, ecs.DiskCategoryCloudSSD))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateInstanceName(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
@ -49,7 +60,12 @@ func validateInstanceName(v interface{}, k string) (ws []string, errors []error)
|
|||
}
|
||||
|
||||
func validateInstanceDescription(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringLenBetween(2, 256)(v, k)
|
||||
value := v.(string)
|
||||
if len(value) < 2 || len(value) > 256 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k))
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateDiskName(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
@ -71,7 +87,12 @@ func validateDiskName(v interface{}, k string) (ws []string, errors []error) {
|
|||
}
|
||||
|
||||
func validateDiskDescription(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringLenBetween(2, 128)(v, k)
|
||||
value := v.(string)
|
||||
if len(value) < 2 || len(value) > 256 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k))
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//security group
|
||||
|
@ -89,114 +110,225 @@ func validateSecurityGroupName(v interface{}, k string) (ws []string, errors []e
|
|||
}
|
||||
|
||||
func validateSecurityGroupDescription(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringLenBetween(2, 256)(v, k)
|
||||
value := v.(string)
|
||||
if len(value) < 2 || len(value) > 256 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 256 characters", k))
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(GroupRuleIngress),
|
||||
string(GroupRuleEgress),
|
||||
}, false)(v, k)
|
||||
rt := GroupRuleDirection(v.(string))
|
||||
if rt != GroupRuleIngress && rt != GroupRuleEgress {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(GroupRuleTcp),
|
||||
string(GroupRuleUdp),
|
||||
string(GroupRuleIcmp),
|
||||
string(GroupRuleGre),
|
||||
string(GroupRuleAll),
|
||||
}, false)(v, k)
|
||||
pt := GroupRuleIpProtocol(v.(string))
|
||||
if pt != GroupRuleTcp && pt != GroupRuleUdp && pt != GroupRuleIcmp && pt != GroupRuleGre && pt != GroupRuleAll {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s %s %s %s", k,
|
||||
GroupRuleTcp, GroupRuleUdp, GroupRuleIcmp, GroupRuleGre, GroupRuleAll))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(GroupRuleInternet),
|
||||
string(GroupRuleIntranet),
|
||||
}, false)(v, k)
|
||||
pt := GroupRuleNicType(v.(string))
|
||||
if pt != GroupRuleInternet && pt != GroupRuleIntranet {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleInternet, GroupRuleIntranet))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(GroupRulePolicyAccept),
|
||||
string(GroupRulePolicyDrop),
|
||||
}, false)(v, k)
|
||||
pt := GroupRulePolicy(v.(string))
|
||||
if pt != GroupRulePolicyAccept && pt != GroupRulePolicyDrop {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRulePolicyAccept, GroupRulePolicyDrop))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSecurityPriority(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.IntBetween(1, 100)(v, k)
|
||||
value := v.(int)
|
||||
if value < 1 || value > 100 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid authorization policy priority between 1 and 100",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// validateCIDRNetworkAddress ensures that the string value is a valid CIDR that
|
||||
// represents a network address - it adds an error otherwise
|
||||
func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.CIDRNetwork(0, 32)(v, k)
|
||||
value := v.(string)
|
||||
_, ipnet, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid CIDR, got error parsing: %s", k, err))
|
||||
return
|
||||
}
|
||||
|
||||
if ipnet == nil || value != ipnet.String() {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid network CIDR, expected %q, got %q",
|
||||
k, ipnet, value))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateRouteEntryNextHopType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
string(ecs.NextHopIntance),
|
||||
string(ecs.NextHopTunnel),
|
||||
}, false)(v, k)
|
||||
nht := ecs.NextHopType(v.(string))
|
||||
if nht != ecs.NextHopIntance && nht != ecs.NextHopTunnel {
|
||||
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k,
|
||||
ecs.NextHopIntance, ecs.NextHopTunnel))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSwitchCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.CIDRNetwork(16, 29)(v, k)
|
||||
value := v.(string)
|
||||
_, ipnet, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid CIDR, got error parsing: %s", k, err))
|
||||
return
|
||||
}
|
||||
|
||||
if ipnet == nil || value != ipnet.String() {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid network CIDR, expected %q, got %q",
|
||||
k, ipnet, value))
|
||||
return
|
||||
}
|
||||
|
||||
mark, _ := strconv.Atoi(strings.Split(ipnet.String(), "/")[1])
|
||||
if mark < 16 || mark > 29 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a network CIDR which mark between 16 and 29",
|
||||
k))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validateIoOptimized ensures that the string value is a valid IoOptimized that
|
||||
// represents a IoOptimized - it adds an error otherwise
|
||||
func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"",
|
||||
string(ecs.IoOptimizedNone),
|
||||
string(ecs.IoOptimizedOptimized),
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
ioOptimized := ecs.IoOptimized(value)
|
||||
if ioOptimized != ecs.IoOptimizedNone &&
|
||||
ioOptimized != ecs.IoOptimizedOptimized {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid IoOptimized, expected %s or %s, got %q",
|
||||
k, ecs.IoOptimizedNone, ecs.IoOptimizedOptimized, ioOptimized))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validateInstanceNetworkType ensures that the string value is a classic or vpc
|
||||
func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"",
|
||||
string(ClassicNet),
|
||||
string(VpcNet),
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
network := InstanceNetWork(value)
|
||||
if network != ClassicNet &&
|
||||
network != VpcNet {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid InstanceNetworkType, expected %s or %s, go %q",
|
||||
k, ClassicNet, VpcNet, network))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateInstanceChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"",
|
||||
string(common.PrePaid),
|
||||
string(common.PostPaid),
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
chargeType := common.InstanceChargeType(value)
|
||||
if chargeType != common.PrePaid &&
|
||||
chargeType != common.PostPaid {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid InstanceChargeType, expected %s or %s, got %q",
|
||||
k, common.PrePaid, common.PostPaid, chargeType))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateInternetChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"",
|
||||
string(common.PayByBandwidth),
|
||||
string(common.PayByTraffic),
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
chargeType := common.InternetChargeType(value)
|
||||
if chargeType != common.PayByBandwidth &&
|
||||
chargeType != common.PayByTraffic {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid InstanceChargeType, expected %s or %s, got %q",
|
||||
k, common.PayByBandwidth, common.PayByTraffic, chargeType))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateInternetMaxBandWidthOut(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.IntBetween(1, 100)(v, k)
|
||||
value := v.(int)
|
||||
if value < 1 || value > 100 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid internet bandwidth out between 1 and 1000",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SLB
|
||||
func validateSlbName(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringLenBetween(0, 80)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
if len(value) < 1 || len(value) > 80 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer name characters between 1 and 80",
|
||||
k))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbInternetChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"paybybandwidth",
|
||||
"paybytraffic",
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
chargeType := common.InternetChargeType(value)
|
||||
|
||||
if chargeType != "paybybandwidth" &&
|
||||
chargeType != "paybytraffic" {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid InstanceChargeType, expected %s or %s, got %q",
|
||||
k, "paybybandwidth", "paybytraffic", value))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbBandwidth(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.IntBetween(1, 1000)(v, k)
|
||||
value := v.(int)
|
||||
if value < 1 || value > 1000 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer bandwidth between 1 and 1000",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
@ -211,23 +343,180 @@ func validateSlbListenerBandwidth(v interface{}, k string) (ws []string, errors
|
|||
}
|
||||
|
||||
func validateSlbListenerScheduler(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{"wrr", "wlc"}, false)(v, k)
|
||||
}
|
||||
if value := v.(string); value != "" {
|
||||
scheduler := slb.SchedulerType(value)
|
||||
|
||||
func validateSlbListenerStickySession(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{"", "on", "off"}, false)(v, k)
|
||||
}
|
||||
if scheduler != "wrr" && scheduler != "wlc" {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid SchedulerType, expected %s or %s, got %q",
|
||||
k, "wrr", "wlc", value))
|
||||
}
|
||||
}
|
||||
|
||||
func validateSlbListenerStickySessionType(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{"", "insert", "server"}, false)(v, k)
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{"", "insert", "server"}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
if len(value) < 1 || len(value) > 200 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 200 characters", k))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerCookieTimeout(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(int)
|
||||
if value < 0 || value > 86400 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer cookie timeout between 0 and 86400",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.IntBetween(0, 86400)(v, k)
|
||||
value := v.(int)
|
||||
if value < 0 || value > 3600 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer persistence timeout between 0 and 86400",
|
||||
k))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerHealthCheckDomain(v interface{}, k string) (ws []string, errors []error) {
|
||||
if value := v.(string); value != "" {
|
||||
//the len add "$_ip",so to max is 84
|
||||
if len(value) < 1 || len(value) > 84 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 84 characters", k))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerHealthCheckUri(v interface{}, k string) (ws []string, errors []error) {
|
||||
if value := v.(string); value != "" {
|
||||
if len(value) < 1 || len(value) > 80 {
|
||||
errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerHealthCheckConnectPort(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(int)
|
||||
if value < 1 || value > 65535 {
|
||||
if value != -520 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer health check connect port between 1 and 65535 or -520",
|
||||
k))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateDBBackupPeriod(v interface{}, k string) (ws []string, errors []error) {
|
||||
days := []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}
|
||||
value := v.(string)
|
||||
exist := false
|
||||
for _, d := range days {
|
||||
if value == d {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid backup period value should in array %#v, got %q",
|
||||
k, days, value))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validateAllowedStringValue(ss []string) schema.SchemaValidateFunc {
|
||||
return func(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
existed := false
|
||||
for _, s := range ss {
|
||||
if s == value {
|
||||
existed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !existed {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid string value should in array %#v, got %q",
|
||||
k, ss, value))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func validateAllowedSplitStringValue(ss []string, splitStr string) schema.SchemaValidateFunc {
|
||||
return func(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
existed := false
|
||||
tsList := strings.Split(value, splitStr)
|
||||
|
||||
for _, ts := range tsList {
|
||||
existed = false
|
||||
for _, s := range ss {
|
||||
if ts == s {
|
||||
existed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !existed {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid string value should in %#v, got %q",
|
||||
k, ss, value))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func validateAllowedIntValue(is []int) schema.SchemaValidateFunc {
|
||||
return func(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(int)
|
||||
existed := false
|
||||
for _, i := range is {
|
||||
if i == value {
|
||||
existed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !existed {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid int value should in array %#v, got %q",
|
||||
k, is, value))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func validateIntegerInRange(min, max int) schema.SchemaValidateFunc {
|
||||
return func(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(int)
|
||||
if value < min {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q cannot be lower than %d: %d", k, min, value))
|
||||
}
|
||||
if value > max {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q cannot be higher than %d: %d", k, max, value))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//data source validate func
|
||||
|
@ -244,14 +533,19 @@ func validateNameRegex(v interface{}, k string) (ws []string, errors []error) {
|
|||
}
|
||||
|
||||
func validateImageOwners(v interface{}, k string) (ws []string, errors []error) {
|
||||
return validation.StringInSlice([]string{
|
||||
"",
|
||||
string(ecs.ImageOwnerSystem),
|
||||
string(ecs.ImageOwnerSelf),
|
||||
string(ecs.ImageOwnerOthers),
|
||||
string(ecs.ImageOwnerMarketplace),
|
||||
string(ecs.ImageOwnerDefault),
|
||||
}, false)(v, k)
|
||||
if value := v.(string); value != "" {
|
||||
owners := ecs.ImageOwnerAlias(value)
|
||||
if owners != ecs.ImageOwnerSystem &&
|
||||
owners != ecs.ImageOwnerSelf &&
|
||||
owners != ecs.ImageOwnerOthers &&
|
||||
owners != ecs.ImageOwnerMarketplace &&
|
||||
owners != ecs.ImageOwnerDefault {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid Image owner , expected %s, %s, %s, %s or %s, got %q",
|
||||
k, ecs.ImageOwnerSystem, ecs.ImageOwnerSelf, ecs.ImageOwnerOthers, ecs.ImageOwnerMarketplace, ecs.ImageOwnerDefault, owners))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateRegion(v interface{}, k string) (ws []string, errors []error) {
|
||||
|
|
|
@ -427,3 +427,76 @@ func TestValidateSlbListenerBandwidth(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAllowedStringValue(t *testing.T) {
|
||||
exceptValues := []string{"aliyun", "alicloud", "alibaba"}
|
||||
validValues := []string{"aliyun"}
|
||||
for _, v := range validValues {
|
||||
_, errors := validateAllowedStringValue(exceptValues)(v, "allowvalue")
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("%q should be a valid value in %#v: %q", v, exceptValues, errors)
|
||||
}
|
||||
}
|
||||
|
||||
invalidValues := []string{"ali", "alidata", "terraform"}
|
||||
for _, v := range invalidValues {
|
||||
_, errors := validateAllowedStringValue(exceptValues)(v, "allowvalue")
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("%q should be an invalid value", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAllowedStringSplitValue(t *testing.T) {
|
||||
exceptValues := []string{"aliyun", "alicloud", "alibaba"}
|
||||
validValues := "aliyun,alicloud"
|
||||
_, errors := validateAllowedSplitStringValue(exceptValues, ",")(validValues, "allowvalue")
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("%q should be a valid value in %#v: %q", validValues, exceptValues, errors)
|
||||
}
|
||||
|
||||
invalidValues := "ali,alidata"
|
||||
_, invalidErr := validateAllowedSplitStringValue(exceptValues, ",")(invalidValues, "allowvalue")
|
||||
if len(invalidErr) == 0 {
|
||||
t.Fatalf("%q should be an invalid value", invalidValues)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAllowedIntValue(t *testing.T) {
|
||||
exceptValues := []int{1, 3, 5, 6}
|
||||
validValues := []int{1, 3, 5, 6}
|
||||
for _, v := range validValues {
|
||||
_, errors := validateAllowedIntValue(exceptValues)(v, "allowvalue")
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("%q should be a valid value in %#v: %q", v, exceptValues, errors)
|
||||
}
|
||||
}
|
||||
|
||||
invalidValues := []int{0, 7, 10}
|
||||
for _, v := range invalidValues {
|
||||
_, errors := validateAllowedIntValue(exceptValues)(v, "allowvalue")
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("%q should be an invalid value", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateIntegerInRange(t *testing.T) {
|
||||
validIntegers := []int{-259, 0, 1, 5, 999}
|
||||
min := -259
|
||||
max := 999
|
||||
for _, v := range validIntegers {
|
||||
_, errors := validateIntegerInRange(min, max)(v, "name")
|
||||
if len(errors) != 0 {
|
||||
t.Fatalf("%q should be an integer in range (%d, %d): %q", v, min, max, errors)
|
||||
}
|
||||
}
|
||||
|
||||
invalidIntegers := []int{-260, -99999, 1000, 25678}
|
||||
for _, v := range invalidIntegers {
|
||||
_, errors := validateIntegerInRange(min, max)(v, "name")
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("%q should be an integer outside range (%d, %d)", v, min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,10 @@ func dataSourceAwsCloudFormationStack() *schema.Resource {
|
|||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"iam_role_arn": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"tags": {
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
|
@ -86,6 +90,7 @@ func dataSourceAwsCloudFormationStackRead(d *schema.ResourceData, meta interface
|
|||
d.Set("description", stack.Description)
|
||||
d.Set("disable_rollback", stack.DisableRollback)
|
||||
d.Set("timeout_in_minutes", stack.TimeoutInMinutes)
|
||||
d.Set("iam_role_arn", stack.RoleARN)
|
||||
|
||||
if len(stack.NotificationARNs) > 0 {
|
||||
d.Set("notification_arns", schema.NewSet(schema.HashString, flattenStringList(stack.NotificationARNs)))
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
|
@ -18,15 +19,15 @@ func timePtr(t time.Time) *time.Time {
|
|||
|
||||
func TestResourceSortByExpirationDate(t *testing.T) {
|
||||
certs := []*iam.ServerCertificateMetadata{
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("oldest"),
|
||||
Expiration: timePtr(time.Now()),
|
||||
},
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("latest"),
|
||||
Expiration: timePtr(time.Now().Add(3 * time.Hour)),
|
||||
},
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("in between"),
|
||||
Expiration: timePtr(time.Now().Add(2 * time.Hour)),
|
||||
},
|
||||
|
@ -38,13 +39,18 @@ func TestResourceSortByExpirationDate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsDataIAMServerCertConfig,
|
||||
Config: testAccIAMServerCertConfig(rInt),
|
||||
},
|
||||
{
|
||||
Config: testAccAwsDataIAMServerCertConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"),
|
||||
resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "arn"),
|
||||
|
@ -71,12 +77,16 @@ func TestAccAWSDataSourceIAMServerCertificate_matchNamePrefix(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
var testAccAwsDataIAMServerCertConfig = fmt.Sprintf(`%s
|
||||
func testAccAwsDataIAMServerCertConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "aws_iam_server_certificate" "test" {
|
||||
name = "${aws_iam_server_certificate.test_cert.name}"
|
||||
latest = true
|
||||
}
|
||||
`, testAccIAMServerCertConfig)
|
||||
`, testAccIAMServerCertConfig(rInt))
|
||||
}
|
||||
|
||||
var testAccAwsDataIAMServerCertConfigMatchNamePrefix = `
|
||||
data "aws_iam_server_certificate" "test" {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
publicResourceName := "aws_route53_zon.test"
|
||||
publicResourceName := "aws_route53_zone.test"
|
||||
publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
|
||||
privateResourceName := "aws_route53_zone.test_private"
|
||||
privateDomain := fmt.Sprintf("test.acc-%d.", rInt)
|
||||
|
|
|
@ -3,19 +3,20 @@ package aws
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSCustomerGateway_importBasic(t *testing.T) {
|
||||
resourceName := "aws_customer_gateway.foo"
|
||||
|
||||
randInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCustomerGatewayConfig,
|
||||
Config: testAccCustomerGatewayConfig(randInt),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
|
|
|
@ -3,11 +3,13 @@ package aws
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
||||
resourceName := "aws_efs_file_system.foo-with-tags"
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -15,7 +17,7 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags,
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags(rInt),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSIAMServerCertificate_importBasic(t *testing.T) {
|
||||
resourceName := "aws_iam_server_certificate.test_cert"
|
||||
rInt := acctest.RandInt()
|
||||
resourceId := fmt.Sprintf("terraform-test-cert-%d", rInt)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccIAMServerCertConfig(rInt),
|
||||
},
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateId: resourceId,
|
||||
ImportStateVerifyIgnore: []string{
|
||||
"private_key"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -31,10 +31,12 @@ func resourceAwsAlbListenerRule() *schema.Resource {
|
|||
"listener_arn": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"priority": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateAwsAlbListenerRulePriority,
|
||||
},
|
||||
"action": {
|
||||
|
@ -66,6 +68,7 @@ func resourceAwsAlbListenerRule() *schema.Resource {
|
|||
},
|
||||
"values": {
|
||||
Type: schema.TypeList,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
|
@ -183,42 +186,75 @@ func resourceAwsAlbListenerRuleRead(d *schema.ResourceData, meta interface{}) er
|
|||
func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
d.Partial(true)
|
||||
|
||||
if d.HasChange("priority") {
|
||||
params := &elbv2.SetRulePrioritiesInput{
|
||||
RulePriorities: []*elbv2.RulePriorityPair{
|
||||
{
|
||||
RuleArn: aws.String(d.Id()),
|
||||
Priority: aws.Int64(int64(d.Get("priority").(int))),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := elbconn.SetRulePriorities(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetPartial("priority")
|
||||
}
|
||||
|
||||
requestUpdate := false
|
||||
params := &elbv2.ModifyRuleInput{
|
||||
RuleArn: aws.String(d.Id()),
|
||||
}
|
||||
|
||||
actions := d.Get("action").([]interface{})
|
||||
params.Actions = make([]*elbv2.Action, len(actions))
|
||||
for i, action := range actions {
|
||||
actionMap := action.(map[string]interface{})
|
||||
params.Actions[i] = &elbv2.Action{
|
||||
TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
|
||||
Type: aws.String(actionMap["type"].(string)),
|
||||
if d.HasChange("action") {
|
||||
actions := d.Get("action").([]interface{})
|
||||
params.Actions = make([]*elbv2.Action, len(actions))
|
||||
for i, action := range actions {
|
||||
actionMap := action.(map[string]interface{})
|
||||
params.Actions[i] = &elbv2.Action{
|
||||
TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
|
||||
Type: aws.String(actionMap["type"].(string)),
|
||||
}
|
||||
}
|
||||
requestUpdate = true
|
||||
d.SetPartial("action")
|
||||
}
|
||||
|
||||
if d.HasChange("condition") {
|
||||
conditions := d.Get("condition").([]interface{})
|
||||
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
||||
for i, condition := range conditions {
|
||||
conditionMap := condition.(map[string]interface{})
|
||||
values := conditionMap["values"].([]interface{})
|
||||
params.Conditions[i] = &elbv2.RuleCondition{
|
||||
Field: aws.String(conditionMap["field"].(string)),
|
||||
Values: make([]*string, len(values)),
|
||||
}
|
||||
for j, value := range values {
|
||||
params.Conditions[i].Values[j] = aws.String(value.(string))
|
||||
}
|
||||
}
|
||||
requestUpdate = true
|
||||
d.SetPartial("condition")
|
||||
}
|
||||
|
||||
if requestUpdate {
|
||||
resp, err := elbconn.ModifyRule(params)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
||||
}
|
||||
|
||||
if len(resp.Rules) == 0 {
|
||||
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
||||
}
|
||||
}
|
||||
|
||||
conditions := d.Get("condition").([]interface{})
|
||||
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
||||
for i, condition := range conditions {
|
||||
conditionMap := condition.(map[string]interface{})
|
||||
values := conditionMap["values"].([]interface{})
|
||||
params.Conditions[i] = &elbv2.RuleCondition{
|
||||
Field: aws.String(conditionMap["field"].(string)),
|
||||
Values: make([]*string, len(values)),
|
||||
}
|
||||
for j, value := range values {
|
||||
params.Conditions[i].Values[j] = aws.String(value.(string))
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := elbconn.ModifyRule(params)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
||||
}
|
||||
|
||||
if len(resp.Rules) == 0 {
|
||||
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
||||
}
|
||||
d.Partial(false)
|
||||
|
||||
return resourceAwsAlbListenerRuleRead(d, meta)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package aws
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
@ -43,6 +44,90 @@ func TestAccAWSALBListenerRule_basic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_updateRulePriority(t *testing.T) {
|
||||
var rule elbv2.Rule
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_listener_rule.static",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &rule),
|
||||
resource.TestCheckResourceAttr("aws_alb_listener_rule.static", "priority", "100"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_updateRulePriority(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &rule),
|
||||
resource.TestCheckResourceAttr("aws_alb_listener_rule.static", "priority", "101"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_changeListenerRuleArnForcesNew(t *testing.T) {
|
||||
var before, after elbv2.Rule
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_listener_rule.static",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &before),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_changeRuleArn(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &after),
|
||||
testAccCheckAWSAlbListenerRuleRecreated(t, &before, &after),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_multipleConditionThrowsError(t *testing.T) {
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName),
|
||||
ExpectError: regexp.MustCompile(`attribute supports 1 item maximum`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSAlbListenerRuleRecreated(t *testing.T,
|
||||
before, after *elbv2.Rule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if *before.RuleArn == *after.RuleArn {
|
||||
t.Fatalf("Expected change of Listener Rule ARNs, but both were %v", before.RuleArn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSALBListenerRuleExists(n string, res *elbv2.Rule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -104,6 +189,117 @@ func testAccCheckAWSALBListenerRuleDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
priority = 100
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*", "static"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
|
@ -214,3 +410,238 @@ resource "aws_security_group" "alb_test" {
|
|||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_updateRulePriority(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
priority = 101
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_changeRuleArn(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end_ruleupdate.arn}"
|
||||
priority = 101
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end_ruleupdate" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "8080"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
|
|
@ -22,12 +22,12 @@ func resourceAwsCloudFormationStack() *schema.Resource {
|
|||
Delete: resourceAwsCloudFormationStackDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"template_body": &schema.Schema{
|
||||
"template_body": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
|
@ -37,42 +37,42 @@ func resourceAwsCloudFormationStack() *schema.Resource {
|
|||
return template
|
||||
},
|
||||
},
|
||||
"template_url": &schema.Schema{
|
||||
"template_url": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"capabilities": &schema.Schema{
|
||||
"capabilities": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
"disable_rollback": &schema.Schema{
|
||||
"disable_rollback": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"notification_arns": &schema.Schema{
|
||||
"notification_arns": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
"on_failure": &schema.Schema{
|
||||
"on_failure": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"parameters": &schema.Schema{
|
||||
"parameters": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
"outputs": &schema.Schema{
|
||||
"outputs": {
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
},
|
||||
"policy_body": &schema.Schema{
|
||||
"policy_body": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
|
@ -82,20 +82,24 @@ func resourceAwsCloudFormationStack() *schema.Resource {
|
|||
return json
|
||||
},
|
||||
},
|
||||
"policy_url": &schema.Schema{
|
||||
"policy_url": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"timeout_in_minutes": &schema.Schema{
|
||||
"timeout_in_minutes": {
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"tags": &schema.Schema{
|
||||
"tags": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"iam_role_arn": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +157,9 @@ func resourceAwsCloudFormationStackCreate(d *schema.ResourceData, meta interface
|
|||
log.Printf("[DEBUG] CloudFormation timeout: %d", retryTimeout)
|
||||
}
|
||||
}
|
||||
if v, ok := d.GetOk("iam_role_arn"); ok {
|
||||
input.RoleARN = aws.String(v.(string))
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Creating CloudFormation Stack: %s", input)
|
||||
resp, err := conn.CreateStack(&input)
|
||||
|
@ -297,6 +304,7 @@ func resourceAwsCloudFormationStackRead(d *schema.ResourceData, meta interface{}
|
|||
|
||||
d.Set("name", stack.StackName)
|
||||
d.Set("arn", stack.StackId)
|
||||
d.Set("iam_role_arn", stack.RoleARN)
|
||||
|
||||
if stack.TimeoutInMinutes != nil {
|
||||
d.Set("timeout_in_minutes", int(*stack.TimeoutInMinutes))
|
||||
|
@ -385,6 +393,10 @@ func resourceAwsCloudFormationStackUpdate(d *schema.ResourceData, meta interface
|
|||
input.StackPolicyURL = aws.String(d.Get("policy_url").(string))
|
||||
}
|
||||
|
||||
if d.HasChange("iam_role_arn") {
|
||||
input.RoleARN = aws.String(d.Get("iam_role_arn").(string))
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Updating CloudFormation stack: %s", input)
|
||||
stack, err := conn.UpdateStack(input)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestAccAWSCloudFormation_basic(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.network", &stack),
|
||||
|
@ -38,7 +38,7 @@ func TestAccAWSCloudFormation_yaml(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_yaml,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.yaml", &stack),
|
||||
|
@ -56,7 +56,7 @@ func TestAccAWSCloudFormation_defaultParams(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_defaultParams,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.asg-demo", &stack),
|
||||
|
@ -75,7 +75,7 @@ func TestAccAWSCloudFormation_allAttributes(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.full", &stack),
|
||||
|
@ -93,7 +93,7 @@ func TestAccAWSCloudFormation_allAttributes(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("aws_cloudformation_stack.full", "timeout_in_minutes", "10"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies_modified,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.full", &stack),
|
||||
|
@ -124,13 +124,13 @@ func TestAccAWSCloudFormation_withParams(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_withParams,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_withParams_modified,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
||||
|
@ -149,13 +149,13 @@ func TestAccAWSCloudFormation_withUrl_withParams(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_modified,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
||||
|
@ -173,7 +173,7 @@ func TestAccAWSCloudFormation_withUrl_withParams_withYaml(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_withYaml,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params-and-yaml", &stack),
|
||||
|
|
|
@ -10,12 +10,14 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
||||
var gateway ec2.CustomerGateway
|
||||
randInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_customer_gateway.foo",
|
||||
|
@ -23,19 +25,19 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCustomerGatewayConfig,
|
||||
Config: testAccCustomerGatewayConfig(randInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccCustomerGatewayConfigUpdateTags,
|
||||
Config: testAccCustomerGatewayConfigUpdateTags(randInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccCustomerGatewayConfigForceReplace,
|
||||
Config: testAccCustomerGatewayConfigForceReplace(randInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||
),
|
||||
|
@ -46,6 +48,7 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
|||
|
||||
func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
||||
var gateway ec2.CustomerGateway
|
||||
randInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_customer_gateway.foo",
|
||||
|
@ -53,13 +56,13 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
|||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCustomerGatewayConfig,
|
||||
Config: testAccCustomerGatewayConfig(randInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccCustomerGatewayConfigIdentical,
|
||||
Config: testAccCustomerGatewayConfigIdentical(randInt),
|
||||
ExpectError: regexp.MustCompile("An existing customer gateway"),
|
||||
},
|
||||
},
|
||||
|
@ -68,13 +71,14 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
|||
|
||||
func TestAccAWSCustomerGateway_disappears(t *testing.T) {
|
||||
var gateway ec2.CustomerGateway
|
||||
randInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCustomerGatewayConfig,
|
||||
Config: testAccCustomerGatewayConfig(randInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||
testAccAWSCustomerGatewayDisappears(&gateway),
|
||||
|
@ -190,59 +194,67 @@ func testAccCheckCustomerGateway(gatewayResource string, cgw *ec2.CustomerGatewa
|
|||
}
|
||||
}
|
||||
|
||||
const testAccCustomerGatewayConfig = `
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCustomerGatewayConfigIdentical = `
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway"
|
||||
func testAccCustomerGatewayConfig(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-%d"
|
||||
}
|
||||
}
|
||||
`, randInt)
|
||||
}
|
||||
|
||||
resource "aws_customer_gateway" "identical" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-identical"
|
||||
}
|
||||
func testAccCustomerGatewayConfigIdentical(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-%d"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_customer_gateway" "identical" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-identical-%d"
|
||||
}
|
||||
}
|
||||
`, randInt, randInt)
|
||||
}
|
||||
`
|
||||
|
||||
// Add the Another: "tag" tag.
|
||||
const testAccCustomerGatewayConfigUpdateTags = `
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway"
|
||||
Another = "tag"
|
||||
}
|
||||
func testAccCustomerGatewayConfigUpdateTags(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.0.0.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-%d"
|
||||
Another = "tag-%d"
|
||||
}
|
||||
}
|
||||
`, randInt, randInt)
|
||||
}
|
||||
`
|
||||
|
||||
// Change the ip_address.
|
||||
const testAccCustomerGatewayConfigForceReplace = `
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.10.10.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway"
|
||||
Another = "tag"
|
||||
func testAccCustomerGatewayConfigForceReplace(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_customer_gateway" "foo" {
|
||||
bgp_asn = 65000
|
||||
ip_address = "172.10.10.1"
|
||||
type = "ipsec.1"
|
||||
tags {
|
||||
Name = "foo-gateway-%d"
|
||||
Another = "tag-%d"
|
||||
}
|
||||
}
|
||||
`, randInt, randInt)
|
||||
}
|
||||
`
|
||||
|
|
|
@ -9,11 +9,14 @@ import (
|
|||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
// ACL Network ACLs all contain an explicit deny-all rule that cannot be
|
||||
// destroyed or changed by users. This rule is numbered very high to be a
|
||||
// ACL Network ACLs all contain explicit deny-all rules that cannot be
|
||||
// destroyed or changed by users. This rules are numbered very high to be a
|
||||
// catch-all.
|
||||
// See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl
|
||||
const awsDefaultAclRuleNumber = 32767
|
||||
const (
|
||||
awsDefaultAclRuleNumberIpv4 = 32767
|
||||
awsDefaultAclRuleNumberIpv6 = 32768
|
||||
)
|
||||
|
||||
func resourceAwsDefaultNetworkAcl() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
|
@ -258,7 +261,8 @@ func revokeAllNetworkACLEntries(netaclId string, meta interface{}) error {
|
|||
for _, e := range networkAcl.Entries {
|
||||
// Skip the default rules added by AWS. They can be neither
|
||||
// configured or deleted by users. See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl
|
||||
if *e.RuleNumber == awsDefaultAclRuleNumber {
|
||||
if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 ||
|
||||
*e.RuleNumber == awsDefaultAclRuleNumberIpv6 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,27 @@ func TestAccAWSDefaultNetworkAcl_basic(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 2),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSDefaultNetworkAcl_basicIpv6Vpc(t *testing.T) {
|
||||
var networkAcl ec2.NetworkAcl
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSDefaultNetworkAclDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_basicIpv6Vpc,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 4),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -58,8 +77,8 @@ func TestAccAWSDefaultNetworkAcl_deny_ingress(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_deny_ingress,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0, 2),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -77,8 +96,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||
),
|
||||
},
|
||||
|
||||
|
@ -88,8 +107,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_Subnets_remove,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||
),
|
||||
ExpectNonEmptyPlan: true,
|
||||
},
|
||||
|
@ -108,8 +127,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||
),
|
||||
},
|
||||
|
||||
|
@ -128,8 +147,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) {
|
|||
resource.TestStep{
|
||||
Config: testAccAWSDefaultNetworkConfig_Subnets_move,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
|
||||
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 2),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -141,14 +160,14 @@ func testAccCheckAWSDefaultNetworkAclDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.NetworkAclEntry, subnetCount int) resource.TestCheckFunc {
|
||||
func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.NetworkAclEntry, subnetCount int, hiddenRuleCount int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
aclEntriesCount := len(acl.Entries)
|
||||
ruleCount := len(rules)
|
||||
|
||||
// Default ACL has 2 hidden rules we can't do anything about
|
||||
ruleCount = ruleCount + 2
|
||||
// Default ACL has hidden rules we can't do anything about
|
||||
ruleCount = ruleCount + hiddenRuleCount
|
||||
|
||||
if ruleCount != aclEntriesCount {
|
||||
return fmt.Errorf("Expected (%d) Rules, got (%d)", ruleCount, aclEntriesCount)
|
||||
|
@ -162,7 +181,7 @@ func testAccCheckAWSDefaultACLAttributes(acl *ec2.NetworkAcl, rules []*ec2.Netwo
|
|||
}
|
||||
}
|
||||
|
||||
func testAccGetWSDefaultNetworkAcl(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc {
|
||||
func testAccGetAWSDefaultNetworkAcl(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
|
@ -426,3 +445,26 @@ resource "aws_default_network_acl" "default" {
|
|||
}
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSDefaultNetworkConfig_basicIpv6Vpc = `
|
||||
provider "aws" {
|
||||
region = "us-east-2"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "tftestvpc" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
assign_generated_ipv6_cidr_block = true
|
||||
|
||||
tags {
|
||||
Name = "TestAccAWSDefaultNetworkAcl_basicIpv6Vpc"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_default_network_acl" "default" {
|
||||
default_network_acl_id = "${aws_vpc.tftestvpc.default_network_acl_id}"
|
||||
|
||||
tags {
|
||||
Name = "TestAccAWSDefaultNetworkAcl_basicIpv6Vpc"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
@ -169,7 +169,7 @@ resource "aws_dms_replication_instance" "dms_replication_instance" {
|
|||
func dmsReplicationInstanceConfigUpdate(randId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "dms_iam_role" {
|
||||
name = "dms-vpc-role"
|
||||
name = "dms-vpc-role-%[1]s"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ func dmsReplicationSubnetGroupDestroy(s *terraform.State) error {
|
|||
func dmsReplicationSubnetGroupConfig(randId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "dms_iam_role" {
|
||||
name = "dms-vpc-role"
|
||||
name = "dms-vpc-role-%[1]s"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ func dmsReplicationTaskDestroy(s *terraform.State) error {
|
|||
func dmsReplicationTaskConfig(randId string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "dms_iam_role" {
|
||||
name = "dms-vpc-role"
|
||||
name = "dms-vpc-role-%[1]s"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"dms.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ func TestResourceAWSEFSFileSystem_hasEmptyFileSystems(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
@ -104,7 +105,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
|||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags,
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckEfsFileSystem(
|
||||
"aws_efs_file_system.foo-with-tags",
|
||||
|
@ -116,7 +117,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
|||
testAccCheckEfsFileSystemTags(
|
||||
"aws_efs_file_system.foo-with-tags",
|
||||
map[string]string{
|
||||
"Name": "foo-efs",
|
||||
"Name": fmt.Sprintf("foo-efs-%d", rInt),
|
||||
"Another": "tag",
|
||||
},
|
||||
),
|
||||
|
@ -143,13 +144,14 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAWSEFSFileSystem_pagedTags(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSEFSFileSystemConfigPagedTags,
|
||||
Config: testAccAWSEFSFileSystemConfigPagedTags(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_efs_file_system.foo",
|
||||
|
@ -312,34 +314,38 @@ resource "aws_efs_file_system" "foo" {
|
|||
}
|
||||
`
|
||||
|
||||
const testAccAWSEFSFileSystemConfigPagedTags = `
|
||||
resource "aws_efs_file_system" "foo" {
|
||||
creation_token = "radeksimko"
|
||||
tags {
|
||||
Name = "foo-efs"
|
||||
Another = "tag"
|
||||
Test = "yes"
|
||||
User = "root"
|
||||
Page = "1"
|
||||
Environment = "prod"
|
||||
CostCenter = "terraform"
|
||||
AcceptanceTest = "PagedTags"
|
||||
CreationToken = "radek"
|
||||
PerfMode = "max"
|
||||
Region = "us-west-2"
|
||||
func testAccAWSEFSFileSystemConfigPagedTags(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_efs_file_system" "foo" {
|
||||
creation_token = "radeksimko"
|
||||
tags {
|
||||
Name = "foo-efs-%d"
|
||||
Another = "tag"
|
||||
Test = "yes"
|
||||
User = "root"
|
||||
Page = "1"
|
||||
Environment = "prod"
|
||||
CostCenter = "terraform"
|
||||
AcceptanceTest = "PagedTags"
|
||||
CreationToken = "radek"
|
||||
PerfMode = "max"
|
||||
Region = "us-west-2"
|
||||
}
|
||||
}
|
||||
`, rInt)
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSEFSFileSystemConfigWithTags = `
|
||||
resource "aws_efs_file_system" "foo-with-tags" {
|
||||
creation_token = "yada_yada"
|
||||
tags {
|
||||
Name = "foo-efs"
|
||||
Another = "tag"
|
||||
func testAccAWSEFSFileSystemConfigWithTags(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_efs_file_system" "foo-with-tags" {
|
||||
creation_token = "yada_yada"
|
||||
tags {
|
||||
Name = "foo-efs-%d"
|
||||
Another = "tag"
|
||||
}
|
||||
}
|
||||
`, rInt)
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSEFSFileSystemConfigWithPerformanceMode = `
|
||||
resource "aws_efs_file_system" "foo-with-performance-mode" {
|
||||
|
|
|
@ -661,7 +661,7 @@ resource "aws_elastic_beanstalk_environment" "tfenvtest" {
|
|||
func testAccBeanstalkWorkerEnvConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_instance_profile" "tftest" {
|
||||
name = "tftest_profile"
|
||||
name = "tftest_profile-%d"
|
||||
roles = ["${aws_iam_role.tftest.name}"]
|
||||
}
|
||||
|
||||
|
@ -693,7 +693,7 @@ func testAccBeanstalkWorkerEnvConfig(rInt int) string {
|
|||
name = "IamInstanceProfile"
|
||||
value = "${aws_iam_instance_profile.tftest.name}"
|
||||
}
|
||||
}`, rInt, rInt)
|
||||
}`, rInt, rInt, rInt)
|
||||
}
|
||||
|
||||
func testAccBeanstalkEnvCnamePrefixConfig(randString string, rInt int) string {
|
||||
|
@ -937,24 +937,24 @@ resource "aws_s3_bucket_object" "default" {
|
|||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_application" "default" {
|
||||
name = "tf-test-name"
|
||||
name = "tf-test-name-%d"
|
||||
description = "tf-test-desc"
|
||||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_application_version" "default" {
|
||||
application = "tf-test-name"
|
||||
application = "tf-test-name-%d"
|
||||
name = "tf-test-version-label"
|
||||
bucket = "${aws_s3_bucket.default.id}"
|
||||
key = "${aws_s3_bucket_object.default.id}"
|
||||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_environment" "default" {
|
||||
name = "tf-test-name"
|
||||
name = "tf-test-name-%d"
|
||||
application = "${aws_elastic_beanstalk_application.default.name}"
|
||||
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
||||
solution_stack_name = "64bit Amazon Linux running Python"
|
||||
}
|
||||
`, randInt)
|
||||
`, randInt, randInt, randInt, randInt)
|
||||
}
|
||||
|
||||
func testAccBeanstalkEnvApplicationVersionConfigUpdate(randInt int) string {
|
||||
|
@ -970,22 +970,22 @@ resource "aws_s3_bucket_object" "default" {
|
|||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_application" "default" {
|
||||
name = "tf-test-name"
|
||||
name = "tf-test-name-%d"
|
||||
description = "tf-test-desc"
|
||||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_application_version" "default" {
|
||||
application = "tf-test-name"
|
||||
application = "tf-test-name-%d"
|
||||
name = "tf-test-version-label-v2"
|
||||
bucket = "${aws_s3_bucket.default.id}"
|
||||
key = "${aws_s3_bucket_object.default.id}"
|
||||
}
|
||||
|
||||
resource "aws_elastic_beanstalk_environment" "default" {
|
||||
name = "tf-test-name"
|
||||
name = "tf-test-name-%d"
|
||||
application = "${aws_elastic_beanstalk_application.default.name}"
|
||||
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
||||
solution_stack_name = "64bit Amazon Linux running Python"
|
||||
}
|
||||
`, randInt)
|
||||
`, randInt, randInt, randInt, randInt)
|
||||
}
|
||||
|
|
|
@ -86,10 +86,20 @@ func resourceAwsIamInstanceProfile() *schema.Resource {
|
|||
},
|
||||
|
||||
"roles": {
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ConflictsWith: []string{"role"},
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
Deprecated: "Use `role` instead. Only a single role can be passed to an IAM Instance Profile",
|
||||
},
|
||||
|
||||
"role": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ConflictsWith: []string{"roles"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -107,6 +117,13 @@ func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{
|
|||
name = resource.UniqueId()
|
||||
}
|
||||
|
||||
_, hasRoles := d.GetOk("roles")
|
||||
_, hasRole := d.GetOk("role")
|
||||
|
||||
if hasRole == false && hasRoles == false {
|
||||
return fmt.Errorf("Either `roles` or `role` must be specified when creating an IAM Instance Profile")
|
||||
}
|
||||
|
||||
request := &iam.CreateInstanceProfileInput{
|
||||
InstanceProfileName: aws.String(name),
|
||||
Path: aws.String(d.Get("path").(string)),
|
||||
|
@ -132,7 +149,7 @@ func resourceAwsIamInstanceProfileCreate(d *schema.ResourceData, meta interface{
|
|||
return fmt.Errorf("Timed out while waiting for instance profile %s: %s", name, err)
|
||||
}
|
||||
|
||||
return instanceProfileSetRoles(d, iamconn)
|
||||
return resourceAwsIamInstanceProfileUpdate(d, meta)
|
||||
}
|
||||
|
||||
func instanceProfileAddRole(iamconn *iam.IAM, profileName, roleName string) error {
|
||||
|
@ -205,11 +222,35 @@ func instanceProfileRemoveAllRoles(d *schema.ResourceData, iamconn *iam.IAM) err
|
|||
func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
iamconn := meta.(*AWSClient).iamconn
|
||||
|
||||
if !d.HasChange("roles") {
|
||||
return nil
|
||||
d.Partial(true)
|
||||
|
||||
if d.HasChange("role") {
|
||||
oldRole, newRole := d.GetChange("role")
|
||||
|
||||
if oldRole.(string) != "" {
|
||||
err := instanceProfileRemoveRole(iamconn, d.Id(), oldRole.(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", oldRole.(string), d.Id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if newRole.(string) != "" {
|
||||
err := instanceProfileAddRole(iamconn, d.Id(), newRole.(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error adding role %s to IAM instance profile %s: %s", newRole.(string), d.Id(), err)
|
||||
}
|
||||
}
|
||||
|
||||
d.SetPartial("role")
|
||||
}
|
||||
|
||||
return instanceProfileSetRoles(d, iamconn)
|
||||
if d.HasChange("roles") {
|
||||
return instanceProfileSetRoles(d, iamconn)
|
||||
}
|
||||
|
||||
d.Partial(false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
@ -262,6 +303,10 @@ func instanceProfileReadResult(d *schema.ResourceData, result *iam.InstanceProfi
|
|||
}
|
||||
d.Set("unique_id", result.InstanceProfileId)
|
||||
|
||||
if result.Roles != nil && len(result.Roles) > 0 {
|
||||
d.Set("role", result.Roles[0].RoleName) //there will only be 1 role returned
|
||||
}
|
||||
|
||||
roles := &schema.Set{F: schema.HashString}
|
||||
for _, role := range result.Roles {
|
||||
roles.Add(*role.RoleName)
|
||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -37,6 +38,8 @@ func TestAccAWSIAMInstanceProfile_importBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAccAWSIAMInstanceProfile_basic(t *testing.T) {
|
||||
var conf iam.GetInstanceProfileOutput
|
||||
|
||||
rName := acctest.RandString(5)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -44,6 +47,41 @@ func TestAccAWSIAMInstanceProfile_basic(t *testing.T) {
|
|||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsIamInstanceProfileConfig(rName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSInstanceProfileExists("aws_iam_instance_profile.test", &conf),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSIAMInstanceProfile_withRoleNotRoles(t *testing.T) {
|
||||
var conf iam.GetInstanceProfileOutput
|
||||
|
||||
rName := acctest.RandString(5)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSInstanceProfileWithRoleSpecified(rName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSInstanceProfileExists("aws_iam_instance_profile.test", &conf),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSIAMInstanceProfile_missingRoleThrowsError(t *testing.T) {
|
||||
rName := acctest.RandString(5)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsIamInstanceProfileConfigMissingRole(rName),
|
||||
ExpectError: regexp.MustCompile("Either `roles` or `role` must be specified when creating an IAM Instance Profile"),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -157,6 +195,13 @@ resource "aws_iam_instance_profile" "test" {
|
|||
}`, rName)
|
||||
}
|
||||
|
||||
func testAccAwsIamInstanceProfileConfigMissingRole(rName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_instance_profile" "test" {
|
||||
name = "test-%s"
|
||||
}`, rName)
|
||||
}
|
||||
|
||||
func testAccAWSInstanceProfilePrefixNameConfig(rName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "test" {
|
||||
|
@ -169,3 +214,16 @@ resource "aws_iam_instance_profile" "test" {
|
|||
roles = ["${aws_iam_role.test.name}"]
|
||||
}`, rName)
|
||||
}
|
||||
|
||||
func testAccAWSInstanceProfileWithRoleSpecified(rName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "test" {
|
||||
name = "test-%s"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "test" {
|
||||
name_prefix = "test-"
|
||||
role = "${aws_iam_role.test.name}"
|
||||
}`, rName)
|
||||
}
|
||||
|
|
|
@ -20,37 +20,41 @@ func resourceAwsIAMServerCertificate() *schema.Resource {
|
|||
Create: resourceAwsIAMServerCertificateCreate,
|
||||
Read: resourceAwsIAMServerCertificateRead,
|
||||
Delete: resourceAwsIAMServerCertificateDelete,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: resourceAwsIAMServerCertificateImport,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"certificate_body": &schema.Schema{
|
||||
"certificate_body": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
StateFunc: normalizeCert,
|
||||
},
|
||||
|
||||
"certificate_chain": &schema.Schema{
|
||||
"certificate_chain": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
StateFunc: normalizeCert,
|
||||
},
|
||||
|
||||
"path": &schema.Schema{
|
||||
"path": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "/",
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"private_key": &schema.Schema{
|
||||
"private_key": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
StateFunc: normalizeCert,
|
||||
Sensitive: true,
|
||||
},
|
||||
|
||||
"name": &schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
|
@ -66,7 +70,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource {
|
|||
},
|
||||
},
|
||||
|
||||
"name_prefix": &schema.Schema{
|
||||
"name_prefix": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
|
@ -80,7 +84,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource {
|
|||
},
|
||||
},
|
||||
|
||||
"arn": &schema.Schema{
|
||||
"arn": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
|
@ -148,6 +152,8 @@ func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{
|
|||
return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(*resp.ServerCertificate.ServerCertificateMetadata.ServerCertificateId)
|
||||
|
||||
// these values should always be present, and have a default if not set in
|
||||
// configuration, and so safe to reference with nil checks
|
||||
d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody))
|
||||
|
@ -196,6 +202,13 @@ func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interfac
|
|||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsIAMServerCertificateImport(
|
||||
d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
d.Set("name", d.Id())
|
||||
// private_key can't be fetched from any API call
|
||||
return []*schema.ResourceData{d}, nil
|
||||
}
|
||||
|
||||
func normalizeCert(cert interface{}) string {
|
||||
if cert == nil || cert == (*string)(nil) {
|
||||
return ""
|
||||
|
|
|
@ -2,10 +2,8 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
|
@ -16,14 +14,15 @@ import (
|
|||
|
||||
func TestAccAWSIAMServerCertificate_basic(t *testing.T) {
|
||||
var cert iam.ServerCertificate
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccIAMServerCertConfig,
|
||||
{
|
||||
Config: testAccIAMServerCertConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
testAccCheckAWSServerCertAttributes(&cert),
|
||||
|
@ -41,7 +40,7 @@ func TestAccAWSIAMServerCertificate_name_prefix(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccIAMServerCertConfig_random,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
|
@ -74,7 +73,7 @@ func TestAccAWSIAMServerCertificate_disappears(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccIAMServerCertConfig_random,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
|
@ -97,7 +96,7 @@ func TestAccAWSIAMServerCertificate_file(t *testing.T) {
|
|||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-unix-line-endings"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
|
@ -105,7 +104,7 @@ func TestAccAWSIAMServerCertificate_file(t *testing.T) {
|
|||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-windows-line-endings"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
|
@ -202,7 +201,8 @@ CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4
|
|||
GnSud83VUo9G9w==
|
||||
-----END CERTIFICATE-----`)
|
||||
|
||||
var testAccIAMServerCertConfig = fmt.Sprintf(`
|
||||
func testAccIAMServerCertConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_server_certificate" "test_cert" {
|
||||
name = "terraform-test-cert-%d"
|
||||
certificate_body = <<EOF
|
||||
|
@ -257,7 +257,8 @@ dg+Sd4Wjm89UQoUUoiIcstY7FPbqfBtYKfh4RYHAHV2BwDFqzZCM
|
|||
-----END RSA PRIVATE KEY-----
|
||||
EOF
|
||||
}
|
||||
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
|
||||
`, rInt)
|
||||
}
|
||||
|
||||
var testAccIAMServerCertConfig_random = `
|
||||
resource "aws_iam_server_certificate" "test_cert" {
|
||||
|
|
|
@ -201,7 +201,8 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
|
|||
for _, e := range networkAcl.Entries {
|
||||
// Skip the default rules added by AWS. They can be neither
|
||||
// configured or deleted by users.
|
||||
if *e.RuleNumber == awsDefaultAclRuleNumber {
|
||||
if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 ||
|
||||
*e.RuleNumber == awsDefaultAclRuleNumberIpv6 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -358,7 +359,8 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2
|
|||
// neither modified nor destroyed. They have a custom rule
|
||||
// number that is out of bounds for any other rule. If we
|
||||
// encounter it, just continue. There's no work to be done.
|
||||
if *remove.RuleNumber == awsDefaultAclRuleNumber {
|
||||
if *remove.RuleNumber == awsDefaultAclRuleNumberIpv4 ||
|
||||
*remove.RuleNumber == awsDefaultAclRuleNumberIpv6 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,12 @@ func resourceAwsNetworkAclRule() *schema.Resource {
|
|||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
if old == "all" && new == "-1" || old == "-1" && new == "all" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
"rule_action": {
|
||||
Type: schema.TypeString,
|
||||
|
|
|
@ -66,6 +66,25 @@ func TestAccAWSNetworkAclRule_ipv6(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSNetworkAclRule_allProtocol(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSNetworkAclRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSNetworkAclRuleAllProtocolConfig,
|
||||
ExpectNonEmptyPlan: false,
|
||||
},
|
||||
{
|
||||
Config: testAccAWSNetworkAclRuleAllProtocolConfigNoRealUpdate,
|
||||
ExpectNonEmptyPlan: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceAWSNetworkAclRule_validateICMPArgumentValue(t *testing.T) {
|
||||
type testCases struct {
|
||||
Value string
|
||||
|
@ -251,6 +270,44 @@ resource "aws_network_acl_rule" "baz" {
|
|||
}
|
||||
`
|
||||
|
||||
const testAccAWSNetworkAclRuleAllProtocolConfigNoRealUpdate = `
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.3.0.0/16"
|
||||
}
|
||||
resource "aws_network_acl" "bar" {
|
||||
vpc_id = "${aws_vpc.foo.id}"
|
||||
}
|
||||
resource "aws_network_acl_rule" "baz" {
|
||||
network_acl_id = "${aws_network_acl.bar.id}"
|
||||
rule_number = 150
|
||||
egress = false
|
||||
protocol = "all"
|
||||
rule_action = "allow"
|
||||
cidr_block = "0.0.0.0/0"
|
||||
from_port = 22
|
||||
to_port = 22
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSNetworkAclRuleAllProtocolConfig = `
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.3.0.0/16"
|
||||
}
|
||||
resource "aws_network_acl" "bar" {
|
||||
vpc_id = "${aws_vpc.foo.id}"
|
||||
}
|
||||
resource "aws_network_acl_rule" "baz" {
|
||||
network_acl_id = "${aws_network_acl.bar.id}"
|
||||
rule_number = 150
|
||||
egress = false
|
||||
protocol = "-1"
|
||||
rule_action = "allow"
|
||||
cidr_block = "0.0.0.0/0"
|
||||
from_port = 22
|
||||
to_port = 22
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSNetworkAclRuleIpv6Config = `
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.3.0.0/16"
|
||||
|
|
|
@ -242,6 +242,8 @@ func TestAccAWSNetworkAcl_ipv6Rules(t *testing.T) {
|
|||
Config: testAccAWSNetworkAclIpv6Config,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_network_acl.foos", "ingress.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_network_acl.foos", "ingress.1976110835.protocol", "6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
|
@ -260,6 +262,29 @@ func TestAccAWSNetworkAcl_ipv6Rules(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSNetworkAcl_ipv6VpcRules(t *testing.T) {
|
||||
var networkAcl ec2.NetworkAcl
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_network_acl.foos",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSNetworkAclDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSNetworkAclIpv6VpcConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_network_acl.foos", "ingress.#", "1"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_network_acl.foos", "ingress.1296304962.ipv6_cidr_block", "2600:1f16:d1e:9a00::/56"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSNetworkAcl_espProtocol(t *testing.T) {
|
||||
var networkAcl ec2.NetworkAcl
|
||||
|
||||
|
@ -436,6 +461,33 @@ resource "aws_network_acl" "foos" {
|
|||
}
|
||||
`
|
||||
|
||||
const testAccAWSNetworkAclIpv6VpcConfig = `
|
||||
provider "aws" {
|
||||
region = "us-east-2"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
assign_generated_ipv6_cidr_block = true
|
||||
|
||||
tags {
|
||||
Name = "TestAccAWSNetworkAcl_ipv6VpcRules"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_network_acl" "foos" {
|
||||
vpc_id = "${aws_vpc.foo.id}"
|
||||
ingress = {
|
||||
protocol = "tcp"
|
||||
rule_no = 1
|
||||
action = "allow"
|
||||
ipv6_cidr_block = "2600:1f16:d1e:9a00::/56"
|
||||
from_port = 0
|
||||
to_port = 22
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSNetworkAclIngressConfig = `
|
||||
resource "aws_vpc" "foo" {
|
||||
cidr_block = "10.1.0.0/16"
|
||||
|
|
|
@ -565,6 +565,10 @@ func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) e
|
|||
for _, v := range instance.LayerIds {
|
||||
layerIds = append(layerIds, *v)
|
||||
}
|
||||
layerIds, err = sortListBasedonTFFile(layerIds, d, "layer_ids")
|
||||
if err != nil {
|
||||
return fmt.Errorf("[DEBUG] Error sorting layer_ids attribute: %#v", err)
|
||||
}
|
||||
if err := d.Set("layer_ids", layerIds); err != nil {
|
||||
return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err)
|
||||
}
|
||||
|
@ -820,7 +824,6 @@ func resourceAwsOpsworksInstanceUpdate(d *schema.ResourceData, meta interface{})
|
|||
|
||||
if v, ok := d.GetOk("layer_ids"); ok {
|
||||
req.LayerIds = expandStringList(v.([]interface{}))
|
||||
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("os"); ok {
|
||||
|
|
|
@ -3,14 +3,17 @@ package aws
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||
)
|
||||
|
||||
|
@ -183,6 +186,11 @@ func resourceAwsOpsworksStack() *schema.Resource {
|
|||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"stack_endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -254,6 +262,13 @@ func resourceAwsOpsworksSetStackCustomCookbooksSource(d *schema.ResourceData, v
|
|||
|
||||
func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
req := &opsworks.DescribeStacksInput{
|
||||
StackIds: []*string{
|
||||
|
@ -263,16 +278,53 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
|||
|
||||
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
|
||||
|
||||
resp, err := client.DescribeStacks(req)
|
||||
if err != nil {
|
||||
if awserr, ok := err.(awserr.Error); ok {
|
||||
if awserr.Code() == "ResourceNotFoundException" {
|
||||
log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
// notFound represents the number of times we've called DescribeStacks looking
|
||||
// for this Stack. If it's not found in the the default region we're in, we
|
||||
// check us-east-1 in the event this stack was created with Terraform before
|
||||
// version 0.9
|
||||
// See https://github.com/hashicorp/terraform/issues/12842
|
||||
var notFound int
|
||||
var resp *opsworks.DescribeStacksOutput
|
||||
var dErr error
|
||||
|
||||
for {
|
||||
resp, dErr = client.DescribeStacks(req)
|
||||
if dErr != nil {
|
||||
if awserr, ok := dErr.(awserr.Error); ok {
|
||||
if awserr.Code() == "ResourceNotFoundException" {
|
||||
if notFound < 1 {
|
||||
// If we haven't already, try us-east-1, legacy connection
|
||||
notFound++
|
||||
var connErr error
|
||||
client, connErr = opsworksConnForRegion("us-east-1", meta)
|
||||
if connErr != nil {
|
||||
return connErr
|
||||
}
|
||||
// start again from the top of the FOR loop, but with a client
|
||||
// configured to talk to us-east-1
|
||||
continue
|
||||
}
|
||||
|
||||
// We've tried both the original and us-east-1 endpoint, and the stack
|
||||
// is still not found
|
||||
log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id())
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
// not ResoureNotFoundException, fall through to returning error
|
||||
}
|
||||
return dErr
|
||||
}
|
||||
// If the stack was found, set the stack_endpoint
|
||||
if client.Config.Region != nil && *client.Config.Region != "" {
|
||||
log.Printf("[DEBUG] Setting stack_endpoint for (%s) to (%s)", d.Id(), *client.Config.Region)
|
||||
if err := d.Set("stack_endpoint", *client.Config.Region); err != nil {
|
||||
log.Printf("[WARN] Error setting stack_endpoint: %s", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
log.Printf("[DEBUG] Breaking stack endpoint search, found stack for (%s)", d.Id())
|
||||
// Break the FOR loop
|
||||
break
|
||||
}
|
||||
|
||||
stack := resp.Stacks[0]
|
||||
|
@ -309,6 +361,40 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
// opsworksConn will return a connection for the stack_endpoint in the
|
||||
// configuration. Stacks can only be accessed or managed within the endpoint
|
||||
// in which they are created, so we allow users to specify an original endpoint
|
||||
// for Stacks created before multiple endpoints were offered (Terraform v0.9.0).
|
||||
// See:
|
||||
// - https://github.com/hashicorp/terraform/pull/12688
|
||||
// - https://github.com/hashicorp/terraform/issues/12842
|
||||
func opsworksConnForRegion(region string, meta interface{}) (*opsworks.OpsWorks, error) {
|
||||
originalConn := meta.(*AWSClient).opsworksconn
|
||||
|
||||
// Regions are the same, no need to reconfigure
|
||||
if originalConn.Config.Region != nil && *originalConn.Config.Region == region {
|
||||
return originalConn, nil
|
||||
}
|
||||
|
||||
// Set up base session
|
||||
sess, err := session.NewSession(&originalConn.Config)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err)
|
||||
}
|
||||
|
||||
sess.Handlers.Build.PushBackNamed(addTerraformVersionToUserAgent)
|
||||
|
||||
if extraDebug := os.Getenv("TERRAFORM_AWS_AUTHFAILURE_DEBUG"); extraDebug != "" {
|
||||
sess.Handlers.UnmarshalError.PushFrontNamed(debugAuthFailure)
|
||||
}
|
||||
|
||||
newSession := sess.Copy(&aws.Config{Region: aws.String(region)})
|
||||
newOpsworksconn := opsworks.New(newSession)
|
||||
|
||||
log.Printf("[DEBUG] Returning new OpsWorks client")
|
||||
return newOpsworksconn, nil
|
||||
}
|
||||
|
||||
func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
|
||||
|
@ -396,6 +482,13 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
err := resourceAwsOpsworksStackValidate(d)
|
||||
if err != nil {
|
||||
|
@ -456,6 +549,13 @@ func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) er
|
|||
|
||||
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient).opsworksconn
|
||||
var conErr error
|
||||
if v := d.Get("stack_endpoint").(string); v != "" {
|
||||
client, conErr = opsworksConnForRegion(v, meta)
|
||||
if conErr != nil {
|
||||
return conErr
|
||||
}
|
||||
}
|
||||
|
||||
req := &opsworks.DeleteStackInput{
|
||||
StackId: aws.String(d.Id()),
|
||||
|
|
|
@ -74,6 +74,205 @@ func TestAccAWSOpsworksStackVpc(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// Tests the addition of regional endpoints and supporting the classic link used
|
||||
// to create Stack's prior to v0.9.0.
|
||||
// See https://github.com/hashicorp/terraform/issues/12842
|
||||
func TestAccAWSOpsWorksStack_classic_endpoints(t *testing.T) {
|
||||
stackName := fmt.Sprintf("tf-opsworks-acc-%d", acctest.RandInt())
|
||||
rInt := acctest.RandInt()
|
||||
var opsstack opsworks.Stack
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAwsOpsWorksStack_classic_endpoint(stackName, rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSOpsworksStackExists(
|
||||
"aws_opsworks_stack.main", false, &opsstack),
|
||||
),
|
||||
},
|
||||
// Ensure that changing to us-west-2 region results in no plan
|
||||
resource.TestStep{
|
||||
Config: testAccAwsOpsWorksStack_regional_endpoint(stackName, rInt),
|
||||
PlanOnly: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccAwsOpsWorksStack_classic_endpoint(rName string, rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
resource "aws_opsworks_stack" "main" {
|
||||
name = "%s"
|
||||
region = "us-west-2"
|
||||
service_role_arn = "${aws_iam_role.opsworks_service.arn}"
|
||||
default_instance_profile_arn = "${aws_iam_instance_profile.opsworks_instance.arn}"
|
||||
|
||||
configuration_manager_version = "12"
|
||||
default_availability_zone = "us-west-2b"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "opsworks.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
role = "${aws_iam_role.opsworks_service.id}"
|
||||
|
||||
policy = <<EOT
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"iam:PassRole",
|
||||
"cloudwatch:GetMetricStatistics",
|
||||
"elasticloadbalancing:*",
|
||||
"rds:*"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_instance" {
|
||||
name = "tf_opsworks_instance_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "opsworks_instance" {
|
||||
name = "%s_profile"
|
||||
roles = ["${aws_iam_role.opsworks_instance.name}"]
|
||||
}`, rName, rInt, rInt, rInt, rName)
|
||||
}
|
||||
|
||||
func testAccAwsOpsWorksStack_regional_endpoint(rName string, rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_opsworks_stack" "main" {
|
||||
name = "%s"
|
||||
region = "us-west-2"
|
||||
service_role_arn = "${aws_iam_role.opsworks_service.arn}"
|
||||
default_instance_profile_arn = "${aws_iam_instance_profile.opsworks_instance.arn}"
|
||||
|
||||
configuration_manager_version = "12"
|
||||
default_availability_zone = "us-west-2b"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "opsworks.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "opsworks_service" {
|
||||
name = "tf_opsworks_service_%d"
|
||||
role = "${aws_iam_role.opsworks_service.id}"
|
||||
|
||||
policy = <<EOT
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"ec2:*",
|
||||
"iam:PassRole",
|
||||
"cloudwatch:GetMetricStatistics",
|
||||
"elasticloadbalancing:*",
|
||||
"rds:*"
|
||||
],
|
||||
"Effect": "Allow",
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "opsworks_instance" {
|
||||
name = "tf_opsworks_instance_%d"
|
||||
|
||||
assume_role_policy = <<EOT
|
||||
{
|
||||
"Version": "2008-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "opsworks_instance" {
|
||||
name = "%s_profile"
|
||||
roles = ["${aws_iam_role.opsworks_instance.name}"]
|
||||
}`, rName, rInt, rInt, rInt, rName)
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
//// Checkers and Utilities
|
||||
////////////////////////////
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -42,7 +43,7 @@ func testAccCheckSESConfigurationSetDestroy(s *terraform.State) error {
|
|||
|
||||
found := false
|
||||
for _, element := range response.ConfigurationSets {
|
||||
if *element.Name == "some-configuration-set" {
|
||||
if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +78,7 @@ func testAccCheckAwsSESConfigurationSetExists(n string) resource.TestCheckFunc {
|
|||
|
||||
found := false
|
||||
for _, element := range response.ConfigurationSets {
|
||||
if *element.Name == "some-configuration-set" {
|
||||
if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +91,9 @@ func testAccCheckAwsSESConfigurationSetExists(n string) resource.TestCheckFunc {
|
|||
}
|
||||
}
|
||||
|
||||
const testAccAWSSESConfigurationSetConfig = `
|
||||
var escRandomInteger = acctest.RandInt()
|
||||
var testAccAWSSESConfigurationSetConfig = fmt.Sprintf(`
|
||||
resource "aws_ses_configuration_set" "test" {
|
||||
name = "some-configuration-set"
|
||||
name = "some-configuration-set-%d"
|
||||
}
|
||||
`
|
||||
`, escRandomInteger)
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -46,7 +47,7 @@ func testAccCheckSESEventDestinationDestroy(s *terraform.State) error {
|
|||
|
||||
found := false
|
||||
for _, element := range response.ConfigurationSets {
|
||||
if *element.Name == "some-configuration-set" {
|
||||
if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ func testAccCheckAwsSESEventDestinationExists(n string) resource.TestCheckFunc {
|
|||
|
||||
found := false
|
||||
for _, element := range response.ConfigurationSets {
|
||||
if *element.Name == "some-configuration-set" {
|
||||
if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +95,8 @@ func testAccCheckAwsSESEventDestinationExists(n string) resource.TestCheckFunc {
|
|||
}
|
||||
}
|
||||
|
||||
const testAccAWSSESEventDestinationConfig = `
|
||||
var edRandomInteger = acctest.RandInt()
|
||||
var testAccAWSSESEventDestinationConfig = fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "tf-test-bucket-format"
|
||||
acl = "private"
|
||||
|
@ -155,7 +157,7 @@ data "aws_iam_policy_document" "fh_felivery_document" {
|
|||
}
|
||||
|
||||
resource "aws_ses_configuration_set" "test" {
|
||||
name = "some-configuration-set"
|
||||
name = "some-configuration-set-%d"
|
||||
}
|
||||
|
||||
resource "aws_ses_event_destination" "kinesis" {
|
||||
|
@ -182,4 +184,4 @@ resource "aws_ses_event_destination" "cloudwatch" {
|
|||
value_source = "emailHeader"
|
||||
}
|
||||
}
|
||||
`
|
||||
`, edRandomInteger)
|
||||
|
|
|
@ -443,7 +443,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
addHeaderAction := map[string]interface{}{
|
||||
"header_name": *element.AddHeaderAction.HeaderName,
|
||||
"header_value": *element.AddHeaderAction.HeaderValue,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
addHeaderActionList = append(addHeaderActionList, addHeaderAction)
|
||||
}
|
||||
|
@ -453,7 +453,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
"message": *element.BounceAction.Message,
|
||||
"sender": *element.BounceAction.Sender,
|
||||
"smtp_reply_code": *element.BounceAction.SmtpReplyCode,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
if element.BounceAction.StatusCode != nil {
|
||||
|
@ -470,7 +470,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
if element.LambdaAction != nil {
|
||||
lambdaAction := map[string]interface{}{
|
||||
"function_arn": *element.LambdaAction.FunctionArn,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
if element.LambdaAction.InvocationType != nil {
|
||||
|
@ -487,7 +487,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
if element.S3Action != nil {
|
||||
s3Action := map[string]interface{}{
|
||||
"bucket_name": *element.S3Action.BucketName,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
if element.S3Action.KmsKeyArn != nil {
|
||||
|
@ -508,7 +508,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
if element.SNSAction != nil {
|
||||
snsAction := map[string]interface{}{
|
||||
"topic_arn": *element.SNSAction.TopicArn,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
snsActionList = append(snsActionList, snsAction)
|
||||
|
@ -517,7 +517,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
if element.StopAction != nil {
|
||||
stopAction := map[string]interface{}{
|
||||
"scope": *element.StopAction.Scope,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
if element.StopAction.TopicArn != nil {
|
||||
|
@ -530,7 +530,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
|||
if element.WorkmailAction != nil {
|
||||
workmailAction := map[string]interface{}{
|
||||
"organization_arn": *element.WorkmailAction.OrganizationArn,
|
||||
"position": i,
|
||||
"position": i + 1,
|
||||
}
|
||||
|
||||
if element.WorkmailAction.TopicArn != nil {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ses"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -111,7 +112,7 @@ func testAccCheckAwsSESReceiptRuleExists(n string) resource.TestCheckFunc {
|
|||
|
||||
params := &ses.DescribeReceiptRuleInput{
|
||||
RuleName: aws.String("basic"),
|
||||
RuleSetName: aws.String("test-me"),
|
||||
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||
}
|
||||
|
||||
response, err := conn.DescribeReceiptRule(params)
|
||||
|
@ -153,7 +154,7 @@ func testAccCheckAwsSESReceiptRuleOrder(n string) resource.TestCheckFunc {
|
|||
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
||||
|
||||
params := &ses.DescribeReceiptRuleSetInput{
|
||||
RuleSetName: aws.String("test-me"),
|
||||
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||
}
|
||||
|
||||
response, err := conn.DescribeReceiptRuleSet(params)
|
||||
|
@ -185,8 +186,8 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc {
|
|||
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
||||
|
||||
params := &ses.DescribeReceiptRuleInput{
|
||||
RuleName: aws.String("actions"),
|
||||
RuleSetName: aws.String("test-me"),
|
||||
RuleName: aws.String("actions4"),
|
||||
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||
}
|
||||
|
||||
response, err := conn.DescribeReceiptRule(params)
|
||||
|
@ -227,9 +228,10 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc {
|
|||
}
|
||||
}
|
||||
|
||||
const testAccAWSSESReceiptRuleBasicConfig = `
|
||||
var srrsRandomInt = acctest.RandInt()
|
||||
var testAccAWSSESReceiptRuleBasicConfig = fmt.Sprintf(`
|
||||
resource "aws_ses_receipt_rule_set" "test" {
|
||||
rule_set_name = "test-me"
|
||||
rule_set_name = "test-me-%d"
|
||||
}
|
||||
|
||||
resource "aws_ses_receipt_rule" "basic" {
|
||||
|
@ -240,11 +242,11 @@ resource "aws_ses_receipt_rule" "basic" {
|
|||
scan_enabled = true
|
||||
tls_policy = "Require"
|
||||
}
|
||||
`
|
||||
`, srrsRandomInt)
|
||||
|
||||
const testAccAWSSESReceiptRuleOrderConfig = `
|
||||
var testAccAWSSESReceiptRuleOrderConfig = fmt.Sprintf(`
|
||||
resource "aws_ses_receipt_rule_set" "test" {
|
||||
rule_set_name = "test-me"
|
||||
rule_set_name = "test-me-%d"
|
||||
}
|
||||
|
||||
resource "aws_ses_receipt_rule" "second" {
|
||||
|
@ -257,36 +259,36 @@ resource "aws_ses_receipt_rule" "first" {
|
|||
name = "first"
|
||||
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
||||
}
|
||||
`
|
||||
`, srrsRandomInt)
|
||||
|
||||
const testAccAWSSESReceiptRuleActionsConfig = `
|
||||
var testAccAWSSESReceiptRuleActionsConfig = fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "emails" {
|
||||
bucket = "ses-terraform-emails"
|
||||
}
|
||||
|
||||
resource "aws_ses_receipt_rule_set" "test" {
|
||||
rule_set_name = "test-me"
|
||||
rule_set_name = "test-me-%d"
|
||||
}
|
||||
|
||||
resource "aws_ses_receipt_rule" "actions" {
|
||||
name = "actions"
|
||||
name = "actions4"
|
||||
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
||||
|
||||
add_header_action {
|
||||
header_name = "Added-By"
|
||||
header_value = "Terraform"
|
||||
position = 1
|
||||
header_name = "Added-By"
|
||||
header_value = "Terraform"
|
||||
position = 1
|
||||
}
|
||||
|
||||
add_header_action {
|
||||
header_name = "Another-Header"
|
||||
header_value = "First"
|
||||
position = 0
|
||||
header_name = "Another-Header"
|
||||
header_value = "First"
|
||||
position = 0
|
||||
}
|
||||
|
||||
stop_action {
|
||||
scope = "RuleSet"
|
||||
position = 2
|
||||
scope = "RuleSet"
|
||||
position = 2
|
||||
}
|
||||
}
|
||||
`
|
||||
`, srrsRandomInt)
|
||||
|
|
|
@ -22,6 +22,9 @@ func resourceAwsVpc() *schema.Resource {
|
|||
State: resourceAwsVpcInstanceImport,
|
||||
},
|
||||
|
||||
SchemaVersion: 1,
|
||||
MigrateState: resourceAwsVpcMigrateState,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cidr_block": {
|
||||
Type: schema.TypeString,
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func resourceAwsVpcMigrateState(
|
||||
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
|
||||
switch v {
|
||||
case 0:
|
||||
log.Println("[INFO] Found AWS VPC State v0; migrating to v1")
|
||||
return migrateVpcStateV0toV1(is)
|
||||
default:
|
||||
return is, fmt.Errorf("Unexpected schema version: %d", v)
|
||||
}
|
||||
}
|
||||
|
||||
func migrateVpcStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
|
||||
if is.Empty() || is.Attributes == nil {
|
||||
log.Println("[DEBUG] Empty VPC State; nothing to migrate.")
|
||||
return is, nil
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)
|
||||
|
||||
is.Attributes["assign_generated_ipv6_cidr_block"] = "false"
|
||||
|
||||
log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes)
|
||||
return is, nil
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAWSVpcMigrateState(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
StateVersion int
|
||||
ID string
|
||||
Attributes map[string]string
|
||||
Expected string
|
||||
Meta interface{}
|
||||
}{
|
||||
"v0_1": {
|
||||
StateVersion: 0,
|
||||
ID: "some_id",
|
||||
Attributes: map[string]string{
|
||||
"assign_generated_ipv6_cidr_block": "true",
|
||||
},
|
||||
Expected: "false",
|
||||
},
|
||||
"v0_1_without_value": {
|
||||
StateVersion: 0,
|
||||
ID: "some_id",
|
||||
Attributes: map[string]string{},
|
||||
Expected: "false",
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range cases {
|
||||
is := &terraform.InstanceState{
|
||||
ID: tc.ID,
|
||||
Attributes: tc.Attributes,
|
||||
}
|
||||
is, err := resourceAwsVpcMigrateState(
|
||||
tc.StateVersion, is, tc.Meta)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s, err: %#v", tn, err)
|
||||
}
|
||||
|
||||
if is.Attributes["assign_generated_ipv6_cidr_block"] != tc.Expected {
|
||||
t.Fatalf("bad VPC Migrate: %s\n\n expected: %s", is.Attributes["assign_generated_ipv6_cidr_block"], tc.Expected)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1490,6 +1490,22 @@ func sortInterfaceSlice(in []interface{}) []interface{} {
|
|||
return b
|
||||
}
|
||||
|
||||
// This function sorts List A to look like a list found in the tf file.
|
||||
func sortListBasedonTFFile(in []string, d *schema.ResourceData, listName string) ([]string, error) {
|
||||
if attributeCount, ok := d.Get(listName + ".#").(int); ok {
|
||||
for i := 0; i < attributeCount; i++ {
|
||||
currAttributeId := d.Get(listName + "." + strconv.Itoa(i))
|
||||
for j := 0; j < len(in); j++ {
|
||||
if currAttributeId == in[j] {
|
||||
in[i], in[j] = in[j], in[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return in, nil
|
||||
}
|
||||
return in, fmt.Errorf("Could not find list: %s", listName)
|
||||
}
|
||||
|
||||
func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, 1)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
func TestAccAzureRMTemplateDeployment_basic(t *testing.T) {
|
||||
ri := acctest.RandInt()
|
||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri)
|
||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicMultiple, ri, ri)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
@ -31,7 +31,7 @@ func TestAccAzureRMTemplateDeployment_basic(t *testing.T) {
|
|||
|
||||
func TestAccAzureRMTemplateDeployment_disappears(t *testing.T) {
|
||||
ri := acctest.RandInt()
|
||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri)
|
||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicSingle, ri, ri, ri)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
|
@ -163,7 +163,47 @@ func testCheckAzureRMTemplateDeploymentDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var testAccAzureRMTemplateDeployment_basicExample = `
|
||||
var testAccAzureRMTemplateDeployment_basicSingle = `
|
||||
resource "azurerm_resource_group" "test" {
|
||||
name = "acctestRG-%d"
|
||||
location = "West US"
|
||||
}
|
||||
|
||||
resource "azurerm_template_deployment" "test" {
|
||||
name = "acctesttemplate-%d"
|
||||
resource_group_name = "${azurerm_resource_group.test.name}"
|
||||
template_body = <<DEPLOY
|
||||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"variables": {
|
||||
"location": "[resourceGroup().location]",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"apiVersion": "2015-06-15",
|
||||
"dnsLabelPrefix": "[concat('terraform-tdacctest', uniquestring(resourceGroup().id))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.Network/publicIPAddresses",
|
||||
"apiVersion": "[variables('apiVersion')]",
|
||||
"name": "acctestpip-%d",
|
||||
"location": "[variables('location')]",
|
||||
"properties": {
|
||||
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
|
||||
"dnsSettings": {
|
||||
"domainNameLabel": "[variables('dnsLabelPrefix')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
DEPLOY
|
||||
deployment_mode = "Complete"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
var testAccAzureRMTemplateDeployment_basicMultiple = `
|
||||
resource "azurerm_resource_group" "test" {
|
||||
name = "acctestRG-%d"
|
||||
location = "West US"
|
||||
|
@ -196,7 +236,7 @@ var testAccAzureRMTemplateDeployment_basicExample = `
|
|||
"publicIPAddressName": "[concat('myPublicIp', uniquestring(resourceGroup().id))]",
|
||||
"publicIPAddressType": "Dynamic",
|
||||
"apiVersion": "2015-06-15",
|
||||
"dnsLabelPrefix": "terraform-tdacctest"
|
||||
"dnsLabelPrefix": "[concat('terraform-tdacctest', uniquestring(resourceGroup().id))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ const (
|
|||
|
||||
const (
|
||||
apiCheckTypeCAQL circonusCheckType = "caql"
|
||||
apiCheckTypeConsul circonusCheckType = "consul"
|
||||
apiCheckTypeICMPPing circonusCheckType = "ping_icmp"
|
||||
apiCheckTypeHTTP circonusCheckType = "http"
|
||||
apiCheckTypeJSON circonusCheckType = "json"
|
||||
|
@ -108,15 +109,24 @@ func (c *circonusCheck) Fixup() error {
|
|||
}
|
||||
|
||||
func (c *circonusCheck) Validate() error {
|
||||
if len(c.Metrics) == 0 {
|
||||
return fmt.Errorf("At least one %s must be specified", checkMetricAttr)
|
||||
}
|
||||
|
||||
if c.Timeout > float32(c.Period) {
|
||||
return fmt.Errorf("Timeout (%f) can not exceed period (%d)", c.Timeout, c.Period)
|
||||
}
|
||||
|
||||
// Check-type specific validation
|
||||
switch apiCheckType(c.Type) {
|
||||
case apiCheckTypeCloudWatchAttr:
|
||||
if !(c.Period == 60 || c.Period == 300) {
|
||||
return fmt.Errorf("Period must be either 1m or 5m for a %s check", apiCheckTypeCloudWatchAttr)
|
||||
}
|
||||
case apiCheckTypeConsulAttr:
|
||||
if v, found := c.Config[config.URL]; !found || v == "" {
|
||||
return fmt.Errorf("%s must have at least one check mode set: %s, %s, or %s must be set", checkConsulAttr, checkConsulServiceAttr, checkConsulNodeAttr, checkConsulStateAttr)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -17,6 +17,19 @@ const (
|
|||
providerAutoTagAttr = "auto_tag"
|
||||
providerKeyAttr = "key"
|
||||
|
||||
apiConsulCheckBlacklist = "check_name_blacklist"
|
||||
apiConsulDatacenterAttr = "dc"
|
||||
apiConsulNodeBlacklist = "node_blacklist"
|
||||
apiConsulServiceBlacklist = "service_blacklist"
|
||||
apiConsulStaleAttr = "stale"
|
||||
checkConsulTokenHeader = `X-Consul-Token`
|
||||
checkConsulV1NodePrefix = "node"
|
||||
checkConsulV1Prefix = "/v1/health"
|
||||
checkConsulV1ServicePrefix = "service"
|
||||
checkConsulV1StatePrefix = "state"
|
||||
defaultCheckConsulHTTPAddr = "http://consul.service.consul"
|
||||
defaultCheckConsulPort = "8500"
|
||||
|
||||
defaultCheckJSONMethod = "GET"
|
||||
defaultCheckJSONPort = "443"
|
||||
defaultCheckJSONVersion = "1.1"
|
||||
|
|
|
@ -33,21 +33,22 @@ const (
|
|||
checkCAQLAttr = "caql"
|
||||
checkCloudWatchAttr = "cloudwatch"
|
||||
checkCollectorAttr = "collector"
|
||||
checkConsulAttr = "consul"
|
||||
checkHTTPAttr = "http"
|
||||
checkHTTPTrapAttr = "httptrap"
|
||||
checkICMPPingAttr = "icmp_ping"
|
||||
checkJSONAttr = "json"
|
||||
checkMetricAttr = "metric"
|
||||
checkMetricLimitAttr = "metric_limit"
|
||||
checkMySQLAttr = "mysql"
|
||||
checkNameAttr = "name"
|
||||
checkNotesAttr = "notes"
|
||||
checkPeriodAttr = "period"
|
||||
checkPostgreSQLAttr = "postgresql"
|
||||
checkMetricAttr = "metric"
|
||||
checkStatsdAttr = "statsd"
|
||||
checkTCPAttr = "tcp"
|
||||
checkTagsAttr = "tags"
|
||||
checkTargetAttr = "target"
|
||||
checkTCPAttr = "tcp"
|
||||
checkTimeoutAttr = "timeout"
|
||||
checkTypeAttr = "type"
|
||||
|
||||
|
@ -75,6 +76,7 @@ const (
|
|||
// Circonus API constants from their API endpoints
|
||||
apiCheckTypeCAQLAttr apiCheckType = "caql"
|
||||
apiCheckTypeCloudWatchAttr apiCheckType = "cloudwatch"
|
||||
apiCheckTypeConsulAttr apiCheckType = "consul"
|
||||
apiCheckTypeHTTPAttr apiCheckType = "http"
|
||||
apiCheckTypeHTTPTrapAttr apiCheckType = "httptrap"
|
||||
apiCheckTypeICMPPingAttr apiCheckType = "ping_icmp"
|
||||
|
@ -90,6 +92,7 @@ var checkDescriptions = attrDescrs{
|
|||
checkCAQLAttr: "CAQL check configuration",
|
||||
checkCloudWatchAttr: "CloudWatch check configuration",
|
||||
checkCollectorAttr: "The collector(s) that are responsible for gathering the metrics",
|
||||
checkConsulAttr: "Consul check configuration",
|
||||
checkHTTPAttr: "HTTP check configuration",
|
||||
checkHTTPTrapAttr: "HTTP Trap check configuration",
|
||||
checkICMPPingAttr: "ICMP ping check configuration",
|
||||
|
@ -157,6 +160,7 @@ func resourceCheck() *schema.Resource {
|
|||
}),
|
||||
},
|
||||
},
|
||||
checkConsulAttr: schemaCheckConsul,
|
||||
checkHTTPAttr: schemaCheckHTTP,
|
||||
checkHTTPTrapAttr: schemaCheckHTTPTrap,
|
||||
checkJSONAttr: schemaCheckJSON,
|
||||
|
@ -577,6 +581,7 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error {
|
|||
checkTypeParseMap := map[string]func(*circonusCheck, interfaceList) error{
|
||||
checkCAQLAttr: checkConfigToAPICAQL,
|
||||
checkCloudWatchAttr: checkConfigToAPICloudWatch,
|
||||
checkConsulAttr: checkConfigToAPIConsul,
|
||||
checkHTTPAttr: checkConfigToAPIHTTP,
|
||||
checkHTTPTrapAttr: checkConfigToAPIHTTPTrap,
|
||||
checkICMPPingAttr: checkConfigToAPIICMPPing,
|
||||
|
@ -589,8 +594,17 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error {
|
|||
|
||||
for checkType, fn := range checkTypeParseMap {
|
||||
if listRaw, found := d.GetOk(checkType); found {
|
||||
if err := fn(c, listRaw.(*schema.Set).List()); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err)
|
||||
switch u := listRaw.(type) {
|
||||
case []interface{}:
|
||||
if err := fn(c, u); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err)
|
||||
}
|
||||
case *schema.Set:
|
||||
if err := fn(c, u.List()); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Unable to parse type %q: {{err}}", string(checkType)), err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("PROVIDER BUG: unsupported check type interface: %q", checkType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -604,6 +618,7 @@ func parseCheckTypeConfig(c *circonusCheck, d *schema.ResourceData) error {
|
|||
checkTypeConfigHandlers := map[apiCheckType]func(*circonusCheck, *schema.ResourceData) error{
|
||||
apiCheckTypeCAQLAttr: checkAPIToStateCAQL,
|
||||
apiCheckTypeCloudWatchAttr: checkAPIToStateCloudWatch,
|
||||
apiCheckTypeConsulAttr: checkAPIToStateConsul,
|
||||
apiCheckTypeHTTPAttr: checkAPIToStateHTTP,
|
||||
apiCheckTypeHTTPTrapAttr: checkAPIToStateHTTPTrap,
|
||||
apiCheckTypeICMPPingAttr: checkAPIToStateICMPPing,
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
package circonus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/circonus-labs/circonus-gometrics/api/config"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
// circonus_check.consul.* resource attribute names
|
||||
checkConsulACLTokenAttr = "acl_token"
|
||||
checkConsulAllowStaleAttr = "allow_stale"
|
||||
checkConsulCAChainAttr = "ca_chain"
|
||||
checkConsulCertFileAttr = "certificate_file"
|
||||
checkConsulCheckNameBlacklistAttr = "check_blacklist"
|
||||
checkConsulCiphersAttr = "ciphers"
|
||||
checkConsulDatacenterAttr = "dc"
|
||||
checkConsulHTTPAddrAttr = "http_addr"
|
||||
checkConsulHeadersAttr = "headers"
|
||||
checkConsulKeyFileAttr = "key_file"
|
||||
checkConsulNodeAttr = "node"
|
||||
checkConsulNodeBlacklistAttr = "node_blacklist"
|
||||
checkConsulServiceAttr = "service"
|
||||
checkConsulServiceNameBlacklistAttr = "service_blacklist"
|
||||
checkConsulStateAttr = "state"
|
||||
)
|
||||
|
||||
var checkConsulDescriptions = attrDescrs{
|
||||
checkConsulACLTokenAttr: "A Consul ACL token",
|
||||
checkConsulAllowStaleAttr: "Allow Consul to read from a non-leader system",
|
||||
checkConsulCAChainAttr: "A path to a file containing all the certificate authorities that should be loaded to validate the remote certificate (for TLS checks)",
|
||||
checkConsulCertFileAttr: "A path to a file containing the client certificate that will be presented to the remote server (for TLS-enabled checks)",
|
||||
checkConsulCheckNameBlacklistAttr: "A blacklist of check names to exclude from metric results",
|
||||
checkConsulCiphersAttr: "A list of ciphers to be used in the TLS protocol (for HTTPS checks)",
|
||||
checkConsulDatacenterAttr: "The Consul datacenter to extract health information from",
|
||||
checkConsulHeadersAttr: "Map of HTTP Headers to send along with HTTP Requests",
|
||||
checkConsulHTTPAddrAttr: "The HTTP Address of a Consul agent to query",
|
||||
checkConsulKeyFileAttr: "A path to a file containing key to be used in conjunction with the cilent certificate (for TLS checks)",
|
||||
checkConsulNodeAttr: "Node Name or NodeID of a Consul agent",
|
||||
checkConsulNodeBlacklistAttr: "A blacklist of node names or IDs to exclude from metric results",
|
||||
checkConsulServiceAttr: "Name of the Consul service to check",
|
||||
checkConsulServiceNameBlacklistAttr: "A blacklist of service names to exclude from metric results",
|
||||
checkConsulStateAttr: "Check for Consul services in this particular state",
|
||||
}
|
||||
|
||||
var consulHealthCheckRE = regexp.MustCompile(fmt.Sprintf(`^%s/(%s|%s|%s)/(.+)`, checkConsulV1Prefix, checkConsulV1NodePrefix, checkConsulV1ServicePrefix, checkConsulV1StatePrefix))
|
||||
|
||||
var schemaCheckConsul = &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: convertToHelperSchema(checkConsulDescriptions, map[schemaAttr]*schema.Schema{
|
||||
checkConsulACLTokenAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulACLTokenAttr, `^[a-zA-Z0-9\-]+$`),
|
||||
},
|
||||
checkConsulAllowStaleAttr: &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: true,
|
||||
},
|
||||
checkConsulCAChainAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulCAChainAttr, `.+`),
|
||||
},
|
||||
checkConsulCertFileAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulCertFileAttr, `.+`),
|
||||
},
|
||||
checkConsulCheckNameBlacklistAttr: &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateRegexp(checkConsulCheckNameBlacklistAttr, `^[A-Za-z0-9_-]+$`),
|
||||
},
|
||||
},
|
||||
checkConsulCiphersAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulCiphersAttr, `.+`),
|
||||
},
|
||||
checkConsulDatacenterAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulCertFileAttr, `^[a-zA-Z0-9]+$`),
|
||||
},
|
||||
checkConsulHTTPAddrAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: defaultCheckConsulHTTPAddr,
|
||||
ValidateFunc: validateHTTPURL(checkConsulHTTPAddrAttr, urlIsAbs|urlWithoutPath),
|
||||
},
|
||||
checkConsulHeadersAttr: &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
Elem: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateHTTPHeaders,
|
||||
},
|
||||
checkConsulKeyFileAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulKeyFileAttr, `.+`),
|
||||
},
|
||||
checkConsulNodeAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulNodeAttr, `^[a-zA-Z0-9_\-]+$`),
|
||||
ConflictsWith: []string{
|
||||
checkConsulAttr + "." + checkConsulServiceAttr,
|
||||
checkConsulAttr + "." + checkConsulStateAttr,
|
||||
},
|
||||
},
|
||||
checkConsulNodeBlacklistAttr: &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateRegexp(checkConsulNodeBlacklistAttr, `^[A-Za-z0-9_-]+$`),
|
||||
},
|
||||
},
|
||||
checkConsulServiceAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulServiceAttr, `^[a-zA-Z0-9_\-]+$`),
|
||||
ConflictsWith: []string{
|
||||
checkConsulAttr + "." + checkConsulNodeAttr,
|
||||
checkConsulAttr + "." + checkConsulStateAttr,
|
||||
},
|
||||
},
|
||||
checkConsulServiceNameBlacklistAttr: &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Elem: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateRegexp(checkConsulServiceNameBlacklistAttr, `^[A-Za-z0-9_-]+$`),
|
||||
},
|
||||
},
|
||||
checkConsulStateAttr: &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ValidateFunc: validateRegexp(checkConsulStateAttr, `^(any|passing|warning|critical)$`),
|
||||
ConflictsWith: []string{
|
||||
checkConsulAttr + "." + checkConsulNodeAttr,
|
||||
checkConsulAttr + "." + checkConsulServiceAttr,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
// checkAPIToStateConsul reads the Config data out of circonusCheck.CheckBundle into
|
||||
// the statefile.
|
||||
func checkAPIToStateConsul(c *circonusCheck, d *schema.ResourceData) error {
|
||||
consulConfig := make(map[string]interface{}, len(c.Config))
|
||||
|
||||
// swamp is a sanity check: it must be empty by the time this method returns
|
||||
swamp := make(map[config.Key]string, len(c.Config))
|
||||
for k, s := range c.Config {
|
||||
swamp[k] = s
|
||||
}
|
||||
|
||||
saveStringConfigToState := func(apiKey config.Key, attrName schemaAttr) {
|
||||
if s, ok := c.Config[apiKey]; ok && s != "" {
|
||||
consulConfig[string(attrName)] = s
|
||||
}
|
||||
|
||||
delete(swamp, apiKey)
|
||||
}
|
||||
|
||||
saveStringConfigToState(config.CAChain, checkConsulCAChainAttr)
|
||||
saveStringConfigToState(config.CertFile, checkConsulCertFileAttr)
|
||||
saveStringConfigToState(config.Ciphers, checkConsulCiphersAttr)
|
||||
|
||||
// httpAddrURL is used to compose the http_addr value using multiple c.Config
|
||||
// values.
|
||||
var httpAddrURL url.URL
|
||||
|
||||
headers := make(map[string]interface{}, len(c.Config)+1) // +1 is for the ACLToken
|
||||
headerPrefixLen := len(config.HeaderPrefix)
|
||||
|
||||
// Explicitly handle several config parameters in sequence: URL, then port,
|
||||
// then everything else.
|
||||
if v, found := c.Config[config.URL]; found {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("unable to parse %q from config: {{err}}", config.URL), err)
|
||||
}
|
||||
|
||||
queryArgs := u.Query()
|
||||
if vals, found := queryArgs[apiConsulStaleAttr]; found && len(vals) > 0 {
|
||||
consulConfig[string(checkConsulAllowStaleAttr)] = true
|
||||
}
|
||||
|
||||
if dc := queryArgs.Get(apiConsulDatacenterAttr); dc != "" {
|
||||
consulConfig[string(checkConsulDatacenterAttr)] = dc
|
||||
}
|
||||
|
||||
httpAddrURL.Host = u.Host
|
||||
httpAddrURL.Scheme = u.Scheme
|
||||
|
||||
md := consulHealthCheckRE.FindStringSubmatch(u.EscapedPath())
|
||||
if md == nil {
|
||||
return fmt.Errorf("config %q failed to match the health regexp", config.URL)
|
||||
}
|
||||
|
||||
checkMode := md[1]
|
||||
checkArg := md[2]
|
||||
switch checkMode {
|
||||
case checkConsulV1NodePrefix:
|
||||
consulConfig[string(checkConsulNodeAttr)] = checkArg
|
||||
case checkConsulV1ServicePrefix:
|
||||
consulConfig[string(checkConsulServiceAttr)] = checkArg
|
||||
case checkConsulV1StatePrefix:
|
||||
consulConfig[string(checkConsulStateAttr)] = checkArg
|
||||
default:
|
||||
return fmt.Errorf("PROVIDER BUG: unsupported check mode %q from %q", checkMode, u.EscapedPath())
|
||||
}
|
||||
|
||||
delete(swamp, config.URL)
|
||||
}
|
||||
|
||||
if v, found := c.Config[config.Port]; found {
|
||||
hostInfo := strings.SplitN(httpAddrURL.Host, ":", 2)
|
||||
switch {
|
||||
case len(hostInfo) == 1 && v != defaultCheckConsulPort, len(hostInfo) > 1:
|
||||
httpAddrURL.Host = net.JoinHostPort(hostInfo[0], v)
|
||||
}
|
||||
|
||||
delete(swamp, config.Port)
|
||||
}
|
||||
|
||||
if v, found := c.Config[apiConsulCheckBlacklist]; found {
|
||||
consulConfig[checkConsulCheckNameBlacklistAttr] = strings.Split(v, ",")
|
||||
}
|
||||
|
||||
if v, found := c.Config[apiConsulNodeBlacklist]; found {
|
||||
consulConfig[checkConsulNodeBlacklistAttr] = strings.Split(v, ",")
|
||||
}
|
||||
|
||||
if v, found := c.Config[apiConsulServiceBlacklist]; found {
|
||||
consulConfig[checkConsulServiceNameBlacklistAttr] = strings.Split(v, ",")
|
||||
}
|
||||
|
||||
// NOTE(sean@): headers attribute processed last. See below.
|
||||
|
||||
consulConfig[string(checkConsulHTTPAddrAttr)] = httpAddrURL.String()
|
||||
|
||||
saveStringConfigToState(config.KeyFile, checkConsulKeyFileAttr)
|
||||
|
||||
// Process the headers last in order to provide an escape hatch capible of
|
||||
// overriding any other derived value above.
|
||||
for k, v := range c.Config {
|
||||
if len(k) <= headerPrefixLen {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle all of the prefix variable headers, like `header_`
|
||||
if strings.Compare(string(k[:headerPrefixLen]), string(config.HeaderPrefix)) == 0 {
|
||||
key := k[headerPrefixLen:]
|
||||
switch key {
|
||||
case checkConsulTokenHeader:
|
||||
consulConfig[checkConsulACLTokenAttr] = v
|
||||
default:
|
||||
headers[string(key)] = v
|
||||
}
|
||||
}
|
||||
|
||||
delete(swamp, k)
|
||||
}
|
||||
consulConfig[string(checkConsulHeadersAttr)] = headers
|
||||
|
||||
whitelistedConfigKeys := map[config.Key]struct{}{
|
||||
config.Port: struct{}{},
|
||||
config.ReverseSecretKey: struct{}{},
|
||||
config.SubmissionURL: struct{}{},
|
||||
config.URL: struct{}{},
|
||||
}
|
||||
|
||||
for k := range swamp {
|
||||
if _, ok := whitelistedConfigKeys[k]; ok {
|
||||
delete(c.Config, k)
|
||||
}
|
||||
|
||||
if _, ok := whitelistedConfigKeys[k]; !ok {
|
||||
return fmt.Errorf("PROVIDER BUG: API Config not empty: %#v", swamp)
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.Set(checkConsulAttr, []interface{}{consulConfig}); err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Unable to store check %q attribute: {{err}}", checkConsulAttr), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkConfigToAPIConsul(c *circonusCheck, l interfaceList) error {
|
||||
c.Type = string(apiCheckTypeConsul)
|
||||
|
||||
// Iterate over all `consul` attributes, even though we have a max of 1 in the
|
||||
// schema.
|
||||
for _, mapRaw := range l {
|
||||
consulConfig := newInterfaceMap(mapRaw)
|
||||
if v, found := consulConfig[checkConsulCAChainAttr]; found {
|
||||
c.Config[config.CAChain] = v.(string)
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulCertFileAttr]; found {
|
||||
c.Config[config.CertFile] = v.(string)
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulCheckNameBlacklistAttr]; found {
|
||||
listRaw := v.([]interface{})
|
||||
checks := make([]string, 0, len(listRaw))
|
||||
for _, v := range listRaw {
|
||||
checks = append(checks, v.(string))
|
||||
}
|
||||
c.Config[apiConsulCheckBlacklist] = strings.Join(checks, ",")
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulCiphersAttr]; found {
|
||||
c.Config[config.Ciphers] = v.(string)
|
||||
}
|
||||
|
||||
if headers := consulConfig.CollectMap(checkConsulHeadersAttr); headers != nil {
|
||||
for k, v := range headers {
|
||||
h := config.HeaderPrefix + config.Key(k)
|
||||
c.Config[h] = v
|
||||
}
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulKeyFileAttr]; found {
|
||||
c.Config[config.KeyFile] = v.(string)
|
||||
}
|
||||
|
||||
{
|
||||
// Extract all of the input attributes necessary to construct the
|
||||
// Consul agent's URL.
|
||||
|
||||
httpAddr := consulConfig[checkConsulHTTPAddrAttr].(string)
|
||||
checkURL, err := url.Parse(httpAddr)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf(fmt.Sprintf("Unable to parse %s's attribute %q: {{err}}", checkConsulAttr, httpAddr), err)
|
||||
}
|
||||
|
||||
hostInfo := strings.SplitN(checkURL.Host, ":", 2)
|
||||
if len(c.Target) == 0 {
|
||||
c.Target = hostInfo[0]
|
||||
}
|
||||
|
||||
if len(hostInfo) > 1 {
|
||||
c.Config[config.Port] = hostInfo[1]
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulNodeAttr]; found && v.(string) != "" {
|
||||
checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1NodePrefix, v.(string)}, "/")
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulServiceAttr]; found && v.(string) != "" {
|
||||
checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1ServicePrefix, v.(string)}, "/")
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulStateAttr]; found && v.(string) != "" {
|
||||
checkURL.Path = strings.Join([]string{checkConsulV1Prefix, checkConsulV1StatePrefix, v.(string)}, "/")
|
||||
}
|
||||
|
||||
q := checkURL.Query()
|
||||
|
||||
if v, found := consulConfig[checkConsulAllowStaleAttr]; found && v.(bool) {
|
||||
q.Set(apiConsulStaleAttr, "")
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulDatacenterAttr]; found && v.(string) != "" {
|
||||
q.Set(apiConsulDatacenterAttr, v.(string))
|
||||
}
|
||||
|
||||
checkURL.RawQuery = q.Encode()
|
||||
|
||||
c.Config[config.URL] = checkURL.String()
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulNodeBlacklistAttr]; found {
|
||||
listRaw := v.([]interface{})
|
||||
checks := make([]string, 0, len(listRaw))
|
||||
for _, v := range listRaw {
|
||||
checks = append(checks, v.(string))
|
||||
}
|
||||
c.Config[apiConsulNodeBlacklist] = strings.Join(checks, ",")
|
||||
}
|
||||
|
||||
if v, found := consulConfig[checkConsulServiceNameBlacklistAttr]; found {
|
||||
listRaw := v.([]interface{})
|
||||
checks := make([]string, 0, len(listRaw))
|
||||
for _, v := range listRaw {
|
||||
checks = append(checks, v.(string))
|
||||
}
|
||||
c.Config[apiConsulServiceBlacklist] = strings.Join(checks, ",")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
package circonus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/circonus-labs/circonus-gometrics/api/config"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccCirconusCheckConsul_node(t *testing.T) {
|
||||
checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=state check - %s", acctest.RandString(5))
|
||||
|
||||
checkNode := fmt.Sprintf("my-node-name-or-node-id-%s", acctest.RandString(5))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDestroyCirconusCheckBundle,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthNodeFmt, checkName, checkNode),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"),
|
||||
resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.dc", "dc2"),
|
||||
resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul:8501"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node", checkNode),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.#", "3"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.0", "a"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.1", "bad"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.node_blacklist.2", "node"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccCirconusCheckConsul_service(t *testing.T) {
|
||||
checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=service check - %s", acctest.RandString(5))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDestroyCirconusCheckBundle,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthServiceFmt, checkName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"),
|
||||
resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""),
|
||||
resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service", "consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.#", "3"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.0", "bad"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.1", "hombre"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.service_blacklist.2", "service"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "name", checkName),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccCirconusCheckConsul_state(t *testing.T) {
|
||||
checkName := fmt.Sprintf("Terraform test: consul.service.consul mode=state check - %s", acctest.RandString(5))
|
||||
|
||||
checkState := "critical"
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDestroyCirconusCheckBundle,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: fmt.Sprintf(testAccCirconusCheckConsulConfigV1HealthStateFmt, checkName, checkState),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "active", "true"),
|
||||
resource.TestMatchResourceAttr("circonus_check.consul_server", "check_id", regexp.MustCompile(config.CheckCIDRegex)),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.#", "1"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "collector.2084916526.id", "/broker/2110"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.#", "1"),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ca_chain", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.certificate_file", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.ciphers", ""),
|
||||
// resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.key_file", ""),
|
||||
resource.TestCheckNoResourceAttr("circonus_check.consul_server", "consul.0.headers"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.http_addr", "http://consul.service.consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.state", checkState),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.0", "worthless"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "consul.0.check_blacklist.1", "check"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "name", checkName),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "notes", ""),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "period", "60s"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.name", "KnownLeader"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3333874791.type", "text"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.active", "true"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.name", "LastContact"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.type", "numeric"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "metric.3148913305.unit", "seconds"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.#", "2"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.1401442048", "lifecycle:unittest"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "tags.2058715988", "source:consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "target", "consul.service.consul"),
|
||||
resource.TestCheckResourceAttr("circonus_check.consul_server", "type", "consul"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const testAccCirconusCheckConsulConfigV1HealthNodeFmt = `
|
||||
resource "circonus_check" "consul_server" {
|
||||
active = true
|
||||
name = "%s"
|
||||
period = "60s"
|
||||
|
||||
collector {
|
||||
id = "/broker/2110"
|
||||
}
|
||||
|
||||
consul {
|
||||
dc = "dc2"
|
||||
http_addr = "http://consul.service.consul:8501"
|
||||
node = "%s"
|
||||
node_blacklist = ["a","bad","node"]
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "LastContact"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "numeric"
|
||||
unit = "seconds"
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "KnownLeader"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "text"
|
||||
}
|
||||
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
|
||||
target = "consul.service.consul"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCirconusCheckConsulConfigV1HealthServiceFmt = `
|
||||
resource "circonus_check" "consul_server" {
|
||||
active = true
|
||||
name = "%s"
|
||||
period = "60s"
|
||||
|
||||
collector {
|
||||
id = "/broker/2110"
|
||||
}
|
||||
|
||||
consul {
|
||||
service = "consul"
|
||||
service_blacklist = ["bad","hombre","service"]
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "LastContact"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "numeric"
|
||||
unit = "seconds"
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "KnownLeader"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "text"
|
||||
}
|
||||
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
|
||||
target = "consul.service.consul"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCirconusCheckConsulConfigV1HealthStateFmt = `
|
||||
resource "circonus_check" "consul_server" {
|
||||
active = true
|
||||
name = "%s"
|
||||
period = "60s"
|
||||
|
||||
collector {
|
||||
id = "/broker/2110"
|
||||
}
|
||||
|
||||
consul {
|
||||
state = "%s"
|
||||
check_blacklist = ["worthless","check"]
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "LastContact"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "numeric"
|
||||
unit = "seconds"
|
||||
}
|
||||
|
||||
metric {
|
||||
name = "KnownLeader"
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
type = "text"
|
||||
}
|
||||
|
||||
tags = [ "source:consul", "lifecycle:unittest" ]
|
||||
|
||||
target = "consul.service.consul"
|
||||
}
|
||||
`
|
|
@ -372,6 +372,10 @@ func checkConfigToAPIHTTP(c *circonusCheck, l interfaceList) error {
|
|||
if len(c.Target) == 0 {
|
||||
c.Target = hostInfo[0]
|
||||
}
|
||||
|
||||
if len(hostInfo) > 1 && c.Config[config.Port] == "" {
|
||||
c.Config[config.Port] = hostInfo[1]
|
||||
}
|
||||
}
|
||||
|
||||
if v, found := httpConfig[checkHTTPVersionAttr]; found {
|
||||
|
|
|
@ -355,6 +355,10 @@ func checkConfigToAPIJSON(c *circonusCheck, l interfaceList) error {
|
|||
if len(c.Target) == 0 {
|
||||
c.Target = hostInfo[0]
|
||||
}
|
||||
|
||||
if len(hostInfo) > 1 && c.Config[config.Port] == "" {
|
||||
c.Config[config.Port] = hostInfo[1]
|
||||
}
|
||||
}
|
||||
|
||||
if v, found := jsonConfig[checkJSONVersionAttr]; found {
|
||||
|
|
|
@ -314,6 +314,7 @@ type urlParseFlags int
|
|||
const (
|
||||
urlIsAbs urlParseFlags = 1 << iota
|
||||
urlOptional
|
||||
urlWithoutPath
|
||||
urlWithoutPort
|
||||
urlWithoutSchema
|
||||
)
|
||||
|
@ -345,6 +346,10 @@ func validateHTTPURL(attrName schemaAttr, checkFlags urlParseFlags) func(v inter
|
|||
errors = append(errors, fmt.Errorf("Schema is present on URL %q (HINT: drop the https://%s)", v.(string), v.(string)))
|
||||
}
|
||||
|
||||
if checkFlags&urlWithoutPath != 0 && u.Path != "" {
|
||||
errors = append(errors, fmt.Errorf("Path is present on URL %q (HINT: drop the %s)", v.(string), u.Path))
|
||||
}
|
||||
|
||||
if checkFlags&urlWithoutPort != 0 {
|
||||
hostParts := strings.SplitN(u.Host, ":", 2)
|
||||
if len(hostParts) != 1 {
|
||||
|
|
|
@ -52,7 +52,7 @@ func (c *Config) Client() (*consulapi.Client, error) {
|
|||
} else {
|
||||
username = c.HttpAuth
|
||||
}
|
||||
config.HttpAuth = &consulapi.HttpBasicAuth{username, password}
|
||||
config.HttpAuth = &consulapi.HttpBasicAuth{Username: username, Password: password}
|
||||
}
|
||||
|
||||
if c.Token != "" {
|
||||
|
|
|
@ -3,11 +3,17 @@ package digitalocean
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) {
|
||||
resourceName := "digitalocean_ssh_key.foobar"
|
||||
rInt := acctest.RandInt()
|
||||
publicKeyMaterial, _, err := acctest.RandSSHKeyPair("digitalocean@ssh-acceptance-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot generate test SSH key pair: %s", err)
|
||||
}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -15,7 +21,7 @@ func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(testAccValidImportPublicKey),
|
||||
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(rInt, publicKeyMaterial),
|
||||
},
|
||||
|
||||
{
|
||||
|
|
|
@ -322,8 +322,9 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{})
|
|||
return fmt.Errorf("invalid droplet id: %v", err)
|
||||
}
|
||||
|
||||
if d.HasChange("size") {
|
||||
oldSize, newSize := d.GetChange("size")
|
||||
resize_disk := d.Get("resize_disk").(bool)
|
||||
if d.HasChange("size") || d.HasChange("resize_disk") && resize_disk {
|
||||
newSize := d.Get("size")
|
||||
|
||||
_, _, err = client.DropletActions.PowerOff(id)
|
||||
if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") {
|
||||
|
@ -339,13 +340,7 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{})
|
|||
}
|
||||
|
||||
// Resize the droplet
|
||||
resize_disk := d.Get("resize_disk")
|
||||
switch {
|
||||
case resize_disk == true:
|
||||
_, _, err = client.DropletActions.Resize(id, newSize.(string), true)
|
||||
case resize_disk == false:
|
||||
_, _, err = client.DropletActions.Resize(id, newSize.(string), false)
|
||||
}
|
||||
action, _, err := client.DropletActions.Resize(id, newSize.(string), resize_disk)
|
||||
if err != nil {
|
||||
newErr := powerOnAndWait(d, meta)
|
||||
if newErr != nil {
|
||||
|
@ -356,11 +351,8 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{})
|
|||
"Error resizing droplet (%s): %s", d.Id(), err)
|
||||
}
|
||||
|
||||
// Wait for the size to change
|
||||
_, err = WaitForDropletAttribute(
|
||||
d, newSize.(string), []string{"", oldSize.(string)}, "size", meta)
|
||||
|
||||
if err != nil {
|
||||
// Wait for the resize action to complete.
|
||||
if err := waitForAction(client, action); err != nil {
|
||||
newErr := powerOnAndWait(d, meta)
|
||||
if newErr != nil {
|
||||
return fmt.Errorf(
|
||||
|
|
|
@ -144,6 +144,56 @@ func TestAccDigitalOceanDroplet_ResizeWithOutDisk(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanDroplet_ResizeOnlyDisk(t *testing.T) {
|
||||
var droplet godo.Droplet
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDigitalOceanDropletDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckDigitalOceanDropletConfig_basic(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet),
|
||||
testAccCheckDigitalOceanDropletAttributes(&droplet),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet),
|
||||
testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "size", "1gb"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "disk", "20"),
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet),
|
||||
testAccCheckDigitalOceanDropletResizeOnlyDisk(&droplet),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "size", "1gb"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_droplet.foobar", "disk", "30"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccDigitalOceanDroplet_UpdateUserData(t *testing.T) {
|
||||
var afterCreate, afterUpdate godo.Droplet
|
||||
rInt := acctest.RandInt()
|
||||
|
@ -321,6 +371,21 @@ func testAccCheckDigitalOceanDropletResizeWithOutDisk(droplet *godo.Droplet) res
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanDropletResizeOnlyDisk(droplet *godo.Droplet) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
if droplet.Size.Slug != "1gb" {
|
||||
return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug)
|
||||
}
|
||||
|
||||
if droplet.Disk != 30 {
|
||||
return fmt.Errorf("Bad disk: %d", droplet.Disk)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(droplet *godo.Droplet) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
|
@ -492,6 +557,19 @@ resource "digitalocean_droplet" "foobar" {
|
|||
`, rInt)
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "digitalocean_droplet" "foobar" {
|
||||
name = "foo-%d"
|
||||
size = "1gb"
|
||||
image = "centos-7-x64"
|
||||
region = "nyc3"
|
||||
user_data = "foobar"
|
||||
resize_disk = true
|
||||
}
|
||||
`, rInt)
|
||||
}
|
||||
|
||||
// IPV6 only in singapore
|
||||
func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
|
@ -505,3 +583,5 @@ resource "digitalocean_droplet" "foobar" {
|
|||
}
|
||||
`, rInt)
|
||||
}
|
||||
|
||||
var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR`
|
||||
|
|
|
@ -3,16 +3,21 @@ package digitalocean
|
|||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDigitalOceanSSHKey_Basic(t *testing.T) {
|
||||
var key godo.Key
|
||||
rInt := acctest.RandInt()
|
||||
publicKeyMaterial, _, err := acctest.RandSSHKeyPair("digitalocean@ssh-acceptance-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot generate test SSH key pair: %s", err)
|
||||
}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
|
@ -20,14 +25,13 @@ func TestAccDigitalOceanSSHKey_Basic(t *testing.T) {
|
|||
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(testAccValidPublicKey),
|
||||
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(rInt, publicKeyMaterial),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDigitalOceanSSHKeyExists("digitalocean_ssh_key.foobar", &key),
|
||||
testAccCheckDigitalOceanSSHKeyAttributes(&key),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_ssh_key.foobar", "name", "foobar"),
|
||||
"digitalocean_ssh_key.foobar", "name", fmt.Sprintf("foobar-%d", rInt)),
|
||||
resource.TestCheckResourceAttr(
|
||||
"digitalocean_ssh_key.foobar", "public_key", strings.TrimSpace(testAccValidPublicKey)),
|
||||
"digitalocean_ssh_key.foobar", "public_key", publicKeyMaterial),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -58,17 +62,6 @@ func testAccCheckDigitalOceanSSHKeyDestroy(s *terraform.State) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanSSHKeyAttributes(key *godo.Key) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
if key.Name != "foobar" {
|
||||
return fmt.Errorf("Bad name: %s", key.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanSSHKeyExists(n string, key *godo.Key) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -105,13 +98,10 @@ func testAccCheckDigitalOceanSSHKeyExists(n string, key *godo.Key) resource.Test
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckDigitalOceanSSHKeyConfig_basic(key string) string {
|
||||
func testAccCheckDigitalOceanSSHKeyConfig_basic(rInt int, key string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "digitalocean_ssh_key" "foobar" {
|
||||
name = "foobar"
|
||||
name = "foobar-%d"
|
||||
public_key = "%s"
|
||||
}`, key)
|
||||
}`, rInt, key)
|
||||
}
|
||||
|
||||
var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR`
|
||||
var testAccValidImportPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwelf/LV8TKOd6ZCcDwU9L8YRdVwfR2q8E+Bzamcxwb1U41vnfyvEZbzx0aeXimdHipOql0SG2tu9Z+bzekROVc13OP/gtGRlWwZ9RoKE8hFHanhi0K2tC6OWagsvmHpW/xptsYAo2k+eRJJo0iy/hLNG2c1v5rrjg6xwnSL3+a7bFM4xNDux5sNYCmxIBfIL+4rQ8XBlxsjMrGoev/uumZ0yc75JtBCOSZbdie936pvVmoAf4nhxNbe5lOxp+18zHhBbO2fjhux4xmf4hLM2gHsdBGqtnphzLh3d1+uMIpv7ZMTKN7pBw53xQxw7hhDYuNKc8FkQ8xK6IL5bu/Ar/`
|
||||
|
|
|
@ -1922,7 +1922,7 @@ func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} {
|
|||
nb := map[string]interface{}{
|
||||
"name": b.Name,
|
||||
"address": b.Address,
|
||||
"auto_loadbalance": gofastly.CBool(b.AutoLoadbalance),
|
||||
"auto_loadbalance": b.AutoLoadbalance,
|
||||
"between_bytes_timeout": int(b.BetweenBytesTimeout),
|
||||
"connect_timeout": int(b.ConnectTimeout),
|
||||
"error_threshold": int(b.ErrorThreshold),
|
||||
|
@ -1930,7 +1930,7 @@ func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} {
|
|||
"max_conn": int(b.MaxConn),
|
||||
"port": int(b.Port),
|
||||
"shield": b.Shield,
|
||||
"ssl_check_cert": gofastly.CBool(b.SSLCheckCert),
|
||||
"ssl_check_cert": b.SSLCheckCert,
|
||||
"ssl_hostname": b.SSLHostname,
|
||||
"ssl_cert_hostname": b.SSLCertHostname,
|
||||
"ssl_sni_hostname": b.SSLSNIHostname,
|
||||
|
|
|
@ -84,14 +84,14 @@ func TestResourceFastlyFlattenBackend(t *testing.T) {
|
|||
"name": "test.notexample.com",
|
||||
"address": "www.notexample.com",
|
||||
"port": 80,
|
||||
"auto_loadbalance": gofastly.CBool(true),
|
||||
"auto_loadbalance": true,
|
||||
"between_bytes_timeout": 10000,
|
||||
"connect_timeout": 1000,
|
||||
"error_threshold": 0,
|
||||
"first_byte_timeout": 15000,
|
||||
"max_conn": 200,
|
||||
"request_condition": "",
|
||||
"ssl_check_cert": gofastly.CBool(true),
|
||||
"ssl_check_cert": true,
|
||||
"ssl_hostname": "",
|
||||
"ssl_cert_hostname": "",
|
||||
"ssl_sni_hostname": "",
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func diffStringMap(pathPrefix string, oldV, newV map[string]interface{}) PatchOperations {
|
||||
ops := make([]PatchOperation, 0, 0)
|
||||
|
||||
pathPrefix = strings.TrimRight(pathPrefix, "/")
|
||||
|
||||
// This is suboptimal for adding whole new map from scratch
|
||||
// or deleting the whole map, but it's actually intention.
|
||||
// There may be some other map items managed outside of TF
|
||||
// and we don't want to touch these.
|
||||
|
||||
for k, _ := range oldV {
|
||||
if _, ok := newV[k]; ok {
|
||||
continue
|
||||
}
|
||||
ops = append(ops, &RemoveOperation{Path: pathPrefix + "/" + k})
|
||||
}
|
||||
|
||||
for k, v := range newV {
|
||||
newValue := v.(string)
|
||||
|
||||
if oldValue, ok := oldV[k].(string); ok {
|
||||
if oldValue == newValue {
|
||||
continue
|
||||
}
|
||||
|
||||
ops = append(ops, &ReplaceOperation{
|
||||
Path: pathPrefix + "/" + k,
|
||||
Value: newValue,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
ops = append(ops, &AddOperation{
|
||||
Path: pathPrefix + "/" + k,
|
||||
Value: newValue,
|
||||
})
|
||||
}
|
||||
|
||||
return ops
|
||||
}
|
||||
|
||||
type PatchOperations []PatchOperation
|
||||
|
||||
func (po PatchOperations) MarshalJSON() ([]byte, error) {
|
||||
var v []PatchOperation = po
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (po PatchOperations) Equal(ops []PatchOperation) bool {
|
||||
var v []PatchOperation = po
|
||||
|
||||
sort.Slice(v, sortByPathAsc(v))
|
||||
sort.Slice(ops, sortByPathAsc(ops))
|
||||
|
||||
return reflect.DeepEqual(v, ops)
|
||||
}
|
||||
|
||||
func sortByPathAsc(ops []PatchOperation) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
return ops[i].GetPath() < ops[j].GetPath()
|
||||
}
|
||||
}
|
||||
|
||||
type PatchOperation interface {
|
||||
MarshalJSON() ([]byte, error)
|
||||
GetPath() string
|
||||
}
|
||||
|
||||
type ReplaceOperation struct {
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value"`
|
||||
Op string `json:"op"`
|
||||
}
|
||||
|
||||
func (o *ReplaceOperation) GetPath() string {
|
||||
return o.Path
|
||||
}
|
||||
|
||||
func (o *ReplaceOperation) MarshalJSON() ([]byte, error) {
|
||||
o.Op = "replace"
|
||||
return json.Marshal(*o)
|
||||
}
|
||||
|
||||
func (o *ReplaceOperation) String() string {
|
||||
b, _ := o.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
type AddOperation struct {
|
||||
Path string `json:"path"`
|
||||
Value interface{} `json:"value"`
|
||||
Op string `json:"op"`
|
||||
}
|
||||
|
||||
func (o *AddOperation) GetPath() string {
|
||||
return o.Path
|
||||
}
|
||||
|
||||
func (o *AddOperation) MarshalJSON() ([]byte, error) {
|
||||
o.Op = "add"
|
||||
return json.Marshal(*o)
|
||||
}
|
||||
|
||||
func (o *AddOperation) String() string {
|
||||
b, _ := o.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
||||
|
||||
type RemoveOperation struct {
|
||||
Path string `json:"path"`
|
||||
Op string `json:"op"`
|
||||
}
|
||||
|
||||
func (o *RemoveOperation) GetPath() string {
|
||||
return o.Path
|
||||
}
|
||||
|
||||
func (o *RemoveOperation) MarshalJSON() ([]byte, error) {
|
||||
o.Op = "remove"
|
||||
return json.Marshal(*o)
|
||||
}
|
||||
|
||||
func (o *RemoveOperation) String() string {
|
||||
b, _ := o.MarshalJSON()
|
||||
return string(b)
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDiffStringMap(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Path string
|
||||
Old map[string]interface{}
|
||||
New map[string]interface{}
|
||||
ExpectedOps PatchOperations
|
||||
}{
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
New: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
"three": "333",
|
||||
},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&AddOperation{
|
||||
Path: "/parent/three",
|
||||
Value: "333",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
New: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "abcd",
|
||||
},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&ReplaceOperation{
|
||||
Path: "/parent/two",
|
||||
Value: "abcd",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
New: map[string]interface{}{
|
||||
"two": "abcd",
|
||||
"three": "333",
|
||||
},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&RemoveOperation{Path: "/parent/one"},
|
||||
&ReplaceOperation{
|
||||
Path: "/parent/two",
|
||||
Value: "abcd",
|
||||
},
|
||||
&AddOperation{
|
||||
Path: "/parent/three",
|
||||
Value: "333",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
New: map[string]interface{}{
|
||||
"two": "222",
|
||||
},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&RemoveOperation{Path: "/parent/one"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
New: map[string]interface{}{},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&RemoveOperation{Path: "/parent/one"},
|
||||
&RemoveOperation{Path: "/parent/two"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "/parent/",
|
||||
Old: map[string]interface{}{},
|
||||
New: map[string]interface{}{
|
||||
"one": "111",
|
||||
"two": "222",
|
||||
},
|
||||
ExpectedOps: []PatchOperation{
|
||||
&AddOperation{
|
||||
Path: "/parent/one",
|
||||
Value: "111",
|
||||
},
|
||||
&AddOperation{
|
||||
Path: "/parent/two",
|
||||
Value: "222",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
ops := diffStringMap(tc.Path, tc.Old, tc.New)
|
||||
if !tc.ExpectedOps.Equal(ops) {
|
||||
t.Fatalf("Operations don't match.\nExpected: %v\nGiven: %v\n", tc.ExpectedOps, ops)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
pkgApi "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
api "k8s.io/kubernetes/pkg/api/v1"
|
||||
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
||||
|
@ -73,19 +75,22 @@ func resourceKubernetesConfigMapRead(d *schema.ResourceData, meta interface{}) e
|
|||
func resourceKubernetesConfigMapUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*kubernetes.Clientset)
|
||||
|
||||
metadata := expandMetadata(d.Get("metadata").([]interface{}))
|
||||
namespace, name := idParts(d.Id())
|
||||
// This is necessary in case the name is generated
|
||||
metadata.Name = name
|
||||
|
||||
cfgMap := api.ConfigMap{
|
||||
ObjectMeta: metadata,
|
||||
Data: expandStringMap(d.Get("data").(map[string]interface{})),
|
||||
ops := patchMetadata("metadata.0.", "/metadata/", d)
|
||||
if d.HasChange("data") {
|
||||
oldV, newV := d.GetChange("data")
|
||||
diffOps := diffStringMap("/data/", oldV.(map[string]interface{}), newV.(map[string]interface{}))
|
||||
ops = append(ops, diffOps...)
|
||||
}
|
||||
log.Printf("[INFO] Updating config map: %#v", cfgMap)
|
||||
out, err := conn.CoreV1().ConfigMaps(namespace).Update(&cfgMap)
|
||||
data, err := ops.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("Failed to marshal update operations: %s", err)
|
||||
}
|
||||
log.Printf("[INFO] Updating config map %q: %v", name, string(data))
|
||||
out, err := conn.CoreV1().ConfigMaps(namespace).Patch(name, pkgApi.JSONPatchType, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update Config Map: %s", err)
|
||||
}
|
||||
log.Printf("[INFO] Submitted updated config map: %#v", out)
|
||||
d.SetId(buildId(out.ObjectMeta))
|
||||
|
|
|
@ -2,8 +2,10 @@ package kubernetes
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
api "k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
|
@ -39,6 +41,21 @@ func expandMetadata(in []interface{}) api.ObjectMeta {
|
|||
return meta
|
||||
}
|
||||
|
||||
func patchMetadata(keyPrefix, pathPrefix string, d *schema.ResourceData) PatchOperations {
|
||||
ops := make([]PatchOperation, 0, 0)
|
||||
if d.HasChange(keyPrefix + "annotations") {
|
||||
oldV, newV := d.GetChange(keyPrefix + "annotations")
|
||||
diffOps := diffStringMap(pathPrefix+"annotations", oldV.(map[string]interface{}), newV.(map[string]interface{}))
|
||||
ops = append(ops, diffOps...)
|
||||
}
|
||||
if d.HasChange(keyPrefix + "labels") {
|
||||
oldV, newV := d.GetChange(keyPrefix + "labels")
|
||||
diffOps := diffStringMap(pathPrefix+"labels", oldV.(map[string]interface{}), newV.(map[string]interface{}))
|
||||
ops = append(ops, diffOps...)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
|
||||
func expandStringMap(m map[string]interface{}) map[string]string {
|
||||
result := make(map[string]string)
|
||||
for k, v := range m {
|
||||
|
@ -49,7 +66,7 @@ func expandStringMap(m map[string]interface{}) map[string]string {
|
|||
|
||||
func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
m["annotations"] = meta.Annotations
|
||||
m["annotations"] = filterAnnotations(meta.Annotations)
|
||||
m["generate_name"] = meta.GenerateName
|
||||
m["labels"] = meta.Labels
|
||||
m["name"] = meta.Name
|
||||
|
@ -64,3 +81,21 @@ func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} {
|
|||
|
||||
return []map[string]interface{}{m}
|
||||
}
|
||||
|
||||
func filterAnnotations(m map[string]string) map[string]string {
|
||||
for k, _ := range m {
|
||||
if isInternalAnnotationKey(k) {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func isInternalAnnotationKey(annotationKey string) bool {
|
||||
u, err := url.Parse("//" + annotationKey)
|
||||
if err == nil && strings.HasSuffix(u.Hostname(), "kubernetes.io") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package kubernetes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsInternalAnnotationKey(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Key string
|
||||
Expected bool
|
||||
}{
|
||||
{"", false},
|
||||
{"anyKey", false},
|
||||
{"any.hostname.io", false},
|
||||
{"any.hostname.com/with/path", false},
|
||||
{"any.kubernetes.io", true},
|
||||
{"kubernetes.io", true},
|
||||
{"pv.kubernetes.io/any/path", true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
isInternal := isInternalAnnotationKey(tc.Key)
|
||||
if tc.Expected && isInternal != tc.Expected {
|
||||
t.Fatalf("Expected %q to be internal", tc.Key)
|
||||
}
|
||||
if !tc.Expected && isInternal != tc.Expected {
|
||||
t.Fatalf("Expected %q not to be internal", tc.Key)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -79,9 +79,10 @@ func resourceComputeInstanceV2() *schema.Resource {
|
|||
DefaultFunc: schema.EnvDefaultFunc("OS_FLAVOR_NAME", nil),
|
||||
},
|
||||
"floating_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead",
|
||||
},
|
||||
"user_data": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
|
@ -150,9 +151,10 @@ func resourceComputeInstanceV2() *schema.Resource {
|
|||
Computed: true,
|
||||
},
|
||||
"floating_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead",
|
||||
},
|
||||
"mac": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
|
@ -243,8 +245,9 @@ func resourceComputeInstanceV2() *schema.Resource {
|
|||
},
|
||||
},
|
||||
"volume": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Deprecated: "Use block_device or openstack_compute_volume_attach_v2 instead",
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"id": &schema.Schema{
|
||||
|
@ -335,6 +338,10 @@ func resourceComputeInstanceV2() *schema.Resource {
|
|||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"all_metadata": &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -554,7 +561,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err
|
|||
})
|
||||
}
|
||||
|
||||
d.Set("metadata", server.Metadata)
|
||||
d.Set("all_metadata", server.Metadata)
|
||||
|
||||
secGrpNames := []string{}
|
||||
for _, sg := range server.SecurityGroups {
|
||||
|
|
|
@ -29,6 +29,8 @@ func TestAccComputeV2Instance_basic(t *testing.T) {
|
|||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance),
|
||||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "availability_zone", "nova"),
|
||||
),
|
||||
|
@ -607,6 +609,10 @@ func TestAccComputeV2Instance_metadataRemove(t *testing.T) {
|
|||
testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance),
|
||||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||
testAccCheckComputeV2InstanceMetadata(&instance, "abc", "def"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "all_metadata.abc", "def"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
|
@ -616,6 +622,10 @@ func TestAccComputeV2Instance_metadataRemove(t *testing.T) {
|
|||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||
testAccCheckComputeV2InstanceMetadata(&instance, "ghi", "jkl"),
|
||||
testAccCheckComputeV2InstanceNoMetadataKey(&instance, "abc"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"openstack_compute_instance_v2.instance_1", "all_metadata.ghi", "jkl"),
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -240,7 +240,7 @@ func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
if d.HasChange("monitor_ids") {
|
||||
oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups")
|
||||
oldMIDsRaw, newMIDsRaw := d.GetChange("monitor_ids")
|
||||
oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set)
|
||||
monitorsToAdd := newMIDsSet.Difference(oldMIDsSet)
|
||||
monitorsToRemove := oldMIDsSet.Difference(newMIDsSet)
|
||||
|
|
|
@ -104,6 +104,42 @@ func TestAccLBV1Pool_timeout(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccLBV1Pool_updateMonitor(t *testing.T) {
|
||||
var monitor_1 monitors.Monitor
|
||||
var monitor_2 monitors.Monitor
|
||||
var network networks.Network
|
||||
var pool pools.Pool
|
||||
var subnet subnets.Subnet
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckLBV1PoolDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccLBV1Pool_updateMonitor_1,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
|
||||
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
|
||||
testAccCheckLBV1PoolExists("openstack_lb_pool_v1.pool_1", &pool),
|
||||
testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_1", &monitor_1),
|
||||
testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_2", &monitor_2),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccLBV1Pool_updateMonitor_2,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckNetworkingV2NetworkExists("openstack_networking_network_v2.network_1", &network),
|
||||
testAccCheckNetworkingV2SubnetExists("openstack_networking_subnet_v2.subnet_1", &subnet),
|
||||
testAccCheckLBV1PoolExists("openstack_lb_pool_v1.pool_1", &pool),
|
||||
testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_1", &monitor_1),
|
||||
testAccCheckLBV1MonitorExists("openstack_lb_monitor_v1.monitor_2", &monitor_2),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckLBV1PoolDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
|
||||
|
@ -402,3 +438,77 @@ resource "openstack_lb_pool_v1" "pool_1" {
|
|||
}
|
||||
}
|
||||
`
|
||||
|
||||
const testAccLBV1Pool_updateMonitor_1 = `
|
||||
resource "openstack_networking_network_v2" "network_1" {
|
||||
name = "network_1"
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_networking_subnet_v2" "subnet_1" {
|
||||
cidr = "192.168.199.0/24"
|
||||
ip_version = 4
|
||||
network_id = "${openstack_networking_network_v2.network_1.id}"
|
||||
}
|
||||
|
||||
resource "openstack_lb_monitor_v1" "monitor_1" {
|
||||
type = "TCP"
|
||||
delay = 30
|
||||
timeout = 5
|
||||
max_retries = 3
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_lb_monitor_v1" "monitor_2" {
|
||||
type = "TCP"
|
||||
delay = 30
|
||||
timeout = 5
|
||||
max_retries = 3
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_lb_pool_v1" "pool_1" {
|
||||
name = "pool_1"
|
||||
protocol = "TCP"
|
||||
lb_method = "ROUND_ROBIN"
|
||||
monitor_ids = ["${openstack_lb_monitor_v1.monitor_1.id}"]
|
||||
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccLBV1Pool_updateMonitor_2 = `
|
||||
resource "openstack_networking_network_v2" "network_1" {
|
||||
name = "network_1"
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_networking_subnet_v2" "subnet_1" {
|
||||
cidr = "192.168.199.0/24"
|
||||
ip_version = 4
|
||||
network_id = "${openstack_networking_network_v2.network_1.id}"
|
||||
}
|
||||
|
||||
resource "openstack_lb_monitor_v1" "monitor_1" {
|
||||
type = "TCP"
|
||||
delay = 30
|
||||
timeout = 5
|
||||
max_retries = 3
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_lb_monitor_v1" "monitor_2" {
|
||||
type = "TCP"
|
||||
delay = 30
|
||||
timeout = 5
|
||||
max_retries = 3
|
||||
admin_state_up = "true"
|
||||
}
|
||||
|
||||
resource "openstack_lb_pool_v1" "pool_1" {
|
||||
name = "pool_1"
|
||||
protocol = "TCP"
|
||||
lb_method = "ROUND_ROBIN"
|
||||
monitor_ids = ["${openstack_lb_monitor_v1.monitor_2.id}"]
|
||||
subnet_id = "${openstack_networking_subnet_v2.subnet_1.id}"
|
||||
}
|
||||
`
|
||||
|
|
|
@ -167,10 +167,33 @@ func resourcePoolV2Create(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
||||
pool, err := pools.Create(networkingClient, createOpts).Extract()
|
||||
|
||||
var pool *pools.Pool
|
||||
err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
|
||||
var err error
|
||||
log.Printf("[DEBUG] Attempting to create LBaaSV2 pool")
|
||||
pool, err = pools.Create(networkingClient, createOpts).Extract()
|
||||
if err != nil {
|
||||
switch errCode := err.(type) {
|
||||
case gophercloud.ErrDefault500:
|
||||
log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.")
|
||||
return resource.RetryableError(err)
|
||||
case gophercloud.ErrUnexpectedResponseCode:
|
||||
if errCode.Actual == 409 {
|
||||
log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.")
|
||||
return resource.RetryableError(err)
|
||||
}
|
||||
default:
|
||||
return resource.NonRetryableError(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("[INFO] pool ID: %s", pool.ID)
|
||||
|
||||
log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 pool (%s) to become available.", pool.ID)
|
||||
|
|
|
@ -85,10 +85,9 @@ func resourceNetworkingPortV2() *schema.Resource {
|
|||
Computed: true,
|
||||
},
|
||||
"fixed_ip": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
ForceNew: false,
|
||||
Computed: true,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"subnet_id": &schema.Schema{
|
||||
|
@ -98,7 +97,6 @@ func resourceNetworkingPortV2() *schema.Resource {
|
|||
"ip_address": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -128,6 +126,11 @@ func resourceNetworkingPortV2() *schema.Resource {
|
|||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"all_fixed_ips": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -202,15 +205,14 @@ func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) erro
|
|||
d.Set("security_group_ids", p.SecurityGroups)
|
||||
d.Set("device_id", p.DeviceID)
|
||||
|
||||
// Convert FixedIPs to list of map
|
||||
var ips []map[string]interface{}
|
||||
// Create a slice of all returned Fixed IPs.
|
||||
// This will be in the order returned by the API,
|
||||
// which is usually alpha-numeric.
|
||||
var ips []string
|
||||
for _, ipObject := range p.FixedIPs {
|
||||
ip := make(map[string]interface{})
|
||||
ip["subnet_id"] = ipObject.SubnetID
|
||||
ip["ip_address"] = ipObject.IPAddress
|
||||
ips = append(ips, ip)
|
||||
ips = append(ips, ipObject.IPAddress)
|
||||
}
|
||||
d.Set("fixed_ip", ips)
|
||||
d.Set("all_fixed_ips", ips)
|
||||
|
||||
// Convert AllowedAddressPairs to list of map
|
||||
var pairs []map[string]interface{}
|
||||
|
@ -309,7 +311,7 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
|
|||
}
|
||||
|
||||
func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
|
||||
rawIP := d.Get("fixed_ip").(*schema.Set).List()
|
||||
rawIP := d.Get("fixed_ip").([]interface{})
|
||||
|
||||
if len(rawIP) == 0 {
|
||||
return nil
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue