Merge branch 'master' into brandontosch/GH-11874
This commit is contained in:
commit
380f55b8a9
|
@ -4,8 +4,10 @@ language: go
|
||||||
go:
|
go:
|
||||||
- 1.8
|
- 1.8
|
||||||
|
|
||||||
|
# add TF_CONSUL_TEST=1 to run consul tests
|
||||||
|
# they were causing timouts in travis
|
||||||
env:
|
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
|
# Fetch consul for the backend and provider tests
|
||||||
before_install:
|
before_install:
|
||||||
|
|
132
CHANGELOG.md
132
CHANGELOG.md
|
@ -1,55 +1,95 @@
|
||||||
## 0.9.2 (unreleased)
|
## 0.9.3 (unreleased)
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
|
|
||||||
* core: fix `ignore_changes` causing fields to be removed during apply [GH-12897]
|
* config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080]
|
||||||
* core: add `-force-copy` option to `terraform init` to supress prompts for copying state [GH-12939]
|
* helper/resource: Allow unknown "pending" states [GH-13099]
|
||||||
* helper/acctest: Add NewSSHKeyPair function [GH-12894]
|
* provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547]
|
||||||
* provider/alicloud: simplify validators [GH-12982]
|
* provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125]
|
||||||
* provider/aws: Added support for EMR AutoScalingRole [GH-12823]
|
* provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125]
|
||||||
* provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources [GH-12629]
|
* provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130]
|
||||||
* 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]
|
## 0.9.2 (March 28, 2017)
|
||||||
* 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]
|
BACKWARDS IMCOMPATIBILITIES / NOTES:
|
||||||
* provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource [GH-11306]
|
|
||||||
* provider/consul: Add support for basic auth to the provider [GH-12679]
|
* 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.
|
||||||
* provider/dnsimple: Allow dnsimple_record.priority attribute to be set [GH-12843]
|
* Environment names must be safe to use as a URL path segment without escaping, and is enforced by the CLI.
|
||||||
* 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]
|
FEATURES:
|
||||||
* provider/ignition: ignition_filesystem, explicit option to create the filesystem [GH-12980]
|
|
||||||
* provider/ns1: Ensure provider checks for credentials [GH-12920]
|
* **New Resource:** `alicloud_db_instance` ([#12913](https://github.com/hashicorp/terraform/issues/12913))
|
||||||
* provider/openstack: Adding Timeouts to Blockstorage Resources [GH-12862]
|
* **New Resource:** `aws_api_gateway_usage_plan` ([#12542](https://github.com/hashicorp/terraform/issues/12542))
|
||||||
* provider/openstack: Adding Timeouts to FWaaS v1 Resources [GH-12863]
|
* **New Resource:** `aws_api_gateway_usage_plan_key` ([#12851](https://github.com/hashicorp/terraform/issues/12851))
|
||||||
* provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources [GH-12865]
|
* **New Resource:** `github_repository_webhook` ([#12924](https://github.com/hashicorp/terraform/issues/12924))
|
||||||
* provider/openstack: Adding Timeouts to Network Resources [GH-12866]
|
* **New Resource:** `random_pet` ([#12903](https://github.com/hashicorp/terraform/issues/12903))
|
||||||
* provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867]
|
* **New Interpolation:** `substr` ([#12870](https://github.com/hashicorp/terraform/issues/12870))
|
||||||
* provider/pagerduty: Validate credentials [GH-12854]
|
* **S3 Environments:** The S3 remote state backend now supports named environments
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
|
||||||
|
* 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:
|
BUG FIXES:
|
||||||
|
|
||||||
* core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. [GH-12888]
|
* 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) [GH-12849]
|
* 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 [GH-11984]
|
* 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 [GH-12986]
|
* 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 [GH-12883]
|
* 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 [GH-12891]
|
* 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 [GH-12915]
|
* 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 [GH-12922]
|
* 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 [GH-12943]
|
* provider/aws: Increase AWS AMI Destroy timeout ([#12943](https://github.com/hashicorp/terraform/issues/12943))
|
||||||
* provider/aws: Set aws_vpc ipv6 for associated only [GH-12899]
|
* 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 [GH-12998]
|
* 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 [GH-13013]
|
* provider/aws: Specify that aws_network_acl_rule requires a cidr block ([#13013](https://github.com/hashicorp/terraform/issues/13013))
|
||||||
* provider/google: turn compute_instance_group.instances into a set [GH-12790]
|
* provider/aws: aws_network_acl_rule treat all and -1 for protocol the same ([#13049](https://github.com/hashicorp/terraform/issues/13049))
|
||||||
* provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791]
|
* 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)
|
## 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._
|
_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 (
|
import (
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,8 +13,12 @@ const (
|
||||||
VpcNet = InstanceNetWork("vpc")
|
VpcNet = InstanceNetWork("vpc")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// timeout for common product, ecs e.g.
|
||||||
const defaultTimeout = 120
|
const defaultTimeout = 120
|
||||||
|
|
||||||
|
// timeout for long time progerss product, rds e.g.
|
||||||
|
const defaultLongTimeout = 800
|
||||||
|
|
||||||
func getRegion(d *schema.ResourceData, meta interface{}) common.Region {
|
func getRegion(d *schema.ResourceData, meta interface{}) common.Region {
|
||||||
return meta.(*AliyunClient).Region
|
return meta.(*AliyunClient).Region
|
||||||
}
|
}
|
||||||
|
@ -50,3 +55,26 @@ func isProtocalValid(value string) bool {
|
||||||
}
|
}
|
||||||
return res
|
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/common"
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
|
"github.com/denverdino/aliyungo/rds"
|
||||||
"github.com/denverdino/aliyungo/slb"
|
"github.com/denverdino/aliyungo/slb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,6 +20,9 @@ type Config struct {
|
||||||
type AliyunClient struct {
|
type AliyunClient struct {
|
||||||
Region common.Region
|
Region common.Region
|
||||||
ecsconn *ecs.Client
|
ecsconn *ecs.Client
|
||||||
|
rdsconn *rds.Client
|
||||||
|
// use new version
|
||||||
|
ecsNewconn *ecs.Client
|
||||||
vpcconn *ecs.Client
|
vpcconn *ecs.Client
|
||||||
slbconn *slb.Client
|
slbconn *slb.Client
|
||||||
}
|
}
|
||||||
|
@ -35,6 +39,17 @@ func (c *Config) Client() (*AliyunClient, error) {
|
||||||
return nil, err
|
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()
|
slbconn, err := c.slbConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -48,11 +63,15 @@ func (c *Config) Client() (*AliyunClient, error) {
|
||||||
return &AliyunClient{
|
return &AliyunClient{
|
||||||
Region: c.Region,
|
Region: c.Region,
|
||||||
ecsconn: ecsconn,
|
ecsconn: ecsconn,
|
||||||
|
ecsNewconn: ecsNewconn,
|
||||||
vpcconn: vpcconn,
|
vpcconn: vpcconn,
|
||||||
slbconn: slbconn,
|
slbconn: slbconn,
|
||||||
|
rdsconn: rdsconn,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BusinessInfoKey = "Terraform"
|
||||||
|
|
||||||
func (c *Config) loadAndValidate() error {
|
func (c *Config) loadAndValidate() error {
|
||||||
err := c.validateRegion()
|
err := c.validateRegion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,7 +93,9 @@ func (c *Config) validateRegion() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) ecsConn() (*ecs.Client, 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()
|
_, err := client.DescribeRegions()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,20 +105,21 @@ func (c *Config) ecsConn() (*ecs.Client, error) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) slbConn() (*slb.Client, error) {
|
func (c *Config) rdsConn() (*rds.Client, error) {
|
||||||
client := slb.NewClient(c.AccessKey, c.SecretKey)
|
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
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) vpcConn() (*ecs.Client, error) {
|
func (c *Config) vpcConn() (*ecs.Client, error) {
|
||||||
_, err := c.ecsConn()
|
client := ecs.NewVPCClient(c.AccessKey, c.SecretKey, c.Region)
|
||||||
|
client.SetBusinessInfo(BusinessInfoKey)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &ecs.Client{}
|
|
||||||
client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey)
|
|
||||||
return client, nil
|
return client, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func dataSourceAlicloudImages() *schema.Resource {
|
func dataSourceAlicloudImages() *schema.Resource {
|
||||||
|
@ -175,15 +175,28 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string))
|
params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, _, err := conn.DescribeImages(params)
|
var allImages []ecs.ImageType
|
||||||
|
|
||||||
|
for {
|
||||||
|
images, paginationResult, err := conn.DescribeImages(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
allImages = append(allImages, images...)
|
||||||
|
|
||||||
|
pagination := paginationResult.NextPage()
|
||||||
|
if pagination == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Pagination = *pagination
|
||||||
}
|
}
|
||||||
|
|
||||||
var filteredImages []ecs.ImageType
|
var filteredImages []ecs.ImageType
|
||||||
if nameRegexOk {
|
if nameRegexOk {
|
||||||
r := regexp.MustCompile(nameRegex.(string))
|
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
|
// Check for a very rare case where the response would include no
|
||||||
// image name. No name means nothing to attempt a match against,
|
// image name. No name means nothing to attempt a match against,
|
||||||
// therefore we are skipping such image.
|
// therefore we are skipping such image.
|
||||||
|
@ -198,7 +211,7 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filteredImages = resp[:]
|
filteredImages = allImages[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
var images []ecs.ImageType
|
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
|
// Instance store test - using centos images
|
||||||
const testAccCheckAlicloudImagesDataSourceImagesConfig = `
|
const testAccCheckAlicloudImagesDataSourceImagesConfig = `
|
||||||
data "alicloud_images" "multi_image" {
|
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}.*"
|
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(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"),
|
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.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.memory_size", "8"),
|
||||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"),
|
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(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"),
|
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.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.region_id", "cn-shenzhen"),
|
||||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"),
|
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"),
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package alicloud
|
package alicloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,6 +26,7 @@ func TestAccAlicloudZonesDataSource_basic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() {
|
PreCheck: func() {
|
||||||
testAccPreCheck(t)
|
testAccPreCheck(t)
|
||||||
|
@ -33,7 +37,7 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||||
Config: testAccCheckAlicloudZonesDataSourceFilter,
|
Config: testAccCheckAlicloudZonesDataSourceFilter,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
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,
|
Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
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 = `
|
const testAccCheckAlicloudZonesDataSourceBasicConfig = `
|
||||||
data "alicloud_zones" "foo" {
|
data "alicloud_zones" "foo" {
|
||||||
}
|
}
|
||||||
|
@ -55,16 +105,28 @@ data "alicloud_zones" "foo" {
|
||||||
|
|
||||||
const testAccCheckAlicloudZonesDataSourceFilter = `
|
const testAccCheckAlicloudZonesDataSourceFilter = `
|
||||||
data "alicloud_zones" "foo" {
|
data "alicloud_zones" "foo" {
|
||||||
"available_instance_type"= "ecs.c2.xlarge"
|
available_instance_type= "ecs.c2.xlarge"
|
||||||
"available_resource_creation"= "VSwitch"
|
available_resource_creation= "VSwitch"
|
||||||
"available_disk_category"= "cloud_efficiency"
|
available_disk_category= "cloud_efficiency"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = `
|
const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = `
|
||||||
data "alicloud_zones" "foo" {
|
data "alicloud_zones" "foo" {
|
||||||
"available_instance_type"= "ecs.c2.xlarge"
|
available_instance_type= "ecs.c2.xlarge"
|
||||||
"available_resource_creation"= "IoOptimized"
|
available_resource_creation= "IoOptimized"
|
||||||
"available_disk_category"= "cloud"
|
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"
|
DiskIncorrectStatus = "IncorrectDiskStatus"
|
||||||
DiskCreatingSnapshot = "DiskCreatingSnapshot"
|
DiskCreatingSnapshot = "DiskCreatingSnapshot"
|
||||||
InstanceLockedForSecurity = "InstanceLockedForSecurity"
|
InstanceLockedForSecurity = "InstanceLockedForSecurity"
|
||||||
|
SystemDiskNotFound = "SystemDiskNotFound"
|
||||||
// eip
|
// eip
|
||||||
EipIncorrectStatus = "IncorrectEipStatus"
|
EipIncorrectStatus = "IncorrectEipStatus"
|
||||||
InstanceIncorrectStatus = "IncorrectInstanceStatus"
|
InstanceIncorrectStatus = "IncorrectInstanceStatus"
|
||||||
|
|
|
@ -30,3 +30,8 @@ const (
|
||||||
GroupRulePolicyAccept = GroupRulePolicy("accept")
|
GroupRulePolicyAccept = GroupRulePolicy("accept")
|
||||||
GroupRulePolicyDrop = GroupRulePolicy("drop")
|
GroupRulePolicyDrop = GroupRulePolicy("drop")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EcsApiVersion20160314 = "2016-03-14"
|
||||||
|
EcsApiVersion20140526 = "2014-05-26"
|
||||||
|
)
|
||||||
|
|
|
@ -8,13 +8,41 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
|
slb.HTTPListenerType
|
||||||
|
|
||||||
InstancePort int
|
InstancePort int
|
||||||
LoadBalancerPort int
|
LoadBalancerPort int
|
||||||
Protocol string
|
Protocol string
|
||||||
|
//tcp & udp
|
||||||
|
PersistenceTimeout int
|
||||||
|
|
||||||
|
//https
|
||||||
SSLCertificateId string
|
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
|
// Takes the result of flatmap.Expand for an array of listeners and
|
||||||
// returns ELB API compatible objects
|
// returns ELB API compatible objects
|
||||||
func expandListeners(configured []interface{}) ([]*Listener, error) {
|
func expandListeners(configured []interface{}) ([]*Listener, error) {
|
||||||
|
@ -31,13 +59,78 @@ func expandListeners(configured []interface{}) ([]*Listener, error) {
|
||||||
InstancePort: ip,
|
InstancePort: ip,
|
||||||
LoadBalancerPort: lp,
|
LoadBalancerPort: lp,
|
||||||
Protocol: data["lb_protocol"].(string),
|
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 {
|
if v, ok := data["ssl_certificate_id"]; ok {
|
||||||
l.SSLCertificateId = v.(string)
|
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
|
var valid bool
|
||||||
if l.SSLCertificateId != "" {
|
if l.SSLCertificateId != "" {
|
||||||
// validate the protocol is correct
|
// validate the protocol is correct
|
||||||
|
|
|
@ -26,7 +26,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"region": &schema.Schema{
|
"region": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"),
|
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", DEFAULT_REGION),
|
||||||
Description: descriptions["region"],
|
Description: descriptions["region"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"alicloud_disk_attachment": resourceAliyunDiskAttachment(),
|
"alicloud_disk_attachment": resourceAliyunDiskAttachment(),
|
||||||
"alicloud_security_group": resourceAliyunSecurityGroup(),
|
"alicloud_security_group": resourceAliyunSecurityGroup(),
|
||||||
"alicloud_security_group_rule": resourceAliyunSecurityGroupRule(),
|
"alicloud_security_group_rule": resourceAliyunSecurityGroupRule(),
|
||||||
|
"alicloud_db_instance": resourceAlicloudDBInstance(),
|
||||||
"alicloud_vpc": resourceAliyunVpc(),
|
"alicloud_vpc": resourceAliyunVpc(),
|
||||||
"alicloud_nat_gateway": resourceAliyunNatGateway(),
|
"alicloud_nat_gateway": resourceAliyunNatGateway(),
|
||||||
//both subnet and vswith exists,cause compatible old version, and compatible aws habit.
|
//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"
|
name = "terraform-test-group"
|
||||||
description = "New security group"
|
description = "New security group"
|
||||||
}
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
|
@ -136,9 +136,13 @@ func testAccCheckDiskDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccDiskConfig = `
|
const testAccDiskConfig = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_disk" "foo" {
|
resource "alicloud_disk" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
name = "New-disk"
|
name = "New-disk"
|
||||||
description = "Hello ecs disk."
|
description = "Hello ecs disk."
|
||||||
category = "cloud_efficiency"
|
category = "cloud_efficiency"
|
||||||
|
@ -146,10 +150,15 @@ resource "alicloud_disk" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const testAccDiskConfigWithTags = `
|
const testAccDiskConfigWithTags = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_disk" "bar" {
|
resource "alicloud_disk" "bar" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
size = "10"
|
category = "cloud_efficiency"
|
||||||
|
size = "20"
|
||||||
tags {
|
tags {
|
||||||
Name = "TerraformTest"
|
Name = "TerraformTest"
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,10 @@ func testAccCheckEIPAssociationDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccEIPAssociationConfig = `
|
const testAccEIPAssociationConfig = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "main" {
|
resource "alicloud_vpc" "main" {
|
||||||
cidr_block = "10.1.0.0/21"
|
cidr_block = "10.1.0.0/21"
|
||||||
}
|
}
|
||||||
|
@ -115,19 +119,23 @@ resource "alicloud_vpc" "main" {
|
||||||
resource "alicloud_vswitch" "main" {
|
resource "alicloud_vswitch" "main" {
|
||||||
vpc_id = "${alicloud_vpc.main.id}"
|
vpc_id = "${alicloud_vpc.main.id}"
|
||||||
cidr_block = "10.1.1.0/24"
|
cidr_block = "10.1.1.0/24"
|
||||||
availability_zone = "cn-beijing-a"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
depends_on = [
|
depends_on = [
|
||||||
"alicloud_vpc.main"]
|
"alicloud_vpc.main"]
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_instance" "instance" {
|
resource "alicloud_instance" "instance" {
|
||||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
# cn-beijing
|
||||||
instance_type = "ecs.s1.small"
|
|
||||||
availability_zone = "cn-beijing-a"
|
|
||||||
security_groups = ["${alicloud_security_group.group.id}"]
|
|
||||||
vswitch_id = "${alicloud_vswitch.main.id}"
|
vswitch_id = "${alicloud_vswitch.main.id}"
|
||||||
instance_name = "hello"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
io_optimized = "none"
|
|
||||||
|
# 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 {
|
tags {
|
||||||
Name = "TerraformTest-instance"
|
Name = "TerraformTest-instance"
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
@ -21,8 +22,9 @@ func resourceAliyunInstance() *schema.Resource {
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"availability_zone": &schema.Schema{
|
"availability_zone": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"image_id": &schema.Schema{
|
"image_id": &schema.Schema{
|
||||||
|
@ -60,11 +62,6 @@ func resourceAliyunInstance() *schema.Resource {
|
||||||
ValidateFunc: validateInstanceDescription,
|
ValidateFunc: validateInstanceDescription,
|
||||||
},
|
},
|
||||||
|
|
||||||
"instance_network_type": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Computed: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
"internet_charge_type": &schema.Schema{
|
"internet_charge_type": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -104,11 +101,19 @@ func resourceAliyunInstance() *schema.Resource {
|
||||||
Default: "cloud",
|
Default: "cloud",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateAllowedStringValue([]string{
|
||||||
|
string(ecs.DiskCategoryCloud),
|
||||||
|
string(ecs.DiskCategoryCloudSSD),
|
||||||
|
string(ecs.DiskCategoryCloudEfficiency),
|
||||||
|
string(ecs.DiskCategoryEphemeralSSD),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
"system_disk_size": &schema.Schema{
|
"system_disk_size": &schema.Schema{
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateIntegerInRange(40, 500),
|
||||||
},
|
},
|
||||||
|
|
||||||
//subnet_id and vswitch_id both exists, cause compatible old version, and aws habit.
|
//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{
|
"private_ip": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -168,6 +172,11 @@ func resourceAliyunInstance() *schema.Resource {
|
||||||
func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
conn := meta.(*AliyunClient).ecsconn
|
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)
|
args, err := buildAliyunInstanceArgs(d, meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -181,7 +190,8 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
d.SetId(instanceID)
|
d.SetId(instanceID)
|
||||||
|
|
||||||
d.Set("password", d.Get("password"))
|
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) {
|
if d.Get("allocate_public_ip").(bool) {
|
||||||
_, err := conn.AllocatePublicIpAddress(d.Id())
|
_, err := conn.AllocatePublicIpAddress(d.Id())
|
||||||
|
@ -207,11 +217,56 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
return resourceAliyunInstanceUpdate(d, meta)
|
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 {
|
func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AliyunClient)
|
client := meta.(*AliyunClient)
|
||||||
conn := client.ecsconn
|
conn := client.ecsconn
|
||||||
|
|
||||||
instance, err := client.QueryInstancesById(d.Id())
|
instance, err := client.QueryInstancesById(d.Id())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if notFoundError(err) {
|
if notFoundError(err) {
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
|
@ -220,7 +275,15 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
||||||
return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err)
|
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("instance_name", instance.InstanceName)
|
||||||
d.Set("description", instance.Description)
|
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("host_name", instance.HostName)
|
||||||
d.Set("image_id", instance.ImageId)
|
d.Set("image_id", instance.ImageId)
|
||||||
d.Set("instance_type", instance.InstanceType)
|
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 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'.
|
// 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")
|
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) != "" {
|
if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" {
|
||||||
ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||||
d.Set("private_ip", ipAddress)
|
d.Set("private_ip", ipAddress)
|
||||||
|
@ -414,6 +475,30 @@ func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
return nil
|
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) {
|
func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) {
|
||||||
client := meta.(*AliyunClient)
|
client := meta.(*AliyunClient)
|
||||||
|
@ -421,15 +506,18 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||||
args := &ecs.CreateInstanceArgs{
|
args := &ecs.CreateInstanceArgs{
|
||||||
RegionId: getRegion(d, meta),
|
RegionId: getRegion(d, meta),
|
||||||
InstanceType: d.Get("instance_type").(string),
|
InstanceType: d.Get("instance_type").(string),
|
||||||
PrivateIpAddress: d.Get("private_ip").(string),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
imageID := d.Get("image_id").(string)
|
imageID := d.Get("image_id").(string)
|
||||||
|
|
||||||
args.ImageId = imageID
|
args.ImageId = imageID
|
||||||
|
|
||||||
zoneID := d.Get("availability_zone").(string)
|
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)
|
zone, err := client.DescribeZone(zoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -439,8 +527,19 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
args.ZoneId = zoneID
|
args.ZoneId = zoneID
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
args.SystemDisk = ecs.SystemDiskType{
|
||||||
|
Category: systemDiskCategory,
|
||||||
|
Size: systemDiskSize,
|
||||||
|
}
|
||||||
|
|
||||||
sgs, ok := d.GetOk("security_groups")
|
sgs, ok := d.GetOk("security_groups")
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -451,17 +550,6 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||||
if err == nil {
|
if err == nil {
|
||||||
args.SecurityGroupId = sg0
|
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 != "" {
|
if v := d.Get("instance_name").(string); v != "" {
|
||||||
|
@ -472,7 +560,7 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||||
args.Description = v
|
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 != "" {
|
if v := d.Get("internet_charge_type").(string); v != "" {
|
||||||
args.InternetChargeType = common.InternetChargeType(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 != "" {
|
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)
|
vswitchValue := d.Get("subnet_id").(string)
|
||||||
|
|
|
@ -56,6 +56,7 @@ func TestAccAlicloudInstance_basic(t *testing.T) {
|
||||||
"alicloud_instance.foo",
|
"alicloud_instance.foo",
|
||||||
"internet_charge_type",
|
"internet_charge_type",
|
||||||
"PayByBandwidth"),
|
"PayByBandwidth"),
|
||||||
|
testAccCheckSystemDiskSize("alicloud_instance.foo", 80),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -355,10 +356,6 @@ func TestAccAlicloudInstance_tags(t *testing.T) {
|
||||||
Config: testAccCheckInstanceConfigTagsUpdate,
|
Config: testAccCheckInstanceConfigTagsUpdate,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||||
resource.TestCheckResourceAttr(
|
|
||||||
"alicloud_instance.foo",
|
|
||||||
"tags.foo",
|
|
||||||
""),
|
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"alicloud_instance.foo",
|
"alicloud_instance.foo",
|
||||||
"tags.bar",
|
"tags.bar",
|
||||||
|
@ -418,8 +415,8 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) {
|
||||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||||
if privateIP != "172.16.0.229" {
|
if privateIP == "" {
|
||||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
return fmt.Errorf("can't get private IP")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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
|
var instance ecs.InstanceAttributesType
|
||||||
|
|
||||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||||
if privateIP != "172.16.0.229" {
|
if privateIP == "" {
|
||||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
return fmt.Errorf("can't get private IP")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -468,7 +465,7 @@ func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckInstanceDestroy,
|
CheckDestroy: testAccCheckInstanceDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP,
|
Config: testAccInstanceConfigAssociatePublicIP,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||||
testCheckPrivateIP(),
|
testCheckPrivateIP(),
|
||||||
|
@ -597,6 +594,36 @@ func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schem
|
||||||
return nil
|
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 = `
|
const testAccInstanceConfig = `
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
|
@ -609,11 +636,10 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
system_disk_category = "cloud_ssd"
|
system_disk_category = "cloud_ssd"
|
||||||
|
system_disk_size = 80
|
||||||
|
|
||||||
instance_type = "ecs.n1.small"
|
instance_type = "ecs.n1.small"
|
||||||
internet_charge_type = "PayByBandwidth"
|
internet_charge_type = "PayByBandwidth"
|
||||||
|
@ -628,6 +654,11 @@ resource "alicloud_instance" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const testAccInstanceConfigVPC = `
|
const testAccInstanceConfigVPC = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -636,7 +667,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
cidr_block = "172.16.0.0/21"
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -647,7 +678,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
|
@ -666,6 +696,11 @@ resource "alicloud_instance" "foo" {
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccInstanceConfigUserData = `
|
const testAccInstanceConfigUserData = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -674,7 +709,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
cidr_block = "172.16.0.0/21"
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -685,7 +720,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
# series II
|
# series II
|
||||||
|
@ -727,7 +761,6 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
provider = "alicloud.beijing"
|
provider = "alicloud.beijing"
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
internet_charge_type = "PayByBandwidth"
|
internet_charge_type = "PayByBandwidth"
|
||||||
|
@ -742,7 +775,6 @@ resource "alicloud_instance" "foo" {
|
||||||
resource "alicloud_instance" "bar" {
|
resource "alicloud_instance" "bar" {
|
||||||
# cn-shanghai
|
# cn-shanghai
|
||||||
provider = "alicloud.shanghai"
|
provider = "alicloud.shanghai"
|
||||||
availability_zone = "cn-shanghai-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
internet_charge_type = "PayByBandwidth"
|
internet_charge_type = "PayByBandwidth"
|
||||||
|
@ -768,7 +800,6 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
instance_type = "ecs.s2.large"
|
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}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"]
|
||||||
instance_name = "test_foo"
|
instance_name = "test_foo"
|
||||||
io_optimized = "optimized"
|
io_optimized = "optimized"
|
||||||
|
system_disk_category = "cloud_efficiency"
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const testAccInstanceConfig_multiSecurityGroup_add = `
|
const testAccInstanceConfig_multiSecurityGroup_add = `
|
||||||
|
@ -796,7 +828,6 @@ resource "alicloud_security_group" "tf_test_add_sg" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
instance_type = "ecs.s2.large"
|
instance_type = "ecs.s2.large"
|
||||||
|
@ -805,6 +836,7 @@ resource "alicloud_instance" "foo" {
|
||||||
"${alicloud_security_group.tf_test_add_sg.id}"]
|
"${alicloud_security_group.tf_test_add_sg.id}"]
|
||||||
instance_name = "test_foo"
|
instance_name = "test_foo"
|
||||||
io_optimized = "optimized"
|
io_optimized = "optimized"
|
||||||
|
system_disk_category = "cloud_efficiency"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -814,9 +846,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
description = "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" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
instance_type = "ecs.s2.large"
|
instance_type = "ecs.s2.large"
|
||||||
|
@ -824,6 +877,7 @@ resource "alicloud_instance" "foo" {
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||||
instance_name = "test_foo"
|
instance_name = "test_foo"
|
||||||
io_optimized = "optimized"
|
io_optimized = "optimized"
|
||||||
|
system_disk_category = "cloud_efficiency"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -836,18 +890,23 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
instance_type = "ecs.s2.large"
|
instance_type = "ecs.s2.large"
|
||||||
internet_charge_type = "PayByBandwidth"
|
internet_charge_type = "PayByBandwidth"
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"]
|
||||||
instance_name = "test_foo"
|
instance_name = "test_foo"
|
||||||
io_optimized = "none"
|
io_optimized = "optimized"
|
||||||
|
system_disk_category = "cloud_efficiency"
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccInstanceNetworkInstanceSecurityGroups = `
|
const testAccInstanceNetworkInstanceSecurityGroups = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -856,7 +915,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
cidr_block = "172.16.0.0/21"
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -867,7 +926,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
|
@ -892,7 +950,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
|
@ -918,7 +975,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
|
@ -941,9 +997,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
description = "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" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
|
@ -965,9 +1042,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
description = "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" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
|
@ -984,6 +1082,11 @@ resource "alicloud_instance" "foo" {
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccInstanceConfigPrivateIP = `
|
const testAccInstanceConfigPrivateIP = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -992,7 +1095,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/24"
|
cidr_block = "172.16.0.0/24"
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -1003,11 +1106,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||||
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
private_ip = "172.16.0.229"
|
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
instance_type = "ecs.n1.medium"
|
instance_type = "ecs.n1.medium"
|
||||||
|
@ -1017,7 +1118,12 @@ resource "alicloud_instance" "foo" {
|
||||||
instance_name = "test_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" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -1026,7 +1132,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/24"
|
cidr_block = "172.16.0.0/24"
|
||||||
availability_zone = "cn-beijing-b"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -1037,11 +1143,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||||
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
private_ip = "172.16.0.229"
|
|
||||||
allocate_public_ip = "true"
|
allocate_public_ip = "true"
|
||||||
internet_max_bandwidth_out = 5
|
internet_max_bandwidth_out = 5
|
||||||
internet_charge_type = "PayByBandwidth"
|
internet_charge_type = "PayByBandwidth"
|
||||||
|
@ -1055,6 +1159,11 @@ resource "alicloud_instance" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const testAccVpcInstanceWithSecurityRule = `
|
const testAccVpcInstanceWithSecurityRule = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_disk_category"= "cloud_efficiency"
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "10.1.0.0/21"
|
cidr_block = "10.1.0.0/21"
|
||||||
|
@ -1063,7 +1172,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "10.1.1.0/24"
|
cidr_block = "10.1.1.0/24"
|
||||||
availability_zone = "cn-beijing-c"
|
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "alicloud_security_group" "tf_test_foo" {
|
resource "alicloud_security_group" "tf_test_foo" {
|
||||||
|
@ -1085,7 +1194,6 @@ resource "alicloud_security_group_rule" "ingress" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-c"
|
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||||
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"log"
|
"log"
|
||||||
|
@ -71,7 +72,7 @@ func resourceAliyunNatGateway() *schema.Resource {
|
||||||
func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
conn := meta.(*AliyunClient).vpcconn
|
conn := meta.(*AliyunClient).vpcconn
|
||||||
|
|
||||||
args := &CreateNatGatewayArgs{
|
args := &ecs.CreateNatGatewayArgs{
|
||||||
RegionId: getRegion(d, meta),
|
RegionId: getRegion(d, meta),
|
||||||
VpcId: d.Get("vpc_id").(string),
|
VpcId: d.Get("vpc_id").(string),
|
||||||
Spec: d.Get("spec").(string),
|
Spec: d.Get("spec").(string),
|
||||||
|
@ -79,11 +80,11 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
bandwidthPackages := d.Get("bandwidth_packages").([]interface{})
|
bandwidthPackages := d.Get("bandwidth_packages").([]interface{})
|
||||||
|
|
||||||
bandwidthPackageTypes := []BandwidthPackageType{}
|
bandwidthPackageTypes := []ecs.BandwidthPackageType{}
|
||||||
|
|
||||||
for _, e := range bandwidthPackages {
|
for _, e := range bandwidthPackages {
|
||||||
pack := e.(map[string]interface{})
|
pack := e.(map[string]interface{})
|
||||||
bandwidthPackage := BandwidthPackageType{
|
bandwidthPackage := ecs.BandwidthPackageType{
|
||||||
IpCount: pack["ip_count"].(int),
|
IpCount: pack["ip_count"].(int),
|
||||||
Bandwidth: pack["bandwidth"].(int),
|
Bandwidth: pack["bandwidth"].(int),
|
||||||
}
|
}
|
||||||
|
@ -106,8 +107,7 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
||||||
if v, ok := d.GetOk("description"); ok {
|
if v, ok := d.GetOk("description"); ok {
|
||||||
args.Description = v.(string)
|
args.Description = v.(string)
|
||||||
}
|
}
|
||||||
|
resp, err := conn.CreateNatGateway(args)
|
||||||
resp, err := CreateNatGateway(conn, args)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("CreateNatGateway got error: %#v", err)
|
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 {
|
func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
client := meta.(*AliyunClient)
|
client := meta.(*AliyunClient)
|
||||||
|
conn := client.vpcconn
|
||||||
|
|
||||||
natGateway, err := client.DescribeNatGateway(d.Id())
|
natGateway, err := client.DescribeNatGateway(d.Id())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,7 +151,7 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
d.Partial(true)
|
d.Partial(true)
|
||||||
attributeUpdate := false
|
attributeUpdate := false
|
||||||
args := &ModifyNatGatewayAttributeArgs{
|
args := &ecs.ModifyNatGatewayAttributeArgs{
|
||||||
RegionId: natGateway.RegionId,
|
RegionId: natGateway.RegionId,
|
||||||
NatGatewayId: natGateway.NatGatewayId,
|
NatGatewayId: natGateway.NatGatewayId,
|
||||||
}
|
}
|
||||||
|
@ -183,28 +184,28 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
}
|
}
|
||||||
|
|
||||||
if attributeUpdate {
|
if attributeUpdate {
|
||||||
if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil {
|
if err := conn.ModifyNatGatewayAttribute(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("spec") {
|
if d.HasChange("spec") {
|
||||||
d.SetPartial("spec")
|
d.SetPartial("spec")
|
||||||
var spec NatGatewaySpec
|
var spec ecs.NatGatewaySpec
|
||||||
if v, ok := d.GetOk("spec"); ok {
|
if v, ok := d.GetOk("spec"); ok {
|
||||||
spec = NatGatewaySpec(v.(string))
|
spec = ecs.NatGatewaySpec(v.(string))
|
||||||
} else {
|
} else {
|
||||||
// set default to small spec
|
// set default to small spec
|
||||||
spec = NatGatewaySmallSpec
|
spec = ecs.NatGatewaySmallSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
args := &ModifyNatGatewaySpecArgs{
|
args := &ecs.ModifyNatGatewaySpecArgs{
|
||||||
RegionId: natGateway.RegionId,
|
RegionId: natGateway.RegionId,
|
||||||
NatGatewayId: natGateway.NatGatewayId,
|
NatGatewayId: natGateway.NatGatewayId,
|
||||||
Spec: spec,
|
Spec: spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ModifyNatGatewaySpec(client.vpcconn, args)
|
err := conn.ModifyNatGatewaySpec(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%#v %#v", err, *args)
|
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 {
|
func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
client := meta.(*AliyunClient)
|
client := meta.(*AliyunClient)
|
||||||
|
conn := client.vpcconn
|
||||||
|
|
||||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
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),
|
RegionId: getRegion(d, meta),
|
||||||
NatGatewayId: d.Id(),
|
NatGatewayId: d.Id(),
|
||||||
})
|
})
|
||||||
|
@ -232,7 +234,7 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
retry := false
|
retry := false
|
||||||
for _, pack := range packages {
|
for _, pack := range packages {
|
||||||
err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{
|
err = conn.DeleteBandwidthPackage(&ecs.DeleteBandwidthPackageArgs{
|
||||||
RegionId: getRegion(d, meta),
|
RegionId: getRegion(d, meta),
|
||||||
BandwidthPackageId: pack.BandwidthPackageId,
|
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."))
|
return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted."))
|
||||||
}
|
}
|
||||||
|
|
||||||
args := &DeleteNatGatewayArgs{
|
args := &ecs.DeleteNatGatewayArgs{
|
||||||
RegionId: client.Region,
|
RegionId: client.Region,
|
||||||
NatGatewayId: d.Id(),
|
NatGatewayId: d.Id(),
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DeleteNatGateway(client.vpcconn, args)
|
err = conn.DeleteNatGateway(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
er, _ := err.(*common.Error)
|
er, _ := err.(*common.Error)
|
||||||
if er.ErrorResponse.Code == DependencyViolationBandwidthPackages {
|
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,
|
RegionId: client.Region,
|
||||||
NatGatewayId: d.Id(),
|
NatGatewayId: d.Id(),
|
||||||
}
|
}
|
||||||
gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs)
|
gw, _, gwErr := conn.DescribeNatGateways(describeArgs)
|
||||||
|
|
||||||
if gwErr != nil {
|
if gwErr != nil {
|
||||||
log.Printf("[ERROR] Describe NatGateways failed.")
|
log.Printf("[ERROR] Describe NatGateways failed.")
|
||||||
|
|
|
@ -3,13 +3,14 @@ package alicloud
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
||||||
var nat NatGatewaySetType
|
var nat ecs.NatGatewaySetType
|
||||||
|
|
||||||
testCheck := func(*terraform.State) error {
|
testCheck := func(*terraform.State) error {
|
||||||
if nat.BusinessStatus != "Normal" {
|
if nat.BusinessStatus != "Normal" {
|
||||||
|
@ -55,7 +56,7 @@ func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
||||||
var nat NatGatewaySetType
|
var nat ecs.NatGatewaySetType
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() {
|
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 {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -151,6 +152,10 @@ func testAccCheckNatGatewayDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccNatGatewayConfig = `
|
const testAccNatGatewayConfig = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -159,7 +164,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
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" {
|
resource "alicloud_nat_gateway" "foo" {
|
||||||
|
@ -169,11 +174,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||||
bandwidth_packages = [{
|
bandwidth_packages = [{
|
||||||
ip_count = 1
|
ip_count = 1
|
||||||
bandwidth = 5
|
bandwidth = 5
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}, {
|
}, {
|
||||||
ip_count = 2
|
ip_count = 2
|
||||||
bandwidth = 10
|
bandwidth = 10
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}]
|
}]
|
||||||
depends_on = [
|
depends_on = [
|
||||||
"alicloud_vswitch.foo"]
|
"alicloud_vswitch.foo"]
|
||||||
|
@ -181,6 +186,10 @@ resource "alicloud_nat_gateway" "foo" {
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccNatGatewayConfigSpec = `
|
const testAccNatGatewayConfigSpec = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -189,7 +198,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
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" {
|
resource "alicloud_nat_gateway" "foo" {
|
||||||
|
@ -199,11 +208,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||||
bandwidth_packages = [{
|
bandwidth_packages = [{
|
||||||
ip_count = 1
|
ip_count = 1
|
||||||
bandwidth = 5
|
bandwidth = 5
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}, {
|
}, {
|
||||||
ip_count = 2
|
ip_count = 2
|
||||||
bandwidth = 10
|
bandwidth = 10
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}]
|
}]
|
||||||
depends_on = [
|
depends_on = [
|
||||||
"alicloud_vswitch.foo"]
|
"alicloud_vswitch.foo"]
|
||||||
|
@ -211,6 +220,10 @@ resource "alicloud_nat_gateway" "foo" {
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccNatGatewayConfigSpecUpgrade = `
|
const testAccNatGatewayConfigSpecUpgrade = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -219,7 +232,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
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" {
|
resource "alicloud_nat_gateway" "foo" {
|
||||||
|
@ -229,11 +242,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||||
bandwidth_packages = [{
|
bandwidth_packages = [{
|
||||||
ip_count = 1
|
ip_count = 1
|
||||||
bandwidth = 5
|
bandwidth = 5
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}, {
|
}, {
|
||||||
ip_count = 2
|
ip_count = 2
|
||||||
bandwidth = 10
|
bandwidth = 10
|
||||||
zone = "cn-beijing-b"
|
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||||
}]
|
}]
|
||||||
depends_on = [
|
depends_on = [
|
||||||
"alicloud_vswitch.foo"]
|
"alicloud_vswitch.foo"]
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"time"
|
"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."))
|
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) {
|
func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) {
|
||||||
|
|
|
@ -34,6 +34,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
ValidateFunc: validateSecurityRuleNicType,
|
ValidateFunc: validateSecurityRuleNicType,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -67,7 +68,6 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Default: "0.0.0.0/0",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"source_security_group_id": &schema.Schema{
|
"source_security_group_id": &schema.Schema{
|
||||||
|
@ -86,15 +86,17 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
|
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)
|
sgId := d.Get("security_group_id").(string)
|
||||||
ptl := d.Get("ip_protocol").(string)
|
ptl := d.Get("ip_protocol").(string)
|
||||||
port := d.Get("port_range").(string)
|
port := d.Get("port_range").(string)
|
||||||
|
nicType := d.Get("nic_type").(string)
|
||||||
|
|
||||||
var autherr error
|
var autherr error
|
||||||
switch GroupRuleDirection(ruleType) {
|
switch GroupRuleDirection(direction) {
|
||||||
case GroupRuleIngress:
|
case GroupRuleIngress:
|
||||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -114,10 +116,11 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac
|
||||||
if autherr != nil {
|
if autherr != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Error authorizing security group rule type %s: %s",
|
"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)
|
return resourceAliyunSecurityGroupRuleRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +128,11 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||||
client := meta.(*AliyunClient)
|
client := meta.(*AliyunClient)
|
||||||
parts := strings.Split(d.Id(), ":")
|
parts := strings.Split(d.Id(), ":")
|
||||||
sgId := parts[0]
|
sgId := parts[0]
|
||||||
types := parts[1]
|
direction := parts[1]
|
||||||
ip_protocol := parts[2]
|
ip_protocol := parts[2]
|
||||||
port_range := parts[3]
|
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 err != nil {
|
||||||
if notFoundError(err) {
|
if notFoundError(err) {
|
||||||
|
@ -137,7 +141,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Error SecurityGroup rule: %#v", err)
|
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("type", rule.Direction)
|
||||||
d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol)))
|
d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol)))
|
||||||
d.Set("nic_type", rule.NicType)
|
d.Set("nic_type", rule.NicType)
|
||||||
|
@ -146,7 +150,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||||
d.Set("priority", rule.Priority)
|
d.Set("priority", rule.Priority)
|
||||||
d.Set("security_group_id", sgId)
|
d.Set("security_group_id", sgId)
|
||||||
//support source and desc by type
|
//support source and desc by type
|
||||||
if GroupRuleDirection(types) == GroupRuleIngress {
|
if GroupRuleDirection(direction) == GroupRuleIngress {
|
||||||
d.Set("cidr_ip", rule.SourceCidrIp)
|
d.Set("cidr_ip", rule.SourceCidrIp)
|
||||||
d.Set("source_security_group_id", rule.SourceGroupId)
|
d.Set("source_security_group_id", rule.SourceGroupId)
|
||||||
d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount)
|
d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount)
|
||||||
|
@ -161,8 +165,10 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||||
|
|
||||||
func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AliyunClient)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -170,8 +176,30 @@ func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interfac
|
||||||
AuthorizeSecurityGroupArgs: *args,
|
AuthorizeSecurityGroupArgs: *args,
|
||||||
}
|
}
|
||||||
return client.RevokeSecurityGroup(revokeArgs)
|
return client.RevokeSecurityGroup(revokeArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := buildAliyunSecurityEgressArgs(d, meta)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
revokeArgs := &ecs.RevokeSecurityGroupEgressArgs{
|
||||||
|
AuthorizeSecurityGroupEgressArgs: *args,
|
||||||
|
}
|
||||||
|
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) {
|
func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) {
|
||||||
conn := meta.(*AliyunClient).ecsconn
|
conn := meta.(*AliyunClient).ecsconn
|
||||||
|
|
||||||
|
@ -199,12 +227,17 @@ func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*
|
||||||
args.NicType = ecs.NicType(v)
|
args.NicType = ecs.NicType(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := d.Get("cidr_ip").(string); v != "" {
|
cidrIp := d.Get("cidr_ip").(string)
|
||||||
args.SourceCidrIp = v
|
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 != "" {
|
if sourceGroupId != "" {
|
||||||
args.SourceGroupId = v
|
args.SourceGroupId = sourceGroupId
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
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)
|
args.NicType = ecs.NicType(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := d.Get("cidr_ip").(string); v != "" {
|
cidrIp := d.Get("cidr_ip").(string)
|
||||||
args.DestCidrIp = v
|
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 != "" {
|
if sourceGroupId != "" {
|
||||||
args.DestGroupId = v
|
args.DestGroupId = sourceGroupId
|
||||||
}
|
}
|
||||||
|
|
||||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
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/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"log"
|
"log"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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) {
|
func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) {
|
||||||
var pt ecs.PermissionType
|
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 {
|
func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
@ -128,7 +236,8 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour
|
||||||
client := testAccProvider.Meta().(*AliyunClient)
|
client := testAccProvider.Meta().(*AliyunClient)
|
||||||
log.Printf("[WARN]get sg rule %s", rs.Primary.ID)
|
log.Printf("[WARN]get sg rule %s", rs.Primary.ID)
|
||||||
parts := strings.Split(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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -152,7 +261,7 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(rs.Primary.ID, ":")
|
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 {
|
if rule != nil {
|
||||||
return fmt.Errorf("Error SecurityGroup Rule still exist")
|
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 = `
|
const testAccSecurityGroupRuleVpcIngress = `
|
||||||
resource "alicloud_security_group" "foo" {
|
resource "alicloud_security_group" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.vpc.id}"
|
vpc_id = "${alicloud_vpc.vpc.id}"
|
||||||
|
@ -231,6 +357,22 @@ resource "alicloud_security_group_rule" "ingress" {
|
||||||
cidr_ip = "10.159.6.18/12"
|
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 = `
|
const testAccSecurityGroupRuleMultiIngress = `
|
||||||
|
@ -260,4 +402,27 @@ resource "alicloud_security_group_rule" "ingress2" {
|
||||||
cidr_ip = "127.0.1.18/16"
|
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"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"errors"
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
"github.com/denverdino/aliyungo/slb"
|
"github.com/denverdino/aliyungo/slb"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
@ -83,40 +84,124 @@ func resourceAliyunSlb() *schema.Resource {
|
||||||
ValidateFunc: validateSlbListenerBandwidth,
|
ValidateFunc: validateSlbListenerBandwidth,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
//http
|
|
||||||
"scheduler": &schema.Schema{
|
"scheduler": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
ValidateFunc: validateSlbListenerScheduler,
|
ValidateFunc: validateSlbListenerScheduler,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "wrr",
|
Default: slb.WRRScheduler,
|
||||||
},
|
},
|
||||||
|
//http & https
|
||||||
"sticky_session": &schema.Schema{
|
"sticky_session": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
ValidateFunc: validateSlbListenerStickySession,
|
ValidateFunc: validateAllowedStringValue([]string{
|
||||||
|
string(slb.OnFlag),
|
||||||
|
string(slb.OffFlag)}),
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Default: slb.OffFlag,
|
||||||
},
|
},
|
||||||
|
//http & https
|
||||||
"sticky_session_type": &schema.Schema{
|
"sticky_session_type": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
ValidateFunc: validateSlbListenerStickySessionType,
|
ValidateFunc: validateAllowedStringValue([]string{
|
||||||
|
string(slb.InsertStickySessionType),
|
||||||
|
string(slb.ServerStickySessionType)}),
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
//http & https
|
||||||
|
"cookie_timeout": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
ValidateFunc: validateSlbListenerCookieTimeout,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
//http & https
|
||||||
"cookie": &schema.Schema{
|
"cookie": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
ValidateFunc: validateSlbListenerCookie,
|
ValidateFunc: validateSlbListenerCookie,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
"PersistenceTimeout": &schema.Schema{
|
//tcp & udp
|
||||||
|
"persistence_timeout": &schema.Schema{
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
ValidateFunc: validateSlbListenerPersistenceTimeout,
|
ValidateFunc: validateSlbListenerPersistenceTimeout,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: 0,
|
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
|
//https
|
||||||
"ssl_certificate_id": &schema.Schema{
|
"ssl_certificate_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
//https
|
||||||
|
//"ca_certificate_id": &schema.Schema{
|
||||||
|
// Type: schema.TypeString,
|
||||||
|
// Optional: true,
|
||||||
|
//},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Set: resourceAliyunSlbListenerHash,
|
Set: resourceAliyunSlbListenerHash,
|
||||||
|
@ -349,44 +434,53 @@ func resourceAliyunSlbListenerHash(v interface{}) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
|
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") {
|
if listener.Protocol == strings.ToLower("tcp") {
|
||||||
args := &slb.CreateLoadBalancerTCPListenerArgs{
|
|
||||||
LoadBalancerId: loadBalancerId,
|
args := getTcpListenerArgs(loadBalancerId, listener)
|
||||||
ListenerPort: listener.LoadBalancerPort,
|
|
||||||
BackendServerPort: listener.InstancePort,
|
if err := conn.CreateLoadBalancerTCPListener(&args); err != nil {
|
||||||
Bandwidth: listener.Bandwidth,
|
|
||||||
}
|
|
||||||
if err := conn.CreateLoadBalancerTCPListener(args); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else if listener.Protocol == strings.ToLower("http") {
|
||||||
|
args, argsErr := getHttpListenerArgs(loadBalancerId, listener)
|
||||||
|
if paramErr := errTypeJudge(argsErr); paramErr != nil {
|
||||||
|
return paramErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if listener.Protocol == strings.ToLower("http") {
|
if err := conn.CreateLoadBalancerHTTPListener(&args); err != nil {
|
||||||
args := &slb.CreateLoadBalancerHTTPListenerArgs{
|
|
||||||
LoadBalancerId: loadBalancerId,
|
|
||||||
ListenerPort: listener.LoadBalancerPort,
|
|
||||||
BackendServerPort: listener.InstancePort,
|
|
||||||
Bandwidth: listener.Bandwidth,
|
|
||||||
StickySession: slb.OffFlag,
|
|
||||||
HealthCheck: slb.OffFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conn.CreateLoadBalancerHTTPListener(args); err != nil {
|
|
||||||
return err
|
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{
|
args := &slb.CreateLoadBalancerHTTPSListenerArgs{
|
||||||
|
HTTPListenerType: listenerType,
|
||||||
HTTPListenerType: slb.HTTPListenerType{
|
|
||||||
LoadBalancerId: loadBalancerId,
|
|
||||||
ListenerPort: listener.LoadBalancerPort,
|
|
||||||
BackendServerPort: listener.InstancePort,
|
|
||||||
Bandwidth: listener.Bandwidth,
|
|
||||||
StickySession: slb.OffFlag,
|
|
||||||
HealthCheck: slb.OffFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if listener.SSLCertificateId == "" {
|
if listener.SSLCertificateId == "" {
|
||||||
return fmt.Errorf("Server Certificated Id cann't be null")
|
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 {
|
if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
} else if listener.Protocol == strings.ToLower("udp") {
|
||||||
|
args := getUdpListenerArgs(loadBalancerId, listener)
|
||||||
|
|
||||||
if listener.Protocol == strings.ToLower("udp") {
|
if err := conn.CreateLoadBalancerUDPListener(&args); err != nil {
|
||||||
args := &slb.CreateLoadBalancerUDPListenerArgs{
|
|
||||||
LoadBalancerId: loadBalancerId,
|
|
||||||
ListenerPort: listener.LoadBalancerPort,
|
|
||||||
BackendServerPort: listener.InstancePort,
|
|
||||||
Bandwidth: listener.Bandwidth,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conn.CreateLoadBalancerUDPListener(args); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,3 +505,102 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener)
|
||||||
|
|
||||||
return nil
|
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"
|
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" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-b"
|
|
||||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||||
|
|
||||||
# series II
|
# series II
|
||||||
|
|
|
@ -85,7 +85,7 @@ func TestAccAlicloudSlb_listener(t *testing.T) {
|
||||||
testListener := func() resource.TestCheckFunc {
|
testListener := func() resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
listenerPorts := slb.ListenerPorts.ListenerPort[0]
|
listenerPorts := slb.ListenerPorts.ListenerPort[0]
|
||||||
if listenerPorts != 161 {
|
if listenerPorts != 2001 {
|
||||||
return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts)
|
return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,21 +260,49 @@ resource "alicloud_slb" "listener" {
|
||||||
"lb_port" = "21"
|
"lb_port" = "21"
|
||||||
"lb_protocol" = "tcp"
|
"lb_protocol" = "tcp"
|
||||||
"bandwidth" = 1
|
"bandwidth" = 1
|
||||||
|
"persistence_timeout" = 500
|
||||||
|
"health_check_type" = "http"
|
||||||
},{
|
},{
|
||||||
"instance_port" = "8000"
|
"instance_port" = "8000"
|
||||||
"lb_port" = "80"
|
"lb_port" = "80"
|
||||||
"lb_protocol" = "http"
|
"lb_protocol" = "http"
|
||||||
|
"sticky_session" = "on"
|
||||||
|
"sticky_session_type" = "insert"
|
||||||
|
"cookie_timeout" = 800
|
||||||
"bandwidth" = 1
|
"bandwidth" = 1
|
||||||
},{
|
},{
|
||||||
"instance_port" = "1611"
|
"instance_port" = "8001"
|
||||||
"lb_port" = "161"
|
"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"
|
"lb_protocol" = "udp"
|
||||||
"bandwidth" = 1
|
"bandwidth" = 1
|
||||||
|
"persistence_timeout" = 700
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccSlb4Vpc = `
|
const testAccSlb4Vpc = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -283,7 +311,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
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" {
|
resource "alicloud_slb" "vpc" {
|
||||||
|
|
|
@ -124,6 +124,10 @@ func testAccCheckRouteEntryDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccRouteEntryConfig = `
|
const testAccRouteEntryConfig = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "10.1.0.0/21"
|
cidr_block = "10.1.0.0/21"
|
||||||
|
@ -132,7 +136,7 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "10.1.1.0/24"
|
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" {
|
resource "alicloud_route_entry" "foo" {
|
||||||
|
@ -162,7 +166,6 @@ resource "alicloud_security_group_rule" "ingress" {
|
||||||
|
|
||||||
resource "alicloud_instance" "foo" {
|
resource "alicloud_instance" "foo" {
|
||||||
# cn-beijing
|
# cn-beijing
|
||||||
availability_zone = "cn-beijing-c"
|
|
||||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||||
|
|
||||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||||
|
|
|
@ -92,6 +92,10 @@ func testAccCheckVswitchDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccVswitchConfig = `
|
const testAccVswitchConfig = `
|
||||||
|
data "alicloud_zones" "default" {
|
||||||
|
"available_resource_creation"= "VSwitch"
|
||||||
|
}
|
||||||
|
|
||||||
resource "alicloud_vpc" "foo" {
|
resource "alicloud_vpc" "foo" {
|
||||||
name = "tf_test_foo"
|
name = "tf_test_foo"
|
||||||
cidr_block = "172.16.0.0/12"
|
cidr_block = "172.16.0.0/12"
|
||||||
|
@ -100,6 +104,6 @@ resource "alicloud_vpc" "foo" {
|
||||||
resource "alicloud_vswitch" "foo" {
|
resource "alicloud_vswitch" "foo" {
|
||||||
vpc_id = "${alicloud_vpc.foo.id}"
|
vpc_id = "${alicloud_vpc.foo.id}"
|
||||||
cidr_block = "172.16.0.0/21"
|
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 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) {
|
func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) {
|
||||||
idsStr, jerr := json.Marshal(ids)
|
idsStr, jerr := json.Marshal(ids)
|
||||||
if jerr != nil {
|
if jerr != nil {
|
||||||
|
@ -119,6 +137,23 @@ func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.Instanc
|
||||||
return &instances[0], nil
|
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
|
// ResourceAvailable check resource available for zone
|
||||||
func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error {
|
func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error {
|
||||||
available := false
|
available := false
|
||||||
|
@ -186,15 +221,26 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr
|
||||||
return client.ecsconn.DescribeSecurityGroupAttribute(args)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range sg.Permissions.Permission {
|
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
|
return &p, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,6 +249,11 @@ func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error {
|
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)
|
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
|
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,
|
RegionId: client.Region,
|
||||||
NatGatewayId: natGatewayId,
|
NatGatewayId: natGatewayId,
|
||||||
}
|
}
|
||||||
|
|
||||||
natGateways, _, err := DescribeNatGateways(client.ecsconn, args)
|
natGateways, _, err := client.vpcconn.DescribeNatGateways(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -132,3 +132,23 @@ func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType
|
||||||
}
|
}
|
||||||
return nil, nil
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/denverdino/aliyungo/common"
|
"github.com/denverdino/aliyungo/common"
|
||||||
"github.com/denverdino/aliyungo/ecs"
|
"github.com/denverdino/aliyungo/ecs"
|
||||||
"github.com/hashicorp/terraform/helper/validation"
|
"github.com/denverdino/aliyungo/slb"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// common
|
// common
|
||||||
func validateInstancePort(v interface{}, k string) (ws []string, errors []error) {
|
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) {
|
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
|
// ecs
|
||||||
func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) {
|
func validateDiskCategory(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
category := ecs.DiskCategory(v.(string))
|
||||||
string(ecs.DiskCategoryCloud),
|
if category != ecs.DiskCategoryCloud && category != ecs.DiskCategoryCloudEfficiency && category != ecs.DiskCategoryCloudSSD {
|
||||||
string(ecs.DiskCategoryCloudEfficiency),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s %s", k, ecs.DiskCategoryCloud, ecs.DiskCategoryCloudEfficiency, ecs.DiskCategoryCloudSSD))
|
||||||
string(ecs.DiskCategoryCloudSSD),
|
}
|
||||||
}, false)(v, k)
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInstanceName(v interface{}, k string) (ws []string, errors []error) {
|
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) {
|
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) {
|
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) {
|
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
|
//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) {
|
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) {
|
func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
rt := GroupRuleDirection(v.(string))
|
||||||
string(GroupRuleIngress),
|
if rt != GroupRuleIngress && rt != GroupRuleEgress {
|
||||||
string(GroupRuleEgress),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleIngress, GroupRuleEgress))
|
||||||
}, false)(v, k)
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) {
|
func validateSecurityRuleIpProtocol(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
pt := GroupRuleIpProtocol(v.(string))
|
||||||
string(GroupRuleTcp),
|
if pt != GroupRuleTcp && pt != GroupRuleUdp && pt != GroupRuleIcmp && pt != GroupRuleGre && pt != GroupRuleAll {
|
||||||
string(GroupRuleUdp),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s %s %s %s", k,
|
||||||
string(GroupRuleIcmp),
|
GroupRuleTcp, GroupRuleUdp, GroupRuleIcmp, GroupRuleGre, GroupRuleAll))
|
||||||
string(GroupRuleGre),
|
}
|
||||||
string(GroupRuleAll),
|
|
||||||
}, false)(v, k)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) {
|
func validateSecurityRuleNicType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
pt := GroupRuleNicType(v.(string))
|
||||||
string(GroupRuleInternet),
|
if pt != GroupRuleInternet && pt != GroupRuleIntranet {
|
||||||
string(GroupRuleIntranet),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRuleInternet, GroupRuleIntranet))
|
||||||
}, false)(v, k)
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) {
|
func validateSecurityRulePolicy(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
pt := GroupRulePolicy(v.(string))
|
||||||
string(GroupRulePolicyAccept),
|
if pt != GroupRulePolicyAccept && pt != GroupRulePolicyDrop {
|
||||||
string(GroupRulePolicyDrop),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k, GroupRulePolicyAccept, GroupRulePolicyDrop))
|
||||||
}, false)(v, k)
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSecurityPriority(v interface{}, k string) (ws []string, errors []error) {
|
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
|
// validateCIDRNetworkAddress ensures that the string value is a valid CIDR that
|
||||||
// represents a network address - it adds an error otherwise
|
// represents a network address - it adds an error otherwise
|
||||||
func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
|
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) {
|
func validateRouteEntryNextHopType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
nht := ecs.NextHopType(v.(string))
|
||||||
string(ecs.NextHopIntance),
|
if nht != ecs.NextHopIntance && nht != ecs.NextHopTunnel {
|
||||||
string(ecs.NextHopTunnel),
|
errors = append(errors, fmt.Errorf("%s must be one of %s %s", k,
|
||||||
}, false)(v, k)
|
ecs.NextHopIntance, ecs.NextHopTunnel))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSwitchCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
|
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
|
// validateIoOptimized ensures that the string value is a valid IoOptimized that
|
||||||
// represents a IoOptimized - it adds an error otherwise
|
// represents a IoOptimized - it adds an error otherwise
|
||||||
func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) {
|
func validateIoOptimized(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"",
|
ioOptimized := ecs.IoOptimized(value)
|
||||||
string(ecs.IoOptimizedNone),
|
if ioOptimized != ecs.IoOptimizedNone &&
|
||||||
string(ecs.IoOptimizedOptimized),
|
ioOptimized != ecs.IoOptimizedOptimized {
|
||||||
}, false)(v, k)
|
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
|
// validateInstanceNetworkType ensures that the string value is a classic or vpc
|
||||||
func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) {
|
func validateInstanceNetworkType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"",
|
network := InstanceNetWork(value)
|
||||||
string(ClassicNet),
|
if network != ClassicNet &&
|
||||||
string(VpcNet),
|
network != VpcNet {
|
||||||
}, false)(v, k)
|
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) {
|
func validateInstanceChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"",
|
chargeType := common.InstanceChargeType(value)
|
||||||
string(common.PrePaid),
|
if chargeType != common.PrePaid &&
|
||||||
string(common.PostPaid),
|
chargeType != common.PostPaid {
|
||||||
}, false)(v, k)
|
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) {
|
func validateInternetChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"",
|
chargeType := common.InternetChargeType(value)
|
||||||
string(common.PayByBandwidth),
|
if chargeType != common.PayByBandwidth &&
|
||||||
string(common.PayByTraffic),
|
chargeType != common.PayByTraffic {
|
||||||
}, false)(v, k)
|
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) {
|
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
|
// SLB
|
||||||
func validateSlbName(v interface{}, k string) (ws []string, errors []error) {
|
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) {
|
func validateSlbInternetChargeType(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"paybybandwidth",
|
chargeType := common.InternetChargeType(value)
|
||||||
"paybytraffic",
|
|
||||||
}, false)(v, k)
|
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) {
|
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) {
|
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) {
|
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) {
|
if scheduler != "wrr" && scheduler != "wlc" {
|
||||||
return validation.StringInSlice([]string{"", "on", "off"}, false)(v, k)
|
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
|
||||||
return validation.StringInSlice([]string{"", "insert", "server"}, false)(v, k)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) {
|
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) {
|
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
|
//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) {
|
func validateImageOwners(v interface{}, k string) (ws []string, errors []error) {
|
||||||
return validation.StringInSlice([]string{
|
if value := v.(string); value != "" {
|
||||||
"",
|
owners := ecs.ImageOwnerAlias(value)
|
||||||
string(ecs.ImageOwnerSystem),
|
if owners != ecs.ImageOwnerSystem &&
|
||||||
string(ecs.ImageOwnerSelf),
|
owners != ecs.ImageOwnerSelf &&
|
||||||
string(ecs.ImageOwnerOthers),
|
owners != ecs.ImageOwnerOthers &&
|
||||||
string(ecs.ImageOwnerMarketplace),
|
owners != ecs.ImageOwnerMarketplace &&
|
||||||
string(ecs.ImageOwnerDefault),
|
owners != ecs.ImageOwnerDefault {
|
||||||
}, false)(v, k)
|
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) {
|
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,
|
Type: schema.TypeInt,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
"iam_role_arn": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
"tags": {
|
"tags": {
|
||||||
Type: schema.TypeMap,
|
Type: schema.TypeMap,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -86,6 +90,7 @@ func dataSourceAwsCloudFormationStackRead(d *schema.ResourceData, meta interface
|
||||||
d.Set("description", stack.Description)
|
d.Set("description", stack.Description)
|
||||||
d.Set("disable_rollback", stack.DisableRollback)
|
d.Set("disable_rollback", stack.DisableRollback)
|
||||||
d.Set("timeout_in_minutes", stack.TimeoutInMinutes)
|
d.Set("timeout_in_minutes", stack.TimeoutInMinutes)
|
||||||
|
d.Set("iam_role_arn", stack.RoleARN)
|
||||||
|
|
||||||
if len(stack.NotificationARNs) > 0 {
|
if len(stack.NotificationARNs) > 0 {
|
||||||
d.Set("notification_arns", schema.NewSet(schema.HashString, flattenStringList(stack.NotificationARNs)))
|
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/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,15 +19,15 @@ func timePtr(t time.Time) *time.Time {
|
||||||
|
|
||||||
func TestResourceSortByExpirationDate(t *testing.T) {
|
func TestResourceSortByExpirationDate(t *testing.T) {
|
||||||
certs := []*iam.ServerCertificateMetadata{
|
certs := []*iam.ServerCertificateMetadata{
|
||||||
&iam.ServerCertificateMetadata{
|
{
|
||||||
ServerCertificateName: aws.String("oldest"),
|
ServerCertificateName: aws.String("oldest"),
|
||||||
Expiration: timePtr(time.Now()),
|
Expiration: timePtr(time.Now()),
|
||||||
},
|
},
|
||||||
&iam.ServerCertificateMetadata{
|
{
|
||||||
ServerCertificateName: aws.String("latest"),
|
ServerCertificateName: aws.String("latest"),
|
||||||
Expiration: timePtr(time.Now().Add(3 * time.Hour)),
|
Expiration: timePtr(time.Now().Add(3 * time.Hour)),
|
||||||
},
|
},
|
||||||
&iam.ServerCertificateMetadata{
|
{
|
||||||
ServerCertificateName: aws.String("in between"),
|
ServerCertificateName: aws.String("in between"),
|
||||||
Expiration: timePtr(time.Now().Add(2 * time.Hour)),
|
Expiration: timePtr(time.Now().Add(2 * time.Hour)),
|
||||||
},
|
},
|
||||||
|
@ -38,13 +39,18 @@ func TestResourceSortByExpirationDate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) {
|
func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) {
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccAwsDataIAMServerCertConfig,
|
Config: testAccIAMServerCertConfig(rInt),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: testAccAwsDataIAMServerCertConfig(rInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"),
|
resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"),
|
||||||
resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "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" {
|
data "aws_iam_server_certificate" "test" {
|
||||||
name = "${aws_iam_server_certificate.test_cert.name}"
|
name = "${aws_iam_server_certificate.test_cert.name}"
|
||||||
latest = true
|
latest = true
|
||||||
}
|
}
|
||||||
`, testAccIAMServerCertConfig)
|
`, testAccIAMServerCertConfig(rInt))
|
||||||
|
}
|
||||||
|
|
||||||
var testAccAwsDataIAMServerCertConfigMatchNamePrefix = `
|
var testAccAwsDataIAMServerCertConfigMatchNamePrefix = `
|
||||||
data "aws_iam_server_certificate" "test" {
|
data "aws_iam_server_certificate" "test" {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
|
func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
|
||||||
rInt := acctest.RandInt()
|
rInt := acctest.RandInt()
|
||||||
publicResourceName := "aws_route53_zon.test"
|
publicResourceName := "aws_route53_zone.test"
|
||||||
publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
|
publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
|
||||||
privateResourceName := "aws_route53_zone.test_private"
|
privateResourceName := "aws_route53_zone.test_private"
|
||||||
privateDomain := fmt.Sprintf("test.acc-%d.", rInt)
|
privateDomain := fmt.Sprintf("test.acc-%d.", rInt)
|
||||||
|
|
|
@ -3,19 +3,20 @@ package aws
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAWSCustomerGateway_importBasic(t *testing.T) {
|
func TestAccAWSCustomerGateway_importBasic(t *testing.T) {
|
||||||
resourceName := "aws_customer_gateway.foo"
|
resourceName := "aws_customer_gateway.foo"
|
||||||
|
randInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccCustomerGatewayConfig,
|
Config: testAccCustomerGatewayConfig(randInt),
|
||||||
},
|
},
|
||||||
|
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
|
|
|
@ -3,11 +3,13 @@ package aws
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
||||||
resourceName := "aws_efs_file_system.foo-with-tags"
|
resourceName := "aws_efs_file_system.foo-with-tags"
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
@ -15,7 +17,7 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSEFSFileSystemConfigWithTags,
|
Config: testAccAWSEFSFileSystemConfigWithTags(rInt),
|
||||||
},
|
},
|
||||||
|
|
||||||
resource.TestStep{
|
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": {
|
"listener_arn": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
ValidateFunc: validateAwsAlbListenerRulePriority,
|
ValidateFunc: validateAwsAlbListenerRulePriority,
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
|
@ -66,6 +68,7 @@ func resourceAwsAlbListenerRule() *schema.Resource {
|
||||||
},
|
},
|
||||||
"values": {
|
"values": {
|
||||||
Type: schema.TypeList,
|
Type: schema.TypeList,
|
||||||
|
MaxItems: 1,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
@ -183,10 +186,32 @@ func resourceAwsAlbListenerRuleRead(d *schema.ResourceData, meta interface{}) er
|
||||||
func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
elbconn := meta.(*AWSClient).elbv2conn
|
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{
|
params := &elbv2.ModifyRuleInput{
|
||||||
RuleArn: aws.String(d.Id()),
|
RuleArn: aws.String(d.Id()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.HasChange("action") {
|
||||||
actions := d.Get("action").([]interface{})
|
actions := d.Get("action").([]interface{})
|
||||||
params.Actions = make([]*elbv2.Action, len(actions))
|
params.Actions = make([]*elbv2.Action, len(actions))
|
||||||
for i, action := range actions {
|
for i, action := range actions {
|
||||||
|
@ -196,7 +221,11 @@ func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{})
|
||||||
Type: aws.String(actionMap["type"].(string)),
|
Type: aws.String(actionMap["type"].(string)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
requestUpdate = true
|
||||||
|
d.SetPartial("action")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("condition") {
|
||||||
conditions := d.Get("condition").([]interface{})
|
conditions := d.Get("condition").([]interface{})
|
||||||
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
||||||
for i, condition := range conditions {
|
for i, condition := range conditions {
|
||||||
|
@ -210,7 +239,11 @@ func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{})
|
||||||
params.Conditions[i].Values[j] = aws.String(value.(string))
|
params.Conditions[i].Values[j] = aws.String(value.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
requestUpdate = true
|
||||||
|
d.SetPartial("condition")
|
||||||
|
}
|
||||||
|
|
||||||
|
if requestUpdate {
|
||||||
resp, err := elbconn.ModifyRule(params)
|
resp, err := elbconn.ModifyRule(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
||||||
|
@ -219,6 +252,9 @@ func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{})
|
||||||
if len(resp.Rules) == 0 {
|
if len(resp.Rules) == 0 {
|
||||||
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Partial(false)
|
||||||
|
|
||||||
return resourceAwsAlbListenerRuleRead(d, meta)
|
return resourceAwsAlbListenerRuleRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package aws
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"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 {
|
func testAccCheckAWSALBListenerRuleExists(n string, res *elbv2.Rule) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
@ -104,6 +189,117 @@ func testAccCheckAWSALBListenerRuleDestroy(s *terraform.State) error {
|
||||||
return nil
|
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 {
|
func testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName string) string {
|
||||||
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
||||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||||
|
@ -214,3 +410,238 @@ resource "aws_security_group" "alb_test" {
|
||||||
}
|
}
|
||||||
}`, albName, targetGroupName)
|
}`, 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,
|
Delete: resourceAwsCloudFormationStackDelete,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"template_body": &schema.Schema{
|
"template_body": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -37,42 +37,42 @@ func resourceAwsCloudFormationStack() *schema.Resource {
|
||||||
return template
|
return template
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"template_url": &schema.Schema{
|
"template_url": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
"capabilities": &schema.Schema{
|
"capabilities": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
"disable_rollback": &schema.Schema{
|
"disable_rollback": {
|
||||||
Type: schema.TypeBool,
|
Type: schema.TypeBool,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"notification_arns": &schema.Schema{
|
"notification_arns": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Set: schema.HashString,
|
Set: schema.HashString,
|
||||||
},
|
},
|
||||||
"on_failure": &schema.Schema{
|
"on_failure": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"parameters": &schema.Schema{
|
"parameters": {
|
||||||
Type: schema.TypeMap,
|
Type: schema.TypeMap,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"outputs": &schema.Schema{
|
"outputs": {
|
||||||
Type: schema.TypeMap,
|
Type: schema.TypeMap,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"policy_body": &schema.Schema{
|
"policy_body": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -82,20 +82,24 @@ func resourceAwsCloudFormationStack() *schema.Resource {
|
||||||
return json
|
return json
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"policy_url": &schema.Schema{
|
"policy_url": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
"timeout_in_minutes": &schema.Schema{
|
"timeout_in_minutes": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
"tags": &schema.Schema{
|
"tags": {
|
||||||
Type: schema.TypeMap,
|
Type: schema.TypeMap,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: 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)
|
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)
|
log.Printf("[DEBUG] Creating CloudFormation Stack: %s", input)
|
||||||
resp, err := conn.CreateStack(&input)
|
resp, err := conn.CreateStack(&input)
|
||||||
|
@ -297,6 +304,7 @@ func resourceAwsCloudFormationStackRead(d *schema.ResourceData, meta interface{}
|
||||||
|
|
||||||
d.Set("name", stack.StackName)
|
d.Set("name", stack.StackName)
|
||||||
d.Set("arn", stack.StackId)
|
d.Set("arn", stack.StackId)
|
||||||
|
d.Set("iam_role_arn", stack.RoleARN)
|
||||||
|
|
||||||
if stack.TimeoutInMinutes != nil {
|
if stack.TimeoutInMinutes != nil {
|
||||||
d.Set("timeout_in_minutes", int(*stack.TimeoutInMinutes))
|
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))
|
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)
|
log.Printf("[DEBUG] Updating CloudFormation stack: %s", input)
|
||||||
stack, err := conn.UpdateStack(input)
|
stack, err := conn.UpdateStack(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -20,7 +20,7 @@ func TestAccAWSCloudFormation_basic(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig,
|
Config: testAccAWSCloudFormationConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.network", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.network", &stack),
|
||||||
|
@ -38,7 +38,7 @@ func TestAccAWSCloudFormation_yaml(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_yaml,
|
Config: testAccAWSCloudFormationConfig_yaml,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.yaml", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.yaml", &stack),
|
||||||
|
@ -56,7 +56,7 @@ func TestAccAWSCloudFormation_defaultParams(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_defaultParams,
|
Config: testAccAWSCloudFormationConfig_defaultParams,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.asg-demo", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.asg-demo", &stack),
|
||||||
|
@ -75,7 +75,7 @@ func TestAccAWSCloudFormation_allAttributes(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies,
|
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.full", &stack),
|
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.TestCheckResourceAttr("aws_cloudformation_stack.full", "timeout_in_minutes", "10"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies_modified,
|
Config: testAccAWSCloudFormationConfig_allAttributesWithBodies_modified,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.full", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.full", &stack),
|
||||||
|
@ -124,13 +124,13 @@ func TestAccAWSCloudFormation_withParams(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_withParams,
|
Config: testAccAWSCloudFormationConfig_withParams,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_withParams_modified,
|
Config: testAccAWSCloudFormationConfig_withParams_modified,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with_params", &stack),
|
||||||
|
@ -149,13 +149,13 @@ func TestAccAWSCloudFormation_withUrl_withParams(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams,
|
Config: testAccAWSCloudFormationConfig_templateUrl_withParams,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_modified,
|
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_modified,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params", &stack),
|
||||||
|
@ -173,7 +173,7 @@ func TestAccAWSCloudFormation_withUrl_withParams_withYaml(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
CheckDestroy: testAccCheckAWSCloudFormationDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_withYaml,
|
Config: testAccAWSCloudFormationConfig_templateUrl_withParams_withYaml,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCloudFormationStackExists("aws_cloudformation_stack.with-url-and-params-and-yaml", &stack),
|
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/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
||||||
var gateway ec2.CustomerGateway
|
var gateway ec2.CustomerGateway
|
||||||
|
randInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
IDRefreshName: "aws_customer_gateway.foo",
|
IDRefreshName: "aws_customer_gateway.foo",
|
||||||
|
@ -23,19 +25,19 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfig,
|
Config: testAccCustomerGatewayConfig(randInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfigUpdateTags,
|
Config: testAccCustomerGatewayConfigUpdateTags(randInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfigForceReplace,
|
Config: testAccCustomerGatewayConfigForceReplace(randInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||||
),
|
),
|
||||||
|
@ -46,6 +48,7 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) {
|
||||||
|
|
||||||
func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
||||||
var gateway ec2.CustomerGateway
|
var gateway ec2.CustomerGateway
|
||||||
|
randInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
IDRefreshName: "aws_customer_gateway.foo",
|
IDRefreshName: "aws_customer_gateway.foo",
|
||||||
|
@ -53,13 +56,13 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfig,
|
Config: testAccCustomerGatewayConfig(randInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfigIdentical,
|
Config: testAccCustomerGatewayConfigIdentical(randInt),
|
||||||
ExpectError: regexp.MustCompile("An existing customer gateway"),
|
ExpectError: regexp.MustCompile("An existing customer gateway"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -68,13 +71,14 @@ func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) {
|
||||||
|
|
||||||
func TestAccAWSCustomerGateway_disappears(t *testing.T) {
|
func TestAccAWSCustomerGateway_disappears(t *testing.T) {
|
||||||
var gateway ec2.CustomerGateway
|
var gateway ec2.CustomerGateway
|
||||||
|
randInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccCustomerGatewayConfig,
|
Config: testAccCustomerGatewayConfig(randInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway),
|
||||||
testAccAWSCustomerGatewayDisappears(&gateway),
|
testAccAWSCustomerGatewayDisappears(&gateway),
|
||||||
|
@ -190,59 +194,67 @@ func testAccCheckCustomerGateway(gatewayResource string, cgw *ec2.CustomerGatewa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccCustomerGatewayConfig = `
|
func testAccCustomerGatewayConfig(randInt int) string {
|
||||||
resource "aws_customer_gateway" "foo" {
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_customer_gateway" "foo" {
|
||||||
bgp_asn = 65000
|
bgp_asn = 65000
|
||||||
ip_address = "172.0.0.1"
|
ip_address = "172.0.0.1"
|
||||||
type = "ipsec.1"
|
type = "ipsec.1"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-gateway"
|
Name = "foo-gateway-%d"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testAccCustomerGatewayConfigIdentical = `
|
|
||||||
resource "aws_customer_gateway" "foo" {
|
|
||||||
bgp_asn = 65000
|
|
||||||
ip_address = "172.0.0.1"
|
|
||||||
type = "ipsec.1"
|
|
||||||
tags {
|
|
||||||
Name = "foo-gateway"
|
|
||||||
}
|
}
|
||||||
|
`, randInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_customer_gateway" "identical" {
|
func testAccCustomerGatewayConfigIdentical(randInt int) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_customer_gateway" "foo" {
|
||||||
bgp_asn = 65000
|
bgp_asn = 65000
|
||||||
ip_address = "172.0.0.1"
|
ip_address = "172.0.0.1"
|
||||||
type = "ipsec.1"
|
type = "ipsec.1"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-gateway-identical"
|
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.
|
// Add the Another: "tag" tag.
|
||||||
const testAccCustomerGatewayConfigUpdateTags = `
|
func testAccCustomerGatewayConfigUpdateTags(randInt int) string {
|
||||||
resource "aws_customer_gateway" "foo" {
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_customer_gateway" "foo" {
|
||||||
bgp_asn = 65000
|
bgp_asn = 65000
|
||||||
ip_address = "172.0.0.1"
|
ip_address = "172.0.0.1"
|
||||||
type = "ipsec.1"
|
type = "ipsec.1"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-gateway"
|
Name = "foo-gateway-%d"
|
||||||
Another = "tag"
|
Another = "tag-%d"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
`, randInt, randInt)
|
||||||
}
|
}
|
||||||
`
|
|
||||||
|
|
||||||
// Change the ip_address.
|
// Change the ip_address.
|
||||||
const testAccCustomerGatewayConfigForceReplace = `
|
func testAccCustomerGatewayConfigForceReplace(randInt int) string {
|
||||||
resource "aws_customer_gateway" "foo" {
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_customer_gateway" "foo" {
|
||||||
bgp_asn = 65000
|
bgp_asn = 65000
|
||||||
ip_address = "172.10.10.1"
|
ip_address = "172.10.10.1"
|
||||||
type = "ipsec.1"
|
type = "ipsec.1"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-gateway"
|
Name = "foo-gateway-%d"
|
||||||
Another = "tag"
|
Another = "tag-%d"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
`, randInt, randInt)
|
||||||
}
|
}
|
||||||
`
|
|
||||||
|
|
|
@ -9,11 +9,14 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACL Network ACLs all contain an explicit deny-all rule that cannot be
|
// ACL Network ACLs all contain explicit deny-all rules that cannot be
|
||||||
// destroyed or changed by users. This rule is numbered very high to be a
|
// destroyed or changed by users. This rules are numbered very high to be a
|
||||||
// catch-all.
|
// catch-all.
|
||||||
// See http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ACLs.html#default-network-acl
|
// 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 {
|
func resourceAwsDefaultNetworkAcl() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
|
@ -258,7 +261,8 @@ func revokeAllNetworkACLEntries(netaclId string, meta interface{}) error {
|
||||||
for _, e := range networkAcl.Entries {
|
for _, e := range networkAcl.Entries {
|
||||||
// Skip the default rules added by AWS. They can be neither
|
// 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
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,27 @@ func TestAccAWSDefaultNetworkAcl_basic(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_basic,
|
Config: testAccAWSDefaultNetworkConfig_basic,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
|
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{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_deny_ingress,
|
Config: testAccAWSDefaultNetworkConfig_deny_ingress,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0),
|
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{defaultEgressAcl}, 0, 2),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -77,8 +96,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -88,8 +107,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetRemoval(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_Subnets_remove,
|
Config: testAccAWSDefaultNetworkConfig_Subnets_remove,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||||
),
|
),
|
||||||
ExpectNonEmptyPlan: true,
|
ExpectNonEmptyPlan: true,
|
||||||
},
|
},
|
||||||
|
@ -108,8 +127,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
Config: testAccAWSDefaultNetworkConfig_Subnets,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2),
|
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 2, 2),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -128,8 +147,8 @@ func TestAccAWSDefaultNetworkAcl_SubnetReassign(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSDefaultNetworkConfig_Subnets_move,
|
Config: testAccAWSDefaultNetworkConfig_Subnets_move,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccGetWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
testAccGetAWSDefaultNetworkAcl("aws_default_network_acl.default", &networkAcl),
|
||||||
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0),
|
testAccCheckAWSDefaultACLAttributes(&networkAcl, []*ec2.NetworkAclEntry{}, 0, 2),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -141,14 +160,14 @@ func testAccCheckAWSDefaultNetworkAclDestroy(s *terraform.State) error {
|
||||||
return nil
|
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 {
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
aclEntriesCount := len(acl.Entries)
|
aclEntriesCount := len(acl.Entries)
|
||||||
ruleCount := len(rules)
|
ruleCount := len(rules)
|
||||||
|
|
||||||
// Default ACL has 2 hidden rules we can't do anything about
|
// Default ACL has hidden rules we can't do anything about
|
||||||
ruleCount = ruleCount + 2
|
ruleCount = ruleCount + hiddenRuleCount
|
||||||
|
|
||||||
if ruleCount != aclEntriesCount {
|
if ruleCount != aclEntriesCount {
|
||||||
return fmt.Errorf("Expected (%d) Rules, got (%d)", 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 {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
if !ok {
|
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 {
|
func dmsReplicationInstanceConfigUpdate(randId string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_role" "dms_iam_role" {
|
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\"}]}"
|
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 {
|
func dmsReplicationSubnetGroupConfig(randId string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_role" "dms_iam_role" {
|
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\"}]}"
|
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 {
|
func dmsReplicationTaskConfig(randId string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_role" "dms_iam_role" {
|
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\"}]}"
|
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) {
|
func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
||||||
|
rInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
|
@ -104,7 +105,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config: testAccAWSEFSFileSystemConfigWithTags,
|
Config: testAccAWSEFSFileSystemConfigWithTags(rInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckEfsFileSystem(
|
testAccCheckEfsFileSystem(
|
||||||
"aws_efs_file_system.foo-with-tags",
|
"aws_efs_file_system.foo-with-tags",
|
||||||
|
@ -116,7 +117,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
||||||
testAccCheckEfsFileSystemTags(
|
testAccCheckEfsFileSystemTags(
|
||||||
"aws_efs_file_system.foo-with-tags",
|
"aws_efs_file_system.foo-with-tags",
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Name": "foo-efs",
|
"Name": fmt.Sprintf("foo-efs-%d", rInt),
|
||||||
"Another": "tag",
|
"Another": "tag",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -143,13 +144,14 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAWSEFSFileSystem_pagedTags(t *testing.T) {
|
func TestAccAWSEFSFileSystem_pagedTags(t *testing.T) {
|
||||||
|
rInt := acctest.RandInt()
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccAWSEFSFileSystemConfigPagedTags,
|
Config: testAccAWSEFSFileSystemConfigPagedTags(rInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"aws_efs_file_system.foo",
|
"aws_efs_file_system.foo",
|
||||||
|
@ -312,11 +314,12 @@ resource "aws_efs_file_system" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testAccAWSEFSFileSystemConfigPagedTags = `
|
func testAccAWSEFSFileSystemConfigPagedTags(rInt int) string {
|
||||||
resource "aws_efs_file_system" "foo" {
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_efs_file_system" "foo" {
|
||||||
creation_token = "radeksimko"
|
creation_token = "radeksimko"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-efs"
|
Name = "foo-efs-%d"
|
||||||
Another = "tag"
|
Another = "tag"
|
||||||
Test = "yes"
|
Test = "yes"
|
||||||
User = "root"
|
User = "root"
|
||||||
|
@ -328,18 +331,21 @@ resource "aws_efs_file_system" "foo" {
|
||||||
PerfMode = "max"
|
PerfMode = "max"
|
||||||
Region = "us-west-2"
|
Region = "us-west-2"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
`, rInt)
|
||||||
}
|
}
|
||||||
`
|
|
||||||
|
|
||||||
const testAccAWSEFSFileSystemConfigWithTags = `
|
func testAccAWSEFSFileSystemConfigWithTags(rInt int) string {
|
||||||
resource "aws_efs_file_system" "foo-with-tags" {
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_efs_file_system" "foo-with-tags" {
|
||||||
creation_token = "yada_yada"
|
creation_token = "yada_yada"
|
||||||
tags {
|
tags {
|
||||||
Name = "foo-efs"
|
Name = "foo-efs-%d"
|
||||||
Another = "tag"
|
Another = "tag"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
`, rInt)
|
||||||
}
|
}
|
||||||
`
|
|
||||||
|
|
||||||
const testAccAWSEFSFileSystemConfigWithPerformanceMode = `
|
const testAccAWSEFSFileSystemConfigWithPerformanceMode = `
|
||||||
resource "aws_efs_file_system" "foo-with-performance-mode" {
|
resource "aws_efs_file_system" "foo-with-performance-mode" {
|
||||||
|
|
|
@ -661,7 +661,7 @@ resource "aws_elastic_beanstalk_environment" "tfenvtest" {
|
||||||
func testAccBeanstalkWorkerEnvConfig(rInt int) string {
|
func testAccBeanstalkWorkerEnvConfig(rInt int) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_instance_profile" "tftest" {
|
resource "aws_iam_instance_profile" "tftest" {
|
||||||
name = "tftest_profile"
|
name = "tftest_profile-%d"
|
||||||
roles = ["${aws_iam_role.tftest.name}"]
|
roles = ["${aws_iam_role.tftest.name}"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,7 +693,7 @@ func testAccBeanstalkWorkerEnvConfig(rInt int) string {
|
||||||
name = "IamInstanceProfile"
|
name = "IamInstanceProfile"
|
||||||
value = "${aws_iam_instance_profile.tftest.name}"
|
value = "${aws_iam_instance_profile.tftest.name}"
|
||||||
}
|
}
|
||||||
}`, rInt, rInt)
|
}`, rInt, rInt, rInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccBeanstalkEnvCnamePrefixConfig(randString string, rInt int) string {
|
func testAccBeanstalkEnvCnamePrefixConfig(randString string, rInt int) string {
|
||||||
|
@ -937,24 +937,24 @@ resource "aws_s3_bucket_object" "default" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_application" "default" {
|
resource "aws_elastic_beanstalk_application" "default" {
|
||||||
name = "tf-test-name"
|
name = "tf-test-name-%d"
|
||||||
description = "tf-test-desc"
|
description = "tf-test-desc"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_application_version" "default" {
|
resource "aws_elastic_beanstalk_application_version" "default" {
|
||||||
application = "tf-test-name"
|
application = "tf-test-name-%d"
|
||||||
name = "tf-test-version-label"
|
name = "tf-test-version-label"
|
||||||
bucket = "${aws_s3_bucket.default.id}"
|
bucket = "${aws_s3_bucket.default.id}"
|
||||||
key = "${aws_s3_bucket_object.default.id}"
|
key = "${aws_s3_bucket_object.default.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_environment" "default" {
|
resource "aws_elastic_beanstalk_environment" "default" {
|
||||||
name = "tf-test-name"
|
name = "tf-test-name-%d"
|
||||||
application = "${aws_elastic_beanstalk_application.default.name}"
|
application = "${aws_elastic_beanstalk_application.default.name}"
|
||||||
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
||||||
solution_stack_name = "64bit Amazon Linux running Python"
|
solution_stack_name = "64bit Amazon Linux running Python"
|
||||||
}
|
}
|
||||||
`, randInt)
|
`, randInt, randInt, randInt, randInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccBeanstalkEnvApplicationVersionConfigUpdate(randInt int) string {
|
func testAccBeanstalkEnvApplicationVersionConfigUpdate(randInt int) string {
|
||||||
|
@ -970,22 +970,22 @@ resource "aws_s3_bucket_object" "default" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_application" "default" {
|
resource "aws_elastic_beanstalk_application" "default" {
|
||||||
name = "tf-test-name"
|
name = "tf-test-name-%d"
|
||||||
description = "tf-test-desc"
|
description = "tf-test-desc"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_application_version" "default" {
|
resource "aws_elastic_beanstalk_application_version" "default" {
|
||||||
application = "tf-test-name"
|
application = "tf-test-name-%d"
|
||||||
name = "tf-test-version-label-v2"
|
name = "tf-test-version-label-v2"
|
||||||
bucket = "${aws_s3_bucket.default.id}"
|
bucket = "${aws_s3_bucket.default.id}"
|
||||||
key = "${aws_s3_bucket_object.default.id}"
|
key = "${aws_s3_bucket_object.default.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elastic_beanstalk_environment" "default" {
|
resource "aws_elastic_beanstalk_environment" "default" {
|
||||||
name = "tf-test-name"
|
name = "tf-test-name-%d"
|
||||||
application = "${aws_elastic_beanstalk_application.default.name}"
|
application = "${aws_elastic_beanstalk_application.default.name}"
|
||||||
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
version_label = "${aws_elastic_beanstalk_application_version.default.name}"
|
||||||
solution_stack_name = "64bit Amazon Linux running Python"
|
solution_stack_name = "64bit Amazon Linux running Python"
|
||||||
}
|
}
|
||||||
`, randInt)
|
`, randInt, randInt, randInt, randInt)
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,9 +87,19 @@ func resourceAwsIamInstanceProfile() *schema.Resource {
|
||||||
|
|
||||||
"roles": {
|
"roles": {
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Required: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ConflictsWith: []string{"role"},
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
Set: schema.HashString,
|
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()
|
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{
|
request := &iam.CreateInstanceProfileInput{
|
||||||
InstanceProfileName: aws.String(name),
|
InstanceProfileName: aws.String(name),
|
||||||
Path: aws.String(d.Get("path").(string)),
|
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 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 {
|
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 {
|
func resourceAwsIamInstanceProfileUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
iamconn := meta.(*AWSClient).iamconn
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
if !d.HasChange("roles") {
|
d.Partial(true)
|
||||||
return nil
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("roles") {
|
||||||
return instanceProfileSetRoles(d, iamconn)
|
return instanceProfileSetRoles(d, iamconn)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Partial(false)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceAwsIamInstanceProfileRead(d *schema.ResourceData, meta interface{}) error {
|
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)
|
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}
|
roles := &schema.Set{F: schema.HashString}
|
||||||
for _, role := range result.Roles {
|
for _, role := range result.Roles {
|
||||||
roles.Add(*role.RoleName)
|
roles.Add(*role.RoleName)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -37,6 +38,8 @@ func TestAccAWSIAMInstanceProfile_importBasic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccAWSIAMInstanceProfile_basic(t *testing.T) {
|
func TestAccAWSIAMInstanceProfile_basic(t *testing.T) {
|
||||||
|
var conf iam.GetInstanceProfileOutput
|
||||||
|
|
||||||
rName := acctest.RandString(5)
|
rName := acctest.RandString(5)
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
@ -44,6 +47,41 @@ func TestAccAWSIAMInstanceProfile_basic(t *testing.T) {
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccAwsIamInstanceProfileConfig(rName),
|
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)
|
}`, rName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testAccAwsIamInstanceProfileConfigMissingRole(rName string) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
resource "aws_iam_instance_profile" "test" {
|
||||||
|
name = "test-%s"
|
||||||
|
}`, rName)
|
||||||
|
}
|
||||||
|
|
||||||
func testAccAWSInstanceProfilePrefixNameConfig(rName string) string {
|
func testAccAWSInstanceProfilePrefixNameConfig(rName string) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_role" "test" {
|
resource "aws_iam_role" "test" {
|
||||||
|
@ -169,3 +214,16 @@ resource "aws_iam_instance_profile" "test" {
|
||||||
roles = ["${aws_iam_role.test.name}"]
|
roles = ["${aws_iam_role.test.name}"]
|
||||||
}`, rName)
|
}`, 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,
|
Create: resourceAwsIAMServerCertificateCreate,
|
||||||
Read: resourceAwsIAMServerCertificateRead,
|
Read: resourceAwsIAMServerCertificateRead,
|
||||||
Delete: resourceAwsIAMServerCertificateDelete,
|
Delete: resourceAwsIAMServerCertificateDelete,
|
||||||
|
Importer: &schema.ResourceImporter{
|
||||||
|
State: resourceAwsIAMServerCertificateImport,
|
||||||
|
},
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"certificate_body": &schema.Schema{
|
"certificate_body": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
StateFunc: normalizeCert,
|
StateFunc: normalizeCert,
|
||||||
},
|
},
|
||||||
|
|
||||||
"certificate_chain": &schema.Schema{
|
"certificate_chain": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
StateFunc: normalizeCert,
|
StateFunc: normalizeCert,
|
||||||
},
|
},
|
||||||
|
|
||||||
"path": &schema.Schema{
|
"path": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: "/",
|
Default: "/",
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"private_key": &schema.Schema{
|
"private_key": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
StateFunc: normalizeCert,
|
StateFunc: normalizeCert,
|
||||||
|
Sensitive: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"name": &schema.Schema{
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
@ -66,7 +70,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"name_prefix": &schema.Schema{
|
"name_prefix": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
@ -80,7 +84,7 @@ func resourceAwsIAMServerCertificate() *schema.Resource {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"arn": &schema.Schema{
|
"arn": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: 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)
|
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
|
// these values should always be present, and have a default if not set in
|
||||||
// configuration, and so safe to reference with nil checks
|
// configuration, and so safe to reference with nil checks
|
||||||
d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody))
|
d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody))
|
||||||
|
@ -196,6 +202,13 @@ func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interfac
|
||||||
return nil
|
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 {
|
func normalizeCert(cert interface{}) string {
|
||||||
if cert == nil || cert == (*string)(nil) {
|
if cert == nil || cert == (*string)(nil) {
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -2,10 +2,8 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
@ -16,14 +14,15 @@ import (
|
||||||
|
|
||||||
func TestAccAWSIAMServerCertificate_basic(t *testing.T) {
|
func TestAccAWSIAMServerCertificate_basic(t *testing.T) {
|
||||||
var cert iam.ServerCertificate
|
var cert iam.ServerCertificate
|
||||||
|
rInt := acctest.RandInt()
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccIAMServerCertConfig,
|
Config: testAccIAMServerCertConfig(rInt),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||||
testAccCheckAWSServerCertAttributes(&cert),
|
testAccCheckAWSServerCertAttributes(&cert),
|
||||||
|
@ -41,7 +40,7 @@ func TestAccAWSIAMServerCertificate_name_prefix(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccIAMServerCertConfig_random,
|
Config: testAccIAMServerCertConfig_random,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||||
|
@ -74,7 +73,7 @@ func TestAccAWSIAMServerCertificate_disappears(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccIAMServerCertConfig_random,
|
Config: testAccIAMServerCertConfig_random,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||||
|
@ -97,7 +96,7 @@ func TestAccAWSIAMServerCertificate_file(t *testing.T) {
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
{
|
||||||
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-unix-line-endings"),
|
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-unix-line-endings"),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
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"),
|
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-windows-line-endings"),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||||
|
@ -202,7 +201,8 @@ CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4
|
||||||
GnSud83VUo9G9w==
|
GnSud83VUo9G9w==
|
||||||
-----END CERTIFICATE-----`)
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
var testAccIAMServerCertConfig = fmt.Sprintf(`
|
func testAccIAMServerCertConfig(rInt int) string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
resource "aws_iam_server_certificate" "test_cert" {
|
resource "aws_iam_server_certificate" "test_cert" {
|
||||||
name = "terraform-test-cert-%d"
|
name = "terraform-test-cert-%d"
|
||||||
certificate_body = <<EOF
|
certificate_body = <<EOF
|
||||||
|
@ -257,7 +257,8 @@ dg+Sd4Wjm89UQoUUoiIcstY7FPbqfBtYKfh4RYHAHV2BwDFqzZCM
|
||||||
-----END RSA PRIVATE KEY-----
|
-----END RSA PRIVATE KEY-----
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
|
`, rInt)
|
||||||
|
}
|
||||||
|
|
||||||
var testAccIAMServerCertConfig_random = `
|
var testAccIAMServerCertConfig_random = `
|
||||||
resource "aws_iam_server_certificate" "test_cert" {
|
resource "aws_iam_server_certificate" "test_cert" {
|
||||||
|
|
|
@ -201,7 +201,8 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
for _, e := range networkAcl.Entries {
|
for _, e := range networkAcl.Entries {
|
||||||
// Skip the default rules added by AWS. They can be neither
|
// Skip the default rules added by AWS. They can be neither
|
||||||
// configured or deleted by users.
|
// configured or deleted by users.
|
||||||
if *e.RuleNumber == awsDefaultAclRuleNumber {
|
if *e.RuleNumber == awsDefaultAclRuleNumberIpv4 ||
|
||||||
|
*e.RuleNumber == awsDefaultAclRuleNumberIpv6 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +359,8 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, conn *ec2
|
||||||
// neither modified nor destroyed. They have a custom rule
|
// neither modified nor destroyed. They have a custom rule
|
||||||
// number that is out of bounds for any other rule. If we
|
// number that is out of bounds for any other rule. If we
|
||||||
// encounter it, just continue. There's no work to be done.
|
// encounter it, just continue. There's no work to be done.
|
||||||
if *remove.RuleNumber == awsDefaultAclRuleNumber {
|
if *remove.RuleNumber == awsDefaultAclRuleNumberIpv4 ||
|
||||||
|
*remove.RuleNumber == awsDefaultAclRuleNumberIpv6 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,12 @@ func resourceAwsNetworkAclRule() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: 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": {
|
"rule_action": {
|
||||||
Type: schema.TypeString,
|
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) {
|
func TestResourceAWSNetworkAclRule_validateICMPArgumentValue(t *testing.T) {
|
||||||
type testCases struct {
|
type testCases struct {
|
||||||
Value string
|
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 = `
|
const testAccAWSNetworkAclRuleIpv6Config = `
|
||||||
resource "aws_vpc" "foo" {
|
resource "aws_vpc" "foo" {
|
||||||
cidr_block = "10.3.0.0/16"
|
cidr_block = "10.3.0.0/16"
|
||||||
|
|
|
@ -242,6 +242,8 @@ func TestAccAWSNetworkAcl_ipv6Rules(t *testing.T) {
|
||||||
Config: testAccAWSNetworkAclIpv6Config,
|
Config: testAccAWSNetworkAclIpv6Config,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
|
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_network_acl.foos", "ingress.#", "1"),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"aws_network_acl.foos", "ingress.1976110835.protocol", "6"),
|
"aws_network_acl.foos", "ingress.1976110835.protocol", "6"),
|
||||||
resource.TestCheckResourceAttr(
|
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) {
|
func TestAccAWSNetworkAcl_espProtocol(t *testing.T) {
|
||||||
var networkAcl ec2.NetworkAcl
|
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 = `
|
const testAccAWSNetworkAclIngressConfig = `
|
||||||
resource "aws_vpc" "foo" {
|
resource "aws_vpc" "foo" {
|
||||||
cidr_block = "10.1.0.0/16"
|
cidr_block = "10.1.0.0/16"
|
||||||
|
|
|
@ -565,6 +565,10 @@ func resourceAwsOpsworksInstanceRead(d *schema.ResourceData, meta interface{}) e
|
||||||
for _, v := range instance.LayerIds {
|
for _, v := range instance.LayerIds {
|
||||||
layerIds = append(layerIds, *v)
|
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 {
|
if err := d.Set("layer_ids", layerIds); err != nil {
|
||||||
return fmt.Errorf("[DEBUG] Error setting layer_ids attribute: %#v, error: %#v", layerIds, err)
|
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 {
|
if v, ok := d.GetOk("layer_ids"); ok {
|
||||||
req.LayerIds = expandStringList(v.([]interface{}))
|
req.LayerIds = expandStringList(v.([]interface{}))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("os"); ok {
|
if v, ok := d.GetOk("os"); ok {
|
||||||
|
|
|
@ -3,14 +3,17 @@ package aws
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/opsworks"
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -183,6 +186,11 @@ func resourceAwsOpsworksStack() *schema.Resource {
|
||||||
Computed: true,
|
Computed: true,
|
||||||
Optional: 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 {
|
func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AWSClient).opsworksconn
|
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{
|
req := &opsworks.DescribeStacksInput{
|
||||||
StackIds: []*string{
|
StackIds: []*string{
|
||||||
|
@ -263,16 +278,53 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
|
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
|
||||||
|
|
||||||
resp, err := client.DescribeStacks(req)
|
// notFound represents the number of times we've called DescribeStacks looking
|
||||||
if err != nil {
|
// for this Stack. If it's not found in the the default region we're in, we
|
||||||
if awserr, ok := err.(awserr.Error); ok {
|
// 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 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())
|
log.Printf("[DEBUG] OpsWorks stack (%s) not found", d.Id())
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// not ResoureNotFoundException, fall through to returning error
|
||||||
}
|
}
|
||||||
return err
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Breaking stack endpoint search, found stack for (%s)", d.Id())
|
||||||
|
// Break the FOR loop
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
stack := resp.Stacks[0]
|
stack := resp.Stacks[0]
|
||||||
|
@ -309,6 +361,40 @@ func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
return nil
|
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 {
|
func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AWSClient).opsworksconn
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
@ -396,6 +482,13 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AWSClient).opsworksconn
|
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)
|
err := resourceAwsOpsworksStackValidate(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -456,6 +549,13 @@ func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*AWSClient).opsworksconn
|
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{
|
req := &opsworks.DeleteStackInput{
|
||||||
StackId: aws.String(d.Id()),
|
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
|
//// Checkers and Utilities
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ses"
|
"github.com/aws/aws-sdk-go/service/ses"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -42,7 +43,7 @@ func testAccCheckSESConfigurationSetDestroy(s *terraform.State) error {
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, element := range response.ConfigurationSets {
|
for _, element := range response.ConfigurationSets {
|
||||||
if *element.Name == "some-configuration-set" {
|
if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +78,7 @@ func testAccCheckAwsSESConfigurationSetExists(n string) resource.TestCheckFunc {
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, element := range response.ConfigurationSets {
|
for _, element := range response.ConfigurationSets {
|
||||||
if *element.Name == "some-configuration-set" {
|
if *element.Name == fmt.Sprintf("some-configuration-set-%d", escRandomInteger) {
|
||||||
found = true
|
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" {
|
resource "aws_ses_configuration_set" "test" {
|
||||||
name = "some-configuration-set"
|
name = "some-configuration-set-%d"
|
||||||
}
|
}
|
||||||
`
|
`, escRandomInteger)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/ses"
|
"github.com/aws/aws-sdk-go/service/ses"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -46,7 +47,7 @@ func testAccCheckSESEventDestinationDestroy(s *terraform.State) error {
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, element := range response.ConfigurationSets {
|
for _, element := range response.ConfigurationSets {
|
||||||
if *element.Name == "some-configuration-set" {
|
if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +82,7 @@ func testAccCheckAwsSESEventDestinationExists(n string) resource.TestCheckFunc {
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, element := range response.ConfigurationSets {
|
for _, element := range response.ConfigurationSets {
|
||||||
if *element.Name == "some-configuration-set" {
|
if *element.Name == fmt.Sprintf("some-configuration-set-%d", edRandomInteger) {
|
||||||
found = true
|
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" {
|
resource "aws_s3_bucket" "bucket" {
|
||||||
bucket = "tf-test-bucket-format"
|
bucket = "tf-test-bucket-format"
|
||||||
acl = "private"
|
acl = "private"
|
||||||
|
@ -155,7 +157,7 @@ data "aws_iam_policy_document" "fh_felivery_document" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_configuration_set" "test" {
|
resource "aws_ses_configuration_set" "test" {
|
||||||
name = "some-configuration-set"
|
name = "some-configuration-set-%d"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_event_destination" "kinesis" {
|
resource "aws_ses_event_destination" "kinesis" {
|
||||||
|
@ -182,4 +184,4 @@ resource "aws_ses_event_destination" "cloudwatch" {
|
||||||
value_source = "emailHeader"
|
value_source = "emailHeader"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`, edRandomInteger)
|
||||||
|
|
|
@ -443,7 +443,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
addHeaderAction := map[string]interface{}{
|
addHeaderAction := map[string]interface{}{
|
||||||
"header_name": *element.AddHeaderAction.HeaderName,
|
"header_name": *element.AddHeaderAction.HeaderName,
|
||||||
"header_value": *element.AddHeaderAction.HeaderValue,
|
"header_value": *element.AddHeaderAction.HeaderValue,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
addHeaderActionList = append(addHeaderActionList, addHeaderAction)
|
addHeaderActionList = append(addHeaderActionList, addHeaderAction)
|
||||||
}
|
}
|
||||||
|
@ -453,7 +453,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
"message": *element.BounceAction.Message,
|
"message": *element.BounceAction.Message,
|
||||||
"sender": *element.BounceAction.Sender,
|
"sender": *element.BounceAction.Sender,
|
||||||
"smtp_reply_code": *element.BounceAction.SmtpReplyCode,
|
"smtp_reply_code": *element.BounceAction.SmtpReplyCode,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.BounceAction.StatusCode != nil {
|
if element.BounceAction.StatusCode != nil {
|
||||||
|
@ -470,7 +470,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
if element.LambdaAction != nil {
|
if element.LambdaAction != nil {
|
||||||
lambdaAction := map[string]interface{}{
|
lambdaAction := map[string]interface{}{
|
||||||
"function_arn": *element.LambdaAction.FunctionArn,
|
"function_arn": *element.LambdaAction.FunctionArn,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.LambdaAction.InvocationType != nil {
|
if element.LambdaAction.InvocationType != nil {
|
||||||
|
@ -487,7 +487,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
if element.S3Action != nil {
|
if element.S3Action != nil {
|
||||||
s3Action := map[string]interface{}{
|
s3Action := map[string]interface{}{
|
||||||
"bucket_name": *element.S3Action.BucketName,
|
"bucket_name": *element.S3Action.BucketName,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.S3Action.KmsKeyArn != nil {
|
if element.S3Action.KmsKeyArn != nil {
|
||||||
|
@ -508,7 +508,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
if element.SNSAction != nil {
|
if element.SNSAction != nil {
|
||||||
snsAction := map[string]interface{}{
|
snsAction := map[string]interface{}{
|
||||||
"topic_arn": *element.SNSAction.TopicArn,
|
"topic_arn": *element.SNSAction.TopicArn,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
snsActionList = append(snsActionList, snsAction)
|
snsActionList = append(snsActionList, snsAction)
|
||||||
|
@ -517,7 +517,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
if element.StopAction != nil {
|
if element.StopAction != nil {
|
||||||
stopAction := map[string]interface{}{
|
stopAction := map[string]interface{}{
|
||||||
"scope": *element.StopAction.Scope,
|
"scope": *element.StopAction.Scope,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.StopAction.TopicArn != nil {
|
if element.StopAction.TopicArn != nil {
|
||||||
|
@ -530,7 +530,7 @@ func resourceAwsSesReceiptRuleRead(d *schema.ResourceData, meta interface{}) err
|
||||||
if element.WorkmailAction != nil {
|
if element.WorkmailAction != nil {
|
||||||
workmailAction := map[string]interface{}{
|
workmailAction := map[string]interface{}{
|
||||||
"organization_arn": *element.WorkmailAction.OrganizationArn,
|
"organization_arn": *element.WorkmailAction.OrganizationArn,
|
||||||
"position": i,
|
"position": i + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if element.WorkmailAction.TopicArn != nil {
|
if element.WorkmailAction.TopicArn != nil {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/service/ses"
|
"github.com/aws/aws-sdk-go/service/ses"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -111,7 +112,7 @@ func testAccCheckAwsSESReceiptRuleExists(n string) resource.TestCheckFunc {
|
||||||
|
|
||||||
params := &ses.DescribeReceiptRuleInput{
|
params := &ses.DescribeReceiptRuleInput{
|
||||||
RuleName: aws.String("basic"),
|
RuleName: aws.String("basic"),
|
||||||
RuleSetName: aws.String("test-me"),
|
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := conn.DescribeReceiptRule(params)
|
response, err := conn.DescribeReceiptRule(params)
|
||||||
|
@ -153,7 +154,7 @@ func testAccCheckAwsSESReceiptRuleOrder(n string) resource.TestCheckFunc {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
||||||
|
|
||||||
params := &ses.DescribeReceiptRuleSetInput{
|
params := &ses.DescribeReceiptRuleSetInput{
|
||||||
RuleSetName: aws.String("test-me"),
|
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := conn.DescribeReceiptRuleSet(params)
|
response, err := conn.DescribeReceiptRuleSet(params)
|
||||||
|
@ -185,8 +186,8 @@ func testAccCheckAwsSESReceiptRuleActions(n string) resource.TestCheckFunc {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
conn := testAccProvider.Meta().(*AWSClient).sesConn
|
||||||
|
|
||||||
params := &ses.DescribeReceiptRuleInput{
|
params := &ses.DescribeReceiptRuleInput{
|
||||||
RuleName: aws.String("actions"),
|
RuleName: aws.String("actions4"),
|
||||||
RuleSetName: aws.String("test-me"),
|
RuleSetName: aws.String(fmt.Sprintf("test-me-%d", srrsRandomInt)),
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := conn.DescribeReceiptRule(params)
|
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" {
|
resource "aws_ses_receipt_rule_set" "test" {
|
||||||
rule_set_name = "test-me"
|
rule_set_name = "test-me-%d"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_receipt_rule" "basic" {
|
resource "aws_ses_receipt_rule" "basic" {
|
||||||
|
@ -240,11 +242,11 @@ resource "aws_ses_receipt_rule" "basic" {
|
||||||
scan_enabled = true
|
scan_enabled = true
|
||||||
tls_policy = "Require"
|
tls_policy = "Require"
|
||||||
}
|
}
|
||||||
`
|
`, srrsRandomInt)
|
||||||
|
|
||||||
const testAccAWSSESReceiptRuleOrderConfig = `
|
var testAccAWSSESReceiptRuleOrderConfig = fmt.Sprintf(`
|
||||||
resource "aws_ses_receipt_rule_set" "test" {
|
resource "aws_ses_receipt_rule_set" "test" {
|
||||||
rule_set_name = "test-me"
|
rule_set_name = "test-me-%d"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_receipt_rule" "second" {
|
resource "aws_ses_receipt_rule" "second" {
|
||||||
|
@ -257,19 +259,19 @@ resource "aws_ses_receipt_rule" "first" {
|
||||||
name = "first"
|
name = "first"
|
||||||
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
||||||
}
|
}
|
||||||
`
|
`, srrsRandomInt)
|
||||||
|
|
||||||
const testAccAWSSESReceiptRuleActionsConfig = `
|
var testAccAWSSESReceiptRuleActionsConfig = fmt.Sprintf(`
|
||||||
resource "aws_s3_bucket" "emails" {
|
resource "aws_s3_bucket" "emails" {
|
||||||
bucket = "ses-terraform-emails"
|
bucket = "ses-terraform-emails"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_receipt_rule_set" "test" {
|
resource "aws_ses_receipt_rule_set" "test" {
|
||||||
rule_set_name = "test-me"
|
rule_set_name = "test-me-%d"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_ses_receipt_rule" "actions" {
|
resource "aws_ses_receipt_rule" "actions" {
|
||||||
name = "actions"
|
name = "actions4"
|
||||||
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
rule_set_name = "${aws_ses_receipt_rule_set.test.rule_set_name}"
|
||||||
|
|
||||||
add_header_action {
|
add_header_action {
|
||||||
|
@ -289,4 +291,4 @@ resource "aws_ses_receipt_rule" "actions" {
|
||||||
position = 2
|
position = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`, srrsRandomInt)
|
||||||
|
|
|
@ -22,6 +22,9 @@ func resourceAwsVpc() *schema.Resource {
|
||||||
State: resourceAwsVpcInstanceImport,
|
State: resourceAwsVpcInstanceImport,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
SchemaVersion: 1,
|
||||||
|
MigrateState: resourceAwsVpcMigrateState,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"cidr_block": {
|
"cidr_block": {
|
||||||
Type: schema.TypeString,
|
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
|
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{} {
|
func flattenApiGatewayThrottleSettings(settings *apigateway.ThrottleSettings) []map[string]interface{} {
|
||||||
result := make([]map[string]interface{}, 0, 1)
|
result := make([]map[string]interface{}, 0, 1)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
func TestAccAzureRMTemplateDeployment_basic(t *testing.T) {
|
func TestAccAzureRMTemplateDeployment_basic(t *testing.T) {
|
||||||
ri := acctest.RandInt()
|
ri := acctest.RandInt()
|
||||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri)
|
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicMultiple, ri, ri)
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
|
@ -31,7 +31,7 @@ func TestAccAzureRMTemplateDeployment_basic(t *testing.T) {
|
||||||
|
|
||||||
func TestAccAzureRMTemplateDeployment_disappears(t *testing.T) {
|
func TestAccAzureRMTemplateDeployment_disappears(t *testing.T) {
|
||||||
ri := acctest.RandInt()
|
ri := acctest.RandInt()
|
||||||
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicExample, ri, ri)
|
config := fmt.Sprintf(testAccAzureRMTemplateDeployment_basicSingle, ri, ri, ri)
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
|
@ -163,7 +163,47 @@ func testCheckAzureRMTemplateDeploymentDestroy(s *terraform.State) error {
|
||||||
return nil
|
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" {
|
resource "azurerm_resource_group" "test" {
|
||||||
name = "acctestRG-%d"
|
name = "acctestRG-%d"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
@ -196,7 +236,7 @@ var testAccAzureRMTemplateDeployment_basicExample = `
|
||||||
"publicIPAddressName": "[concat('myPublicIp', uniquestring(resourceGroup().id))]",
|
"publicIPAddressName": "[concat('myPublicIp', uniquestring(resourceGroup().id))]",
|
||||||
"publicIPAddressType": "Dynamic",
|
"publicIPAddressType": "Dynamic",
|
||||||
"apiVersion": "2015-06-15",
|
"apiVersion": "2015-06-15",
|
||||||
"dnsLabelPrefix": "terraform-tdacctest"
|
"dnsLabelPrefix": "[concat('terraform-tdacctest', uniquestring(resourceGroup().id))]"
|
||||||
},
|
},
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,6 +25,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
apiCheckTypeCAQL circonusCheckType = "caql"
|
apiCheckTypeCAQL circonusCheckType = "caql"
|
||||||
|
apiCheckTypeConsul circonusCheckType = "consul"
|
||||||
apiCheckTypeICMPPing circonusCheckType = "ping_icmp"
|
apiCheckTypeICMPPing circonusCheckType = "ping_icmp"
|
||||||
apiCheckTypeHTTP circonusCheckType = "http"
|
apiCheckTypeHTTP circonusCheckType = "http"
|
||||||
apiCheckTypeJSON circonusCheckType = "json"
|
apiCheckTypeJSON circonusCheckType = "json"
|
||||||
|
@ -108,15 +109,24 @@ func (c *circonusCheck) Fixup() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *circonusCheck) Validate() 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) {
|
if c.Timeout > float32(c.Period) {
|
||||||
return fmt.Errorf("Timeout (%f) can not exceed period (%d)", c.Timeout, c.Period)
|
return fmt.Errorf("Timeout (%f) can not exceed period (%d)", c.Timeout, c.Period)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check-type specific validation
|
||||||
switch apiCheckType(c.Type) {
|
switch apiCheckType(c.Type) {
|
||||||
case apiCheckTypeCloudWatchAttr:
|
case apiCheckTypeCloudWatchAttr:
|
||||||
if !(c.Period == 60 || c.Period == 300) {
|
if !(c.Period == 60 || c.Period == 300) {
|
||||||
return fmt.Errorf("Period must be either 1m or 5m for a %s check", apiCheckTypeCloudWatchAttr)
|
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
|
return nil
|
||||||
|
|
|
@ -17,6 +17,19 @@ const (
|
||||||
providerAutoTagAttr = "auto_tag"
|
providerAutoTagAttr = "auto_tag"
|
||||||
providerKeyAttr = "key"
|
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"
|
defaultCheckJSONMethod = "GET"
|
||||||
defaultCheckJSONPort = "443"
|
defaultCheckJSONPort = "443"
|
||||||
defaultCheckJSONVersion = "1.1"
|
defaultCheckJSONVersion = "1.1"
|
||||||
|
|
|
@ -33,21 +33,22 @@ const (
|
||||||
checkCAQLAttr = "caql"
|
checkCAQLAttr = "caql"
|
||||||
checkCloudWatchAttr = "cloudwatch"
|
checkCloudWatchAttr = "cloudwatch"
|
||||||
checkCollectorAttr = "collector"
|
checkCollectorAttr = "collector"
|
||||||
|
checkConsulAttr = "consul"
|
||||||
checkHTTPAttr = "http"
|
checkHTTPAttr = "http"
|
||||||
checkHTTPTrapAttr = "httptrap"
|
checkHTTPTrapAttr = "httptrap"
|
||||||
checkICMPPingAttr = "icmp_ping"
|
checkICMPPingAttr = "icmp_ping"
|
||||||
checkJSONAttr = "json"
|
checkJSONAttr = "json"
|
||||||
|
checkMetricAttr = "metric"
|
||||||
checkMetricLimitAttr = "metric_limit"
|
checkMetricLimitAttr = "metric_limit"
|
||||||
checkMySQLAttr = "mysql"
|
checkMySQLAttr = "mysql"
|
||||||
checkNameAttr = "name"
|
checkNameAttr = "name"
|
||||||
checkNotesAttr = "notes"
|
checkNotesAttr = "notes"
|
||||||
checkPeriodAttr = "period"
|
checkPeriodAttr = "period"
|
||||||
checkPostgreSQLAttr = "postgresql"
|
checkPostgreSQLAttr = "postgresql"
|
||||||
checkMetricAttr = "metric"
|
|
||||||
checkStatsdAttr = "statsd"
|
checkStatsdAttr = "statsd"
|
||||||
|
checkTCPAttr = "tcp"
|
||||||
checkTagsAttr = "tags"
|
checkTagsAttr = "tags"
|
||||||
checkTargetAttr = "target"
|
checkTargetAttr = "target"
|
||||||
checkTCPAttr = "tcp"
|
|
||||||
checkTimeoutAttr = "timeout"
|
checkTimeoutAttr = "timeout"
|
||||||
checkTypeAttr = "type"
|
checkTypeAttr = "type"
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ const (
|
||||||
// Circonus API constants from their API endpoints
|
// Circonus API constants from their API endpoints
|
||||||
apiCheckTypeCAQLAttr apiCheckType = "caql"
|
apiCheckTypeCAQLAttr apiCheckType = "caql"
|
||||||
apiCheckTypeCloudWatchAttr apiCheckType = "cloudwatch"
|
apiCheckTypeCloudWatchAttr apiCheckType = "cloudwatch"
|
||||||
|
apiCheckTypeConsulAttr apiCheckType = "consul"
|
||||||
apiCheckTypeHTTPAttr apiCheckType = "http"
|
apiCheckTypeHTTPAttr apiCheckType = "http"
|
||||||
apiCheckTypeHTTPTrapAttr apiCheckType = "httptrap"
|
apiCheckTypeHTTPTrapAttr apiCheckType = "httptrap"
|
||||||
apiCheckTypeICMPPingAttr apiCheckType = "ping_icmp"
|
apiCheckTypeICMPPingAttr apiCheckType = "ping_icmp"
|
||||||
|
@ -90,6 +92,7 @@ var checkDescriptions = attrDescrs{
|
||||||
checkCAQLAttr: "CAQL check configuration",
|
checkCAQLAttr: "CAQL check configuration",
|
||||||
checkCloudWatchAttr: "CloudWatch check configuration",
|
checkCloudWatchAttr: "CloudWatch check configuration",
|
||||||
checkCollectorAttr: "The collector(s) that are responsible for gathering the metrics",
|
checkCollectorAttr: "The collector(s) that are responsible for gathering the metrics",
|
||||||
|
checkConsulAttr: "Consul check configuration",
|
||||||
checkHTTPAttr: "HTTP check configuration",
|
checkHTTPAttr: "HTTP check configuration",
|
||||||
checkHTTPTrapAttr: "HTTP Trap check configuration",
|
checkHTTPTrapAttr: "HTTP Trap check configuration",
|
||||||
checkICMPPingAttr: "ICMP ping check configuration",
|
checkICMPPingAttr: "ICMP ping check configuration",
|
||||||
|
@ -157,6 +160,7 @@ func resourceCheck() *schema.Resource {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
checkConsulAttr: schemaCheckConsul,
|
||||||
checkHTTPAttr: schemaCheckHTTP,
|
checkHTTPAttr: schemaCheckHTTP,
|
||||||
checkHTTPTrapAttr: schemaCheckHTTPTrap,
|
checkHTTPTrapAttr: schemaCheckHTTPTrap,
|
||||||
checkJSONAttr: schemaCheckJSON,
|
checkJSONAttr: schemaCheckJSON,
|
||||||
|
@ -577,6 +581,7 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error {
|
||||||
checkTypeParseMap := map[string]func(*circonusCheck, interfaceList) error{
|
checkTypeParseMap := map[string]func(*circonusCheck, interfaceList) error{
|
||||||
checkCAQLAttr: checkConfigToAPICAQL,
|
checkCAQLAttr: checkConfigToAPICAQL,
|
||||||
checkCloudWatchAttr: checkConfigToAPICloudWatch,
|
checkCloudWatchAttr: checkConfigToAPICloudWatch,
|
||||||
|
checkConsulAttr: checkConfigToAPIConsul,
|
||||||
checkHTTPAttr: checkConfigToAPIHTTP,
|
checkHTTPAttr: checkConfigToAPIHTTP,
|
||||||
checkHTTPTrapAttr: checkConfigToAPIHTTPTrap,
|
checkHTTPTrapAttr: checkConfigToAPIHTTPTrap,
|
||||||
checkICMPPingAttr: checkConfigToAPIICMPPing,
|
checkICMPPingAttr: checkConfigToAPIICMPPing,
|
||||||
|
@ -589,9 +594,18 @@ func checkConfigToAPI(c *circonusCheck, d *schema.ResourceData) error {
|
||||||
|
|
||||||
for checkType, fn := range checkTypeParseMap {
|
for checkType, fn := range checkTypeParseMap {
|
||||||
if listRaw, found := d.GetOk(checkType); found {
|
if listRaw, found := d.GetOk(checkType); found {
|
||||||
if err := fn(c, listRaw.(*schema.Set).List()); err != nil {
|
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)
|
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{
|
checkTypeConfigHandlers := map[apiCheckType]func(*circonusCheck, *schema.ResourceData) error{
|
||||||
apiCheckTypeCAQLAttr: checkAPIToStateCAQL,
|
apiCheckTypeCAQLAttr: checkAPIToStateCAQL,
|
||||||
apiCheckTypeCloudWatchAttr: checkAPIToStateCloudWatch,
|
apiCheckTypeCloudWatchAttr: checkAPIToStateCloudWatch,
|
||||||
|
apiCheckTypeConsulAttr: checkAPIToStateConsul,
|
||||||
apiCheckTypeHTTPAttr: checkAPIToStateHTTP,
|
apiCheckTypeHTTPAttr: checkAPIToStateHTTP,
|
||||||
apiCheckTypeHTTPTrapAttr: checkAPIToStateHTTPTrap,
|
apiCheckTypeHTTPTrapAttr: checkAPIToStateHTTPTrap,
|
||||||
apiCheckTypeICMPPingAttr: checkAPIToStateICMPPing,
|
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 {
|
if len(c.Target) == 0 {
|
||||||
c.Target = hostInfo[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 {
|
if v, found := httpConfig[checkHTTPVersionAttr]; found {
|
||||||
|
|
|
@ -355,6 +355,10 @@ func checkConfigToAPIJSON(c *circonusCheck, l interfaceList) error {
|
||||||
if len(c.Target) == 0 {
|
if len(c.Target) == 0 {
|
||||||
c.Target = hostInfo[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 {
|
if v, found := jsonConfig[checkJSONVersionAttr]; found {
|
||||||
|
|
|
@ -314,6 +314,7 @@ type urlParseFlags int
|
||||||
const (
|
const (
|
||||||
urlIsAbs urlParseFlags = 1 << iota
|
urlIsAbs urlParseFlags = 1 << iota
|
||||||
urlOptional
|
urlOptional
|
||||||
|
urlWithoutPath
|
||||||
urlWithoutPort
|
urlWithoutPort
|
||||||
urlWithoutSchema
|
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)))
|
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 {
|
if checkFlags&urlWithoutPort != 0 {
|
||||||
hostParts := strings.SplitN(u.Host, ":", 2)
|
hostParts := strings.SplitN(u.Host, ":", 2)
|
||||||
if len(hostParts) != 1 {
|
if len(hostParts) != 1 {
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (c *Config) Client() (*consulapi.Client, error) {
|
||||||
} else {
|
} else {
|
||||||
username = c.HttpAuth
|
username = c.HttpAuth
|
||||||
}
|
}
|
||||||
config.HttpAuth = &consulapi.HttpBasicAuth{username, password}
|
config.HttpAuth = &consulapi.HttpBasicAuth{Username: username, Password: password}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Token != "" {
|
if c.Token != "" {
|
||||||
|
|
|
@ -3,11 +3,17 @@ package digitalocean
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) {
|
func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) {
|
||||||
resourceName := "digitalocean_ssh_key.foobar"
|
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{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
@ -15,7 +21,7 @@ func TestAccDigitalOceanSSHKey_importBasic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
||||||
Steps: []resource.TestStep{
|
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)
|
return fmt.Errorf("invalid droplet id: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("size") {
|
resize_disk := d.Get("resize_disk").(bool)
|
||||||
oldSize, newSize := d.GetChange("size")
|
if d.HasChange("size") || d.HasChange("resize_disk") && resize_disk {
|
||||||
|
newSize := d.Get("size")
|
||||||
|
|
||||||
_, _, err = client.DropletActions.PowerOff(id)
|
_, _, err = client.DropletActions.PowerOff(id)
|
||||||
if err != nil && !strings.Contains(err.Error(), "Droplet is already powered off") {
|
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 the droplet
|
||||||
resize_disk := d.Get("resize_disk")
|
action, _, err := client.DropletActions.Resize(id, newSize.(string), 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)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
newErr := powerOnAndWait(d, meta)
|
newErr := powerOnAndWait(d, meta)
|
||||||
if newErr != nil {
|
if newErr != nil {
|
||||||
|
@ -356,11 +351,8 @@ func resourceDigitalOceanDropletUpdate(d *schema.ResourceData, meta interface{})
|
||||||
"Error resizing droplet (%s): %s", d.Id(), err)
|
"Error resizing droplet (%s): %s", d.Id(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the size to change
|
// Wait for the resize action to complete.
|
||||||
_, err = WaitForDropletAttribute(
|
if err := waitForAction(client, action); err != nil {
|
||||||
d, newSize.(string), []string{"", oldSize.(string)}, "size", meta)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
newErr := powerOnAndWait(d, meta)
|
newErr := powerOnAndWait(d, meta)
|
||||||
if newErr != nil {
|
if newErr != nil {
|
||||||
return fmt.Errorf(
|
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) {
|
func TestAccDigitalOceanDroplet_UpdateUserData(t *testing.T) {
|
||||||
var afterCreate, afterUpdate godo.Droplet
|
var afterCreate, afterUpdate godo.Droplet
|
||||||
rInt := acctest.RandInt()
|
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 {
|
func testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(droplet *godo.Droplet) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
@ -492,6 +557,19 @@ resource "digitalocean_droplet" "foobar" {
|
||||||
`, rInt)
|
`, 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
|
// IPV6 only in singapore
|
||||||
func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string {
|
func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string {
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
|
@ -505,3 +583,5 @@ resource "digitalocean_droplet" "foobar" {
|
||||||
}
|
}
|
||||||
`, rInt)
|
`, rInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR`
|
||||||
|
|
|
@ -3,16 +3,21 @@ package digitalocean
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/digitalocean/godo"
|
"github.com/digitalocean/godo"
|
||||||
|
"github.com/hashicorp/terraform/helper/acctest"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccDigitalOceanSSHKey_Basic(t *testing.T) {
|
func TestAccDigitalOceanSSHKey_Basic(t *testing.T) {
|
||||||
var key godo.Key
|
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{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
@ -20,14 +25,13 @@ func TestAccDigitalOceanSSHKey_Basic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
{
|
{
|
||||||
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(testAccValidPublicKey),
|
Config: testAccCheckDigitalOceanSSHKeyConfig_basic(rInt, publicKeyMaterial),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckDigitalOceanSSHKeyExists("digitalocean_ssh_key.foobar", &key),
|
testAccCheckDigitalOceanSSHKeyExists("digitalocean_ssh_key.foobar", &key),
|
||||||
testAccCheckDigitalOceanSSHKeyAttributes(&key),
|
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"digitalocean_ssh_key.foobar", "name", "foobar"),
|
"digitalocean_ssh_key.foobar", "name", fmt.Sprintf("foobar-%d", rInt)),
|
||||||
resource.TestCheckResourceAttr(
|
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
|
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 {
|
func testAccCheckDigitalOceanSSHKeyExists(n string, key *godo.Key) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
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(`
|
return fmt.Sprintf(`
|
||||||
resource "digitalocean_ssh_key" "foobar" {
|
resource "digitalocean_ssh_key" "foobar" {
|
||||||
name = "foobar"
|
name = "foobar-%d"
|
||||||
public_key = "%s"
|
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{}{
|
nb := map[string]interface{}{
|
||||||
"name": b.Name,
|
"name": b.Name,
|
||||||
"address": b.Address,
|
"address": b.Address,
|
||||||
"auto_loadbalance": gofastly.CBool(b.AutoLoadbalance),
|
"auto_loadbalance": b.AutoLoadbalance,
|
||||||
"between_bytes_timeout": int(b.BetweenBytesTimeout),
|
"between_bytes_timeout": int(b.BetweenBytesTimeout),
|
||||||
"connect_timeout": int(b.ConnectTimeout),
|
"connect_timeout": int(b.ConnectTimeout),
|
||||||
"error_threshold": int(b.ErrorThreshold),
|
"error_threshold": int(b.ErrorThreshold),
|
||||||
|
@ -1930,7 +1930,7 @@ func flattenBackends(backendList []*gofastly.Backend) []map[string]interface{} {
|
||||||
"max_conn": int(b.MaxConn),
|
"max_conn": int(b.MaxConn),
|
||||||
"port": int(b.Port),
|
"port": int(b.Port),
|
||||||
"shield": b.Shield,
|
"shield": b.Shield,
|
||||||
"ssl_check_cert": gofastly.CBool(b.SSLCheckCert),
|
"ssl_check_cert": b.SSLCheckCert,
|
||||||
"ssl_hostname": b.SSLHostname,
|
"ssl_hostname": b.SSLHostname,
|
||||||
"ssl_cert_hostname": b.SSLCertHostname,
|
"ssl_cert_hostname": b.SSLCertHostname,
|
||||||
"ssl_sni_hostname": b.SSLSNIHostname,
|
"ssl_sni_hostname": b.SSLSNIHostname,
|
||||||
|
|
|
@ -84,14 +84,14 @@ func TestResourceFastlyFlattenBackend(t *testing.T) {
|
||||||
"name": "test.notexample.com",
|
"name": "test.notexample.com",
|
||||||
"address": "www.notexample.com",
|
"address": "www.notexample.com",
|
||||||
"port": 80,
|
"port": 80,
|
||||||
"auto_loadbalance": gofastly.CBool(true),
|
"auto_loadbalance": true,
|
||||||
"between_bytes_timeout": 10000,
|
"between_bytes_timeout": 10000,
|
||||||
"connect_timeout": 1000,
|
"connect_timeout": 1000,
|
||||||
"error_threshold": 0,
|
"error_threshold": 0,
|
||||||
"first_byte_timeout": 15000,
|
"first_byte_timeout": 15000,
|
||||||
"max_conn": 200,
|
"max_conn": 200,
|
||||||
"request_condition": "",
|
"request_condition": "",
|
||||||
"ssl_check_cert": gofastly.CBool(true),
|
"ssl_check_cert": true,
|
||||||
"ssl_hostname": "",
|
"ssl_hostname": "",
|
||||||
"ssl_cert_hostname": "",
|
"ssl_cert_hostname": "",
|
||||||
"ssl_sni_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
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
pkgApi "k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/errors"
|
"k8s.io/kubernetes/pkg/api/errors"
|
||||||
api "k8s.io/kubernetes/pkg/api/v1"
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
kubernetes "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
|
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 {
|
func resourceKubernetesConfigMapUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
conn := meta.(*kubernetes.Clientset)
|
conn := meta.(*kubernetes.Clientset)
|
||||||
|
|
||||||
metadata := expandMetadata(d.Get("metadata").([]interface{}))
|
|
||||||
namespace, name := idParts(d.Id())
|
namespace, name := idParts(d.Id())
|
||||||
// This is necessary in case the name is generated
|
|
||||||
metadata.Name = name
|
|
||||||
|
|
||||||
cfgMap := api.ConfigMap{
|
ops := patchMetadata("metadata.0.", "/metadata/", d)
|
||||||
ObjectMeta: metadata,
|
if d.HasChange("data") {
|
||||||
Data: expandStringMap(d.Get("data").(map[string]interface{})),
|
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)
|
data, err := ops.MarshalJSON()
|
||||||
out, err := conn.CoreV1().ConfigMaps(namespace).Update(&cfgMap)
|
|
||||||
if err != nil {
|
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)
|
log.Printf("[INFO] Submitted updated config map: %#v", out)
|
||||||
d.SetId(buildId(out.ObjectMeta))
|
d.SetId(buildId(out.ObjectMeta))
|
||||||
|
|
|
@ -2,8 +2,10 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
api "k8s.io/kubernetes/pkg/api/v1"
|
api "k8s.io/kubernetes/pkg/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,6 +41,21 @@ func expandMetadata(in []interface{}) api.ObjectMeta {
|
||||||
return meta
|
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 {
|
func expandStringMap(m map[string]interface{}) map[string]string {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
for k, v := range m {
|
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{} {
|
func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} {
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
m["annotations"] = meta.Annotations
|
m["annotations"] = filterAnnotations(meta.Annotations)
|
||||||
m["generate_name"] = meta.GenerateName
|
m["generate_name"] = meta.GenerateName
|
||||||
m["labels"] = meta.Labels
|
m["labels"] = meta.Labels
|
||||||
m["name"] = meta.Name
|
m["name"] = meta.Name
|
||||||
|
@ -64,3 +81,21 @@ func flattenMetadata(meta api.ObjectMeta) []map[string]interface{} {
|
||||||
|
|
||||||
return []map[string]interface{}{m}
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,6 +82,7 @@ func resourceComputeInstanceV2() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: false,
|
ForceNew: false,
|
||||||
|
Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead",
|
||||||
},
|
},
|
||||||
"user_data": &schema.Schema{
|
"user_data": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -153,6 +154,7 @@ func resourceComputeInstanceV2() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
|
Deprecated: "Use the openstack_compute_floatingip_associate_v2 resource instead",
|
||||||
},
|
},
|
||||||
"mac": &schema.Schema{
|
"mac": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -245,6 +247,7 @@ func resourceComputeInstanceV2() *schema.Resource {
|
||||||
"volume": &schema.Schema{
|
"volume": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Deprecated: "Use block_device or openstack_compute_volume_attach_v2 instead",
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"id": &schema.Schema{
|
"id": &schema.Schema{
|
||||||
|
@ -335,6 +338,10 @@ func resourceComputeInstanceV2() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Default: false,
|
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{}
|
secGrpNames := []string{}
|
||||||
for _, sg := range server.SecurityGroups {
|
for _, sg := range server.SecurityGroups {
|
||||||
|
|
|
@ -29,6 +29,8 @@ func TestAccComputeV2Instance_basic(t *testing.T) {
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance),
|
testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance),
|
||||||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"openstack_compute_instance_v2.instance_1", "all_metadata.foo", "bar"),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"openstack_compute_instance_v2.instance_1", "availability_zone", "nova"),
|
"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),
|
testAccCheckComputeV2InstanceExists("openstack_compute_instance_v2.instance_1", &instance),
|
||||||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||||
testAccCheckComputeV2InstanceMetadata(&instance, "abc", "def"),
|
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{
|
resource.TestStep{
|
||||||
|
@ -616,6 +622,10 @@ func TestAccComputeV2Instance_metadataRemove(t *testing.T) {
|
||||||
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
testAccCheckComputeV2InstanceMetadata(&instance, "foo", "bar"),
|
||||||
testAccCheckComputeV2InstanceMetadata(&instance, "ghi", "jkl"),
|
testAccCheckComputeV2InstanceMetadata(&instance, "ghi", "jkl"),
|
||||||
testAccCheckComputeV2InstanceNoMetadataKey(&instance, "abc"),
|
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") {
|
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)
|
oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set)
|
||||||
monitorsToAdd := newMIDsSet.Difference(oldMIDsSet)
|
monitorsToAdd := newMIDsSet.Difference(oldMIDsSet)
|
||||||
monitorsToRemove := oldMIDsSet.Difference(newMIDsSet)
|
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 {
|
func testAccCheckLBV1PoolDestroy(s *terraform.State) error {
|
||||||
config := testAccProvider.Meta().(*Config)
|
config := testAccProvider.Meta().(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
|
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)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err)
|
return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] pool ID: %s", pool.ID)
|
log.Printf("[INFO] pool ID: %s", pool.ID)
|
||||||
|
|
||||||
log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 pool (%s) to become available.", 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,
|
Computed: true,
|
||||||
},
|
},
|
||||||
"fixed_ip": &schema.Schema{
|
"fixed_ip": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeList,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: false,
|
ForceNew: false,
|
||||||
Computed: true,
|
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"subnet_id": &schema.Schema{
|
"subnet_id": &schema.Schema{
|
||||||
|
@ -98,7 +97,6 @@ func resourceNetworkingPortV2() *schema.Resource {
|
||||||
"ip_address": &schema.Schema{
|
"ip_address": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -128,6 +126,11 @@ func resourceNetworkingPortV2() *schema.Resource {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: 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("security_group_ids", p.SecurityGroups)
|
||||||
d.Set("device_id", p.DeviceID)
|
d.Set("device_id", p.DeviceID)
|
||||||
|
|
||||||
// Convert FixedIPs to list of map
|
// Create a slice of all returned Fixed IPs.
|
||||||
var ips []map[string]interface{}
|
// This will be in the order returned by the API,
|
||||||
|
// which is usually alpha-numeric.
|
||||||
|
var ips []string
|
||||||
for _, ipObject := range p.FixedIPs {
|
for _, ipObject := range p.FixedIPs {
|
||||||
ip := make(map[string]interface{})
|
ips = append(ips, ipObject.IPAddress)
|
||||||
ip["subnet_id"] = ipObject.SubnetID
|
|
||||||
ip["ip_address"] = ipObject.IPAddress
|
|
||||||
ips = append(ips, ip)
|
|
||||||
}
|
}
|
||||||
d.Set("fixed_ip", ips)
|
d.Set("all_fixed_ips", ips)
|
||||||
|
|
||||||
// Convert AllowedAddressPairs to list of map
|
// Convert AllowedAddressPairs to list of map
|
||||||
var pairs []map[string]interface{}
|
var pairs []map[string]interface{}
|
||||||
|
@ -309,7 +311,7 @@ func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
|
func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
|
||||||
rawIP := d.Get("fixed_ip").(*schema.Set).List()
|
rawIP := d.Get("fixed_ip").([]interface{})
|
||||||
|
|
||||||
if len(rawIP) == 0 {
|
if len(rawIP) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue