mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
7fbc7d005a
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -1,4 +0,0 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
*.go eol=lf
|
@ -4,8 +4,10 @@ language: go
|
||||
go:
|
||||
- 1.8
|
||||
|
||||
# add TF_CONSUL_TEST=1 to run consul tests
|
||||
# they were causing timouts in travis
|
||||
env:
|
||||
- CONSUL_VERSION=0.7.5 TF_CONSUL_TEST=1 GOMAXPROCS=4
|
||||
- CONSUL_VERSION=0.7.5 GOMAXPROCS=4
|
||||
|
||||
# Fetch consul for the backend and provider tests
|
||||
before_install:
|
||||
|
181
CHANGELOG.md
181
CHANGELOG.md
@ -1,24 +1,179 @@
|
||||
## 0.9.2 (unreleased)
|
||||
## 0.9.3 (unreleased)
|
||||
|
||||
FEATURES:
|
||||
|
||||
* **New Resource:** `aws_api_gateway_usage_plan` [GH-12542]
|
||||
* **New Resource:** `aws_iam_openid_connect_provider` [GH-13456]
|
||||
* **New Resource:** `aws_lightsail_static_ip` [GH-13175]
|
||||
* **New Resource:** `aws_lightsail_static_ip_attachment` [GH-13207]
|
||||
* **New Resource:** `aws_ses_domain_identity` [GH-13098]
|
||||
* **New Resource:** `azurerm_managed_disk` [GH-12455]
|
||||
* **New Resource:** `kubernetes_persistent_volume` [GH-13277]
|
||||
* **New Resource:** `kubernetes_secret` [GH-12960]
|
||||
* **New Data Source:** `aws_iam_role` [GH-13213]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* core: add `-lock-timeout` option, which will block and retry locks for the given duration [GH-13262]
|
||||
* core: new `chomp` interpolation function which returns the given string with any trailing newline characters removed [GH-13419]
|
||||
* backend/remote-state: Add support for assume role extensions to s3 backend [GH-13236]
|
||||
* config: New interpolation functions `basename` and `dirname`, for file path manipulation [GH-13080]
|
||||
* helper/resource: Allow unknown "pending" states [GH-13099]
|
||||
* command/hook_ui: Increase max length of state IDs from 20 to 80 [GH-13317]
|
||||
* provider/aws: Add support to set iam_role_arn on cloudformation Stack [GH-12547]
|
||||
* provider/aws: Support priority and listener_arn update of alb_listener_rule [GH-13125]
|
||||
* provider/aws: Deprecate roles in favour of role in iam_instance_profile [GH-13130]
|
||||
* provider/aws: Make alb_target_group_attachment port optional [GH-13139]
|
||||
* provider/aws: `aws_api_gateway_domain_name` `certificate_private_key` field marked as sensitive [GH-13147]
|
||||
* provider/aws: `aws_directory_service_directory` `password` field marked as sensitive [GH-13147]
|
||||
* provider/aws: `aws_kinesis_firehose_delivery_stream` `password` field marked as sensitive [GH-13147]
|
||||
* provider/aws: `aws_opsworks_application` `app_source.0.password` & `ssl_configuration.0.private_key` fields marked as sensitive [GH-13147]
|
||||
* provider/aws: `aws_opsworks_stack` `custom_cookbooks_source.0.password` field marked as sensitive [GH-13147]
|
||||
* provider/aws: Support the ability to enable / disable ipv6 support in VPC [GH-12527]
|
||||
* provider/aws: Added API Gateway integration update [GH-13249]
|
||||
* provider/aws: Add `identifier` | `name_prefix` to RDS resources [GH-13232]
|
||||
* provider/aws: Validate `aws_ecs_task_definition.container_definitions` [GH-12161]
|
||||
* provider/aws: Update caller_identity data source [GH-13092]
|
||||
* provider/aws: `aws_subnet_ids` data source for getting a list of subnet ids matching certain criteria [GH-13188]
|
||||
* provider/aws: Support ip_address_type for aws_alb [GH-13227]
|
||||
* provider/aws: Migrate `aws_dms_*` resources away from AWS waiters [GH-13291]
|
||||
* provider/aws: Add support for treat_missing_data to cloudwatch_metric_alarm [GH-13358]
|
||||
* provider/aws: Add support for evaluate_low_sample_count_percentiles to cloudwatch_metric_alarm [GH-13371]
|
||||
* provider/aws: Add `name_prefix` to `aws_alb_target_group` [GH-13442]
|
||||
* provider/aws: Add support for EMR clusters to aws_appautoscaling_target [GH-13368]
|
||||
* provider/bitbucket: Improved error handling [GH-13390]
|
||||
* provider/cloudstack: Do not force a new resource when updating `cloudstack_loadbalancer_rule` members [GH-11786]
|
||||
* provider/fastly: Add support for Sumologic logging [GH-12541]
|
||||
* provider/github: Handle the case when issue labels already exist [GH-13182]
|
||||
* provider/google: Mark `google_container_cluster`'s `client_key` & `password` inside `master_auth` as sensitive [GH-13148]
|
||||
* provider/kubernetes: Allow defining custom config context [GH-12958]
|
||||
* provider/openstack: Add support for 'value_specs' options to `openstack_compute_servergroup_v2` [GH-13380]
|
||||
* provider/statuscake: Add support for StatusCake TriggerRate field [GH-13340]
|
||||
* provider/triton: Move to joyent/triton-go [GH-13225]
|
||||
* provisioner/chef: Make sure we add new Chef-Vault clients as clients [GH-13525]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* core: Escaped interpolation-like sequences (like `$${foo}`) now permitted in variable defaults [GH-13137]
|
||||
* core: Fix strange issues with computed values in provider configuration that were worked around with `-input=false` [GH-11264], [GH-13264]
|
||||
* core: Fix crash when providing nested maps as variable values in a `module` block [GH-13343]
|
||||
* core: `connection` block attributes are now subject to basic validation of attribute names during validate walk [GH-13400]
|
||||
* provider/aws: Add Support for maintenance_window and back_window to rds_cluster_instance [GH-13134]
|
||||
* provider/aws: Increase timeout for AMI registration [GH-13159]
|
||||
* provider/aws: Increase timeouts for ELB [GH-13161]
|
||||
* provider/aws: `volume_type` of `aws_elasticsearch_domain.0.ebs_options` marked as `Computed` which prevents spurious diffs [GH-13160]
|
||||
* provider/aws: Don't set DBName on `aws_db_instance` from snapshot [GH-13140]
|
||||
* provider/aws: Add DiffSuppression to aws_ecs_service placement_strategies [GH-13220]
|
||||
* provider/aws: Refresh aws_alb_target_group stickiness on manual updates [GH-13199]
|
||||
* provider/aws: Preserve default retain_on_delete in cloudfront import [GH-13209]
|
||||
* provider/aws: Refresh aws_alb_target_group tags [GH-13200]
|
||||
* provider/aws: Set aws_vpn_connection to recreate when in deleted state [GH-13204]
|
||||
* provider/aws: Wait for aws_opsworks_instance to be running when it's specified [GH-13218]
|
||||
* provider/aws: Handle `aws_lambda_function` missing s3 key error [GH-10960]
|
||||
* provider/aws: Set stickiness to computed in alb_target_group [GH-13278]
|
||||
* provider/aws: Increase timeout for deploying `cloudfront_distribution` from 40 to 70 mins [GH-13319]
|
||||
* provider/aws: Increase AMI retry timeouts [GH-13324]
|
||||
* provider/aws: Increase subnet deletion timeout [GH-13356]
|
||||
* provider/aws: Increase launch_configuration creation timeout [GH-13357]
|
||||
* provider/aws: Increase Beanstalk env 'ready' timeout [GH-13359]
|
||||
* provider/aws: Raise timeout for deleting APIG REST API [GH-13414]
|
||||
* provider/aws: Raise timeout for attaching/detaching VPN Gateway [GH-13457]
|
||||
* provider/aws: Recreate opsworks_stack on change of service_role_arn [GH-13325]
|
||||
* provider/aws: Fix KMS Key reading with Exists method [GH-13348]
|
||||
* provider/aws: Fix DynamoDB issues about GSIs indexes [GH-13256]
|
||||
* provider/aws: Fix `aws_s3_bucket` drift detection of logging options [GH-13281]
|
||||
* provider/aws: Update ElasticTranscoderPreset to have default for MaxFrameRate [GH-13422]
|
||||
* provider/aws: Fix aws_ami_launch_permission refresh when AMI disappears [GH-13469]
|
||||
* provider/aws: Add support for updating SSM documents [GH-13491]
|
||||
* provider/azurerm: Network Security Group - ignoring protocol casing at Import time [GH-13153]
|
||||
* provider/azurerm: Fix crash when importing Local Network Gateways [GH-13261]
|
||||
* provider/azurerm: Defaulting the value of `duplicate_detection_history_time_window` for `azurerm_servicebus_topic` [GH-13223]
|
||||
* provider/bitbucket: Fixed issue where provider would fail with an "EOF" error on some operations [GH-13390]
|
||||
* provider/kubernetes: Use PATCH to update namespace [GH-13114]
|
||||
* provider/ns1: No splitting answer on SPF records. [GH-13260]
|
||||
* provider/openstack: Refresh volume_attachment from state if NotFound [GH-13342]
|
||||
* provider/openstack: Add SOFT_DELETED to delete status [GH-13444]
|
||||
* provider/profitbricks: Changed output type of ips variable of ip_block ProfitBricks resource [GH-13290]
|
||||
|
||||
## 0.9.2 (March 28, 2017)
|
||||
|
||||
BACKWARDS IMCOMPATIBILITIES / NOTES:
|
||||
|
||||
* provider/openstack: Port Fixed IPs are able to be read again using the original numerical notation. However, Fixed IP configurations which are obtaining addresses via DHCP must now use the `all_fixed_ips` attribute to reference the returned IP address.
|
||||
* Environment names must be safe to use as a URL path segment without escaping, and is enforced by the CLI.
|
||||
|
||||
FEATURES:
|
||||
|
||||
* **New Resource:** `alicloud_db_instance` ([#12913](https://github.com/hashicorp/terraform/issues/12913))
|
||||
* **New Resource:** `aws_api_gateway_usage_plan` ([#12542](https://github.com/hashicorp/terraform/issues/12542))
|
||||
* **New Resource:** `aws_api_gateway_usage_plan_key` ([#12851](https://github.com/hashicorp/terraform/issues/12851))
|
||||
* **New Resource:** `github_repository_webhook` ([#12924](https://github.com/hashicorp/terraform/issues/12924))
|
||||
* **New Resource:** `random_pet` ([#12903](https://github.com/hashicorp/terraform/issues/12903))
|
||||
* **New Interpolation:** `substr` ([#12870](https://github.com/hashicorp/terraform/issues/12870))
|
||||
* **S3 Environments:** The S3 remote state backend now supports named environments
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* provider/aws: Added support for EMR AutoScalingRole [GH-12823]
|
||||
* provider/dnsimple: Allow dnsimple_record.priority attribute to be set [GH-12843]
|
||||
* provider/openstack: Adding Timeouts to Blockstorage Resources [GH-12862]
|
||||
* provider/openstack: Adding Timeouts to FWaaS v1 Resources [GH-12863]
|
||||
* provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources [GH-12865]
|
||||
* provider/openstack: Adding Timeouts to Network Resources [GH-12866]
|
||||
* provider/openstack: Adding Timeouts to LBaaS v1 Resources [GH-12867]
|
||||
* provider/pagerduty: Validate credentials [GH-12854]
|
||||
* core: fix interpolation error when referencing computed values from an `aws_instance` `cidr_block` ([#13046](https://github.com/hashicorp/terraform/issues/13046))
|
||||
* core: fix `ignore_changes` causing fields to be removed during apply ([#12897](https://github.com/hashicorp/terraform/issues/12897))
|
||||
* core: add `-force-copy` option to `terraform init` to supress prompts for copying state ([#12939](https://github.com/hashicorp/terraform/issues/12939))
|
||||
* helper/acctest: Add NewSSHKeyPair function ([#12894](https://github.com/hashicorp/terraform/issues/12894))
|
||||
* provider/alicloud: simplify validators ([#12982](https://github.com/hashicorp/terraform/issues/12982))
|
||||
* provider/aws: Added support for EMR AutoScalingRole ([#12823](https://github.com/hashicorp/terraform/issues/12823))
|
||||
* provider/aws: Add `name_prefix` to `aws_autoscaling_group` and `aws_elb` resources ([#12629](https://github.com/hashicorp/terraform/issues/12629))
|
||||
* provider/aws: Updated default configuration manager version in `aws_opsworks_stack` ([#12979](https://github.com/hashicorp/terraform/issues/12979))
|
||||
* provider/aws: Added aws_api_gateway_api_key value attribute ([#9462](https://github.com/hashicorp/terraform/issues/9462))
|
||||
* provider/aws: Allow aws_alb subnets to change ([#12850](https://github.com/hashicorp/terraform/issues/12850))
|
||||
* provider/aws: Support Attachment of ALB Target Groups to Autoscaling Groups ([#12855](https://github.com/hashicorp/terraform/issues/12855))
|
||||
* provider/aws: Support Import of iam_server_certificate ([#13065](https://github.com/hashicorp/terraform/issues/13065))
|
||||
* provider/azurerm: Add support for setting the primary network interface ([#11290](https://github.com/hashicorp/terraform/issues/11290))
|
||||
* provider/cloudstack: Add `zone_id` to `cloudstack_ipaddress` resource ([#11306](https://github.com/hashicorp/terraform/issues/11306))
|
||||
* provider/consul: Add support for basic auth to the provider ([#12679](https://github.com/hashicorp/terraform/issues/12679))
|
||||
* provider/digitalocean: Support disk only resize ([#13059](https://github.com/hashicorp/terraform/issues/13059))
|
||||
* provider/dnsimple: Allow dnsimple_record.priority attribute to be set ([#12843](https://github.com/hashicorp/terraform/issues/12843))
|
||||
* provider/google: Add support for service_account, metadata, and image_type fields in GKE cluster config ([#12743](https://github.com/hashicorp/terraform/issues/12743))
|
||||
* provider/google: Add local ssd count support for container clusters ([#12281](https://github.com/hashicorp/terraform/issues/12281))
|
||||
* provider/ignition: ignition_filesystem, explicit option to create the filesystem ([#12980](https://github.com/hashicorp/terraform/issues/12980))
|
||||
* provider/kubernetes: Internal K8S annotations are ignored in `config_map` ([#12945](https://github.com/hashicorp/terraform/issues/12945))
|
||||
* provider/ns1: Ensure provider checks for credentials ([#12920](https://github.com/hashicorp/terraform/issues/12920))
|
||||
* provider/openstack: Adding Timeouts to Blockstorage Resources ([#12862](https://github.com/hashicorp/terraform/issues/12862))
|
||||
* provider/openstack: Adding Timeouts to FWaaS v1 Resources ([#12863](https://github.com/hashicorp/terraform/issues/12863))
|
||||
* provider/openstack: Adding Timeouts to Image v2 and LBaaS v2 Resources ([#12865](https://github.com/hashicorp/terraform/issues/12865))
|
||||
* provider/openstack: Adding Timeouts to Network Resources ([#12866](https://github.com/hashicorp/terraform/issues/12866))
|
||||
* provider/openstack: Adding Timeouts to LBaaS v1 Resources ([#12867](https://github.com/hashicorp/terraform/issues/12867))
|
||||
* provider/openstack: Deprecating Instance Volume attribute ([#13062](https://github.com/hashicorp/terraform/issues/13062))
|
||||
* provider/openstack: Decprecating Instance Floating IP attribute ([#13063](https://github.com/hashicorp/terraform/issues/13063))
|
||||
* provider/openstack: Don't log the catalog ([#13075](https://github.com/hashicorp/terraform/issues/13075))
|
||||
* provider/openstack: Handle 409/500 Response on Pool Create ([#13074](https://github.com/hashicorp/terraform/issues/13074))
|
||||
* provider/pagerduty: Validate credentials ([#12854](https://github.com/hashicorp/terraform/issues/12854))
|
||||
* provider/openstack: Adding all_metadata attribute ([#13061](https://github.com/hashicorp/terraform/issues/13061))
|
||||
* provider/profitbricks: Handling missing resources ([#13053](https://github.com/hashicorp/terraform/issues/13053))
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) [GH-12849]
|
||||
* provider/mysql: recreate user/grant if user/grant got deleted manually [GH-12791]
|
||||
|
||||
* core: Remove legacy remote state configuration on state migration. This fixes errors when saving plans. ([#12888](https://github.com/hashicorp/terraform/issues/12888))
|
||||
* provider/arukas: Default timeout for launching container increased to 15mins (was 10mins) ([#12849](https://github.com/hashicorp/terraform/issues/12849))
|
||||
* provider/aws: Fix flattened cloudfront lambda function associations to be a set not a slice ([#11984](https://github.com/hashicorp/terraform/issues/11984))
|
||||
* provider/aws: Consider ACTIVE as pending state during ECS svc deletion ([#12986](https://github.com/hashicorp/terraform/issues/12986))
|
||||
* provider/aws: Deprecate the usage of Api Gateway Key Stages in favor of Usage Plans ([#12883](https://github.com/hashicorp/terraform/issues/12883))
|
||||
* provider/aws: prevent panic in resourceAwsSsmDocumentRead ([#12891](https://github.com/hashicorp/terraform/issues/12891))
|
||||
* provider/aws: Prevent panic when setting AWS CodeBuild Source to state ([#12915](https://github.com/hashicorp/terraform/issues/12915))
|
||||
* provider/aws: Only call replace Iam Instance Profile on existing machines ([#12922](https://github.com/hashicorp/terraform/issues/12922))
|
||||
* provider/aws: Increase AWS AMI Destroy timeout ([#12943](https://github.com/hashicorp/terraform/issues/12943))
|
||||
* provider/aws: Set aws_vpc ipv6 for associated only ([#12899](https://github.com/hashicorp/terraform/issues/12899))
|
||||
* provider/aws: Fix AWS ECS placement strategy spread fields ([#12998](https://github.com/hashicorp/terraform/issues/12998))
|
||||
* provider/aws: Specify that aws_network_acl_rule requires a cidr block ([#13013](https://github.com/hashicorp/terraform/issues/13013))
|
||||
* provider/aws: aws_network_acl_rule treat all and -1 for protocol the same ([#13049](https://github.com/hashicorp/terraform/issues/13049))
|
||||
* provider/aws: Only allow 1 value in alb_listener_rule condition ([#13051](https://github.com/hashicorp/terraform/issues/13051))
|
||||
* provider/aws: Correct handling of network ACL default IPv6 ingress/egress rules ([#12835](https://github.com/hashicorp/terraform/issues/12835))
|
||||
* provider/aws: aws_ses_receipt_rule: fix off-by-one errors ([#12961](https://github.com/hashicorp/terraform/issues/12961))
|
||||
* provider/aws: Fix issue upgrading to Terraform v0.9+ with AWS OpsWorks Stacks ([#13024](https://github.com/hashicorp/terraform/issues/13024))
|
||||
* provider/fastly: Fix issue importing Fastly Services with Backends ([#12538](https://github.com/hashicorp/terraform/issues/12538))
|
||||
* provider/google: turn compute_instance_group.instances into a set ([#12790](https://github.com/hashicorp/terraform/issues/12790))
|
||||
* provider/mysql: recreate user/grant if user/grant got deleted manually ([#12791](https://github.com/hashicorp/terraform/issues/12791))
|
||||
* provider/openstack: Fix monitor_id typo in LBaaS v1 Pool ([#13069](https://github.com/hashicorp/terraform/issues/13069))
|
||||
* provider/openstack: Resolve issues with Port Fixed IPs ([#13056](https://github.com/hashicorp/terraform/issues/13056))
|
||||
* provider/rancher: error when no api_url is provided ([#13086](https://github.com/hashicorp/terraform/issues/13086))
|
||||
* provider/scaleway: work around parallel request limitation ([#13045](https://github.com/hashicorp/terraform/issues/13045))
|
||||
|
||||
## 0.9.1 (March 17, 2017)
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -41,7 +41,7 @@ plugin-dev: generate
|
||||
test: fmtcheck errcheck generate
|
||||
go test -i $(TEST) || exit 1
|
||||
echo $(TEST) | \
|
||||
xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4
|
||||
xargs -t -n4 go test $(TESTARGS) -timeout=60s -parallel=4
|
||||
|
||||
# testacc runs acceptance tests
|
||||
testacc: fmtcheck generate
|
||||
|
12
README.md
12
README.md
@ -5,7 +5,7 @@ Terraform
|
||||
- [](https://gitter.im/hashicorp-terraform/Lobby)
|
||||
- Mailing list: [Google Groups](http://groups.google.com/group/terraform-tool)
|
||||
|
||||

|
||||

|
||||
|
||||
Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.
|
||||
|
||||
@ -152,3 +152,13 @@ $ tree ./pkg/ -P "terraform|*.zip"
|
||||
```
|
||||
|
||||
_Note: Cross-compilation uses [gox](https://github.com/mitchellh/gox), which requires toolchains to be built with versions of Go prior to 1.5. In order to successfully cross-compile with older versions of Go, you will need to run `gox -build-toolchain` before running the commands detailed above._
|
||||
|
||||
#### Docker
|
||||
|
||||
When using docker you don't need to have any of the Go development tools installed and you can clone terraform to any location on disk (doesn't have to be in your $GOPATH). This is useful for users who want to build `master` or a specific branch for testing without setting up a proper Go environment.
|
||||
|
||||
For example, run the following command to build terraform in a linux-based container for macOS.
|
||||
|
||||
```sh
|
||||
docker run --rm -v $(pwd):/go/src/github.com/hashicorp/terraform -w /go/src/github.com/hashicorp/terraform -e XC_OS=darwin -e XC_ARCH=amd64 golang:latest bash -c "apt-get update && apt-get install -y zip && make bin"
|
||||
```
|
||||
|
@ -7,6 +7,7 @@ package backend
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
@ -132,6 +133,9 @@ type Operation struct {
|
||||
// state.Lockers for its duration, and Unlock when complete.
|
||||
LockState bool
|
||||
|
||||
// The duration to retry obtaining a State lock.
|
||||
StateLockTimeout time.Duration
|
||||
|
||||
// Environment is the named state that should be loaded from the Backend.
|
||||
Environment string
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
backendlocal "github.com/hashicorp/terraform/backend/local"
|
||||
backendconsul "github.com/hashicorp/terraform/backend/remote-state/consul"
|
||||
backendinmem "github.com/hashicorp/terraform/backend/remote-state/inmem"
|
||||
backendS3 "github.com/hashicorp/terraform/backend/remote-state/s3"
|
||||
)
|
||||
|
||||
// backends is the list of available backends. This is a global variable
|
||||
@ -36,6 +37,7 @@ func init() {
|
||||
"local": func() backend.Backend { return &backendlocal.Local{} },
|
||||
"consul": func() backend.Backend { return backendconsul.New() },
|
||||
"inmem": func() backend.Backend { return backendinmem.New() },
|
||||
"s3": func() backend.Backend { return backendS3.New() },
|
||||
}
|
||||
|
||||
// Add the legacy remote backends that haven't yet been convertd to
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/command/clistate"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
@ -52,9 +52,12 @@ func (b *Local) opApply(
|
||||
}
|
||||
|
||||
if op.LockState {
|
||||
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||
defer cancel()
|
||||
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = op.Type.String()
|
||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||
if err != nil {
|
||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||
return
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/command/clistate"
|
||||
"github.com/hashicorp/terraform/command/format"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
@ -61,9 +61,12 @@ func (b *Local) opPlan(
|
||||
}
|
||||
|
||||
if op.LockState {
|
||||
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||
defer cancel()
|
||||
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = op.Type.String()
|
||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||
if err != nil {
|
||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||
return
|
||||
@ -110,6 +113,12 @@ func (b *Local) opPlan(
|
||||
// Write the backend if we have one
|
||||
plan.Backend = op.PlanOutBackend
|
||||
|
||||
// This works around a bug (#12871) which is no longer possible to
|
||||
// trigger but will exist for already corrupted upgrades.
|
||||
if plan.Backend != nil && plan.State != nil {
|
||||
plan.State.Remote = nil
|
||||
}
|
||||
|
||||
log.Printf("[INFO] backend/local: writing plan output to: %s", path)
|
||||
f, err := os.Create(path)
|
||||
if err == nil {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
clistate "github.com/hashicorp/terraform/command/state"
|
||||
"github.com/hashicorp/terraform/command/clistate"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
)
|
||||
@ -51,9 +51,12 @@ func (b *Local) opRefresh(
|
||||
}
|
||||
|
||||
if op.LockState {
|
||||
lockCtx, cancel := context.WithTimeout(ctx, op.StateLockTimeout)
|
||||
defer cancel()
|
||||
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = op.Type.String()
|
||||
lockID, err := clistate.Lock(opState, lockInfo, b.CLI, b.Colorize())
|
||||
lockID, err := clistate.Lock(lockCtx, opState, lockInfo, b.CLI, b.Colorize())
|
||||
if err != nil {
|
||||
runningOp.Err = errwrap.Wrapf("Error locking state: {{err}}", err)
|
||||
return
|
||||
|
@ -21,7 +21,8 @@ func TestLocal_impl(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocal_backend(t *testing.T) {
|
||||
b := TestLocal(t)
|
||||
defer testTmpDir(t)()
|
||||
b := &Local{}
|
||||
backend.TestBackend(t, b, b)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT
|
||||
// Code generated by "stringer -type=countHookAction hook_count_action.go"; DO NOT EDIT.
|
||||
|
||||
package local
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT
|
||||
// Code generated by "stringer -type=OperationType operation_type.go"; DO NOT EDIT.
|
||||
|
||||
package backend
|
||||
|
||||
|
@ -56,7 +56,7 @@ func (b *Backend) States() ([]string, error) {
|
||||
}
|
||||
|
||||
func (b *Backend) DeleteState(name string) error {
|
||||
if name == backend.DefaultStateName {
|
||||
if name == backend.DefaultStateName || name == "" {
|
||||
return fmt.Errorf("can't delete default state")
|
||||
}
|
||||
|
||||
@ -102,22 +102,19 @@ func (b *Backend) State(name string) (state.State, error) {
|
||||
stateMgr = &state.LockDisabled{Inner: stateMgr}
|
||||
}
|
||||
|
||||
// Get the locker, which we know always exists
|
||||
stateMgrLocker := stateMgr.(state.Locker)
|
||||
|
||||
// Grab a lock, we use this to write an empty state if one doesn't
|
||||
// exist already. We have to write an empty state as a sentinel value
|
||||
// so States() knows it exists.
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = "init"
|
||||
lockId, err := stateMgrLocker.Lock(lockInfo)
|
||||
lockId, err := stateMgr.Lock(lockInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lock state in Consul: %s", err)
|
||||
}
|
||||
|
||||
// Local helper function so we can call it multiple places
|
||||
lockUnlock := func(parent error) error {
|
||||
if err := stateMgrLocker.Unlock(lockId); err != nil {
|
||||
if err := stateMgr.Unlock(lockId); err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
|
||||
}
|
||||
|
||||
|
@ -121,16 +121,15 @@ func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
||||
default:
|
||||
if c.lockCh != nil {
|
||||
// we have an active lock already
|
||||
return "", nil
|
||||
return "", fmt.Errorf("state %q already locked", c.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if c.consulLock == nil {
|
||||
opts := &consulapi.LockOptions{
|
||||
Key: c.Path + lockSuffix,
|
||||
// We currently don't procide any options to block terraform and
|
||||
// retry lock acquisition, but we can wait briefly in case the
|
||||
// lock is about to be freed.
|
||||
// only wait briefly, so terraform has the choice to fail fast or
|
||||
// retry as needed.
|
||||
LockWaitTime: time.Second,
|
||||
LockTryOnce: true,
|
||||
}
|
||||
@ -191,6 +190,10 @@ func (c *RemoteClient) Unlock(id string) error {
|
||||
err := c.consulLock.Unlock()
|
||||
c.lockCh = nil
|
||||
|
||||
// This is only cleanup, and will fail if the lock was immediately taken by
|
||||
// another client, so we don't report an error to the user here.
|
||||
c.consulLock.Destroy()
|
||||
|
||||
kv := c.Client.KV()
|
||||
_, delErr := kv.Delete(c.Path+lockInfoSuffix, nil)
|
||||
if delErr != nil {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/state/remote"
|
||||
)
|
||||
|
||||
@ -98,3 +99,43 @@ func TestConsul_stateLock(t *testing.T) {
|
||||
|
||||
remote.TestRemoteLocks(t, sA.(*remote.State).Client, sB.(*remote.State).Client)
|
||||
}
|
||||
|
||||
func TestConsul_destroyLock(t *testing.T) {
|
||||
srv := newConsulTestServer(t)
|
||||
defer srv.Stop()
|
||||
|
||||
// Get the backend
|
||||
b := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"address": srv.HTTPAddr,
|
||||
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
|
||||
})
|
||||
|
||||
// Grab the client
|
||||
s, err := b.State(backend.DefaultStateName)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
c := s.(*remote.State).Client.(*RemoteClient)
|
||||
|
||||
info := state.NewLockInfo()
|
||||
id, err := c.Lock(info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
lockPath := c.Path + lockSuffix
|
||||
|
||||
if err := c.Unlock(id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// get the lock val
|
||||
pair, _, err := c.Client.KV().Get(lockPath, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pair != nil {
|
||||
t.Fatalf("lock key not cleaned up at: %s", pair.Key)
|
||||
}
|
||||
}
|
||||
|
195
backend/remote-state/s3/backend.go
Normal file
195
backend/remote-state/s3/backend.go
Normal file
@ -0,0 +1,195 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
terraformAWS "github.com/hashicorp/terraform/builtin/providers/aws"
|
||||
)
|
||||
|
||||
// New creates a new backend for S3 remote state.
|
||||
func New() backend.Backend {
|
||||
s := &schema.Backend{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"bucket": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "The name of the S3 bucket",
|
||||
},
|
||||
|
||||
"key": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "The path to the state file inside the bucket",
|
||||
},
|
||||
|
||||
"region": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Description: "The region of the S3 bucket.",
|
||||
DefaultFunc: schema.EnvDefaultFunc("AWS_DEFAULT_REGION", nil),
|
||||
},
|
||||
|
||||
"endpoint": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "A custom endpoint for the S3 API",
|
||||
DefaultFunc: schema.EnvDefaultFunc("AWS_S3_ENDPOINT", ""),
|
||||
},
|
||||
|
||||
"encrypt": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Description: "Whether to enable server side encryption of the state file",
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"acl": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Canned ACL to be applied to the state file",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"access_key": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "AWS access key",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"secret_key": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "AWS secret key",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"kms_key_id": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The ARN of a KMS Key to use for encrypting the state",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"lock_table": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "DynamoDB table for state locking",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"profile": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "AWS profile name",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"shared_credentials_file": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Path to a shared credentials file",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"token": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "MFA token",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"role_arn": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The role to be assumed",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"session_name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The session name to use when assuming the role.",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"external_id": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The external ID to use when assuming the role",
|
||||
Default: "",
|
||||
},
|
||||
|
||||
"assume_role_policy": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "The permissions applied when assuming a role.",
|
||||
Default: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := &Backend{Backend: s}
|
||||
result.Backend.ConfigureFunc = result.configure
|
||||
return result
|
||||
}
|
||||
|
||||
type Backend struct {
|
||||
*schema.Backend
|
||||
|
||||
// The fields below are set from configure
|
||||
s3Client *s3.S3
|
||||
dynClient *dynamodb.DynamoDB
|
||||
|
||||
bucketName string
|
||||
keyName string
|
||||
serverSideEncryption bool
|
||||
acl string
|
||||
kmsKeyID string
|
||||
lockTable string
|
||||
}
|
||||
|
||||
func (b *Backend) configure(ctx context.Context) error {
|
||||
if b.s3Client != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Grab the resource data
|
||||
data := schema.FromContextBackendConfig(ctx)
|
||||
|
||||
b.bucketName = data.Get("bucket").(string)
|
||||
b.keyName = data.Get("key").(string)
|
||||
b.serverSideEncryption = data.Get("encrypt").(bool)
|
||||
b.acl = data.Get("acl").(string)
|
||||
b.kmsKeyID = data.Get("kms_key_id").(string)
|
||||
b.lockTable = data.Get("lock_table").(string)
|
||||
|
||||
cfg := &terraformAWS.Config{
|
||||
AccessKey: data.Get("access_key").(string),
|
||||
AssumeRoleARN: data.Get("role_arn").(string),
|
||||
AssumeRoleExternalID: data.Get("external_id").(string),
|
||||
AssumeRolePolicy: data.Get("assume_role_policy").(string),
|
||||
AssumeRoleSessionName: data.Get("session_name").(string),
|
||||
CredsFilename: data.Get("shared_credentials_file").(string),
|
||||
Profile: data.Get("profile").(string),
|
||||
Region: data.Get("region").(string),
|
||||
S3Endpoint: data.Get("endpoint").(string),
|
||||
SecretKey: data.Get("secret_key").(string),
|
||||
Token: data.Get("token").(string),
|
||||
}
|
||||
|
||||
client, err := cfg.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.s3Client = client.(*terraformAWS.AWSClient).S3()
|
||||
b.dynClient = client.(*terraformAWS.AWSClient).DynamoDB()
|
||||
|
||||
return nil
|
||||
}
|
159
backend/remote-state/s3/backend_state.go
Normal file
159
backend/remote-state/s3/backend_state.go
Normal file
@ -0,0 +1,159 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/state/remote"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
const (
|
||||
// This will be used as directory name, the odd looking colon is simply to
|
||||
// reduce the chance of name conflicts with existing objects.
|
||||
keyEnvPrefix = "env:"
|
||||
)
|
||||
|
||||
func (b *Backend) States() ([]string, error) {
|
||||
params := &s3.ListObjectsInput{
|
||||
Bucket: &b.bucketName,
|
||||
Prefix: aws.String(keyEnvPrefix + "/"),
|
||||
}
|
||||
|
||||
resp, err := b.s3Client.ListObjects(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var envs []string
|
||||
for _, obj := range resp.Contents {
|
||||
env := keyEnv(*obj.Key)
|
||||
if env != "" {
|
||||
envs = append(envs, env)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(envs)
|
||||
envs = append([]string{backend.DefaultStateName}, envs...)
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
// extract the env name from the S3 key
|
||||
func keyEnv(key string) string {
|
||||
parts := strings.Split(key, "/")
|
||||
if len(parts) < 3 {
|
||||
// no env here
|
||||
return ""
|
||||
}
|
||||
|
||||
if parts[0] != keyEnvPrefix {
|
||||
// not our key, so ignore
|
||||
return ""
|
||||
}
|
||||
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
func (b *Backend) DeleteState(name string) error {
|
||||
if name == backend.DefaultStateName || name == "" {
|
||||
return fmt.Errorf("can't delete default state")
|
||||
}
|
||||
|
||||
params := &s3.DeleteObjectInput{
|
||||
Bucket: &b.bucketName,
|
||||
Key: aws.String(b.path(name)),
|
||||
}
|
||||
|
||||
_, err := b.s3Client.DeleteObject(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) State(name string) (state.State, error) {
|
||||
client := &RemoteClient{
|
||||
s3Client: b.s3Client,
|
||||
dynClient: b.dynClient,
|
||||
bucketName: b.bucketName,
|
||||
path: b.path(name),
|
||||
serverSideEncryption: b.serverSideEncryption,
|
||||
acl: b.acl,
|
||||
kmsKeyID: b.kmsKeyID,
|
||||
lockTable: b.lockTable,
|
||||
}
|
||||
|
||||
stateMgr := &remote.State{Client: client}
|
||||
|
||||
//if this isn't the default state name, we need to create the object so
|
||||
//it's listed by States.
|
||||
if name != backend.DefaultStateName {
|
||||
// take a lock on this state while we write it
|
||||
lockInfo := state.NewLockInfo()
|
||||
lockInfo.Operation = "init"
|
||||
lockId, err := client.Lock(lockInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lock s3 state: %s", err)
|
||||
}
|
||||
|
||||
// Local helper function so we can call it multiple places
|
||||
lockUnlock := func(parent error) error {
|
||||
if err := stateMgr.Unlock(lockId); err != nil {
|
||||
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
// Grab the value
|
||||
if err := stateMgr.RefreshState(); err != nil {
|
||||
err = lockUnlock(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we have no state, we have to create an empty state
|
||||
if v := stateMgr.State(); v == nil {
|
||||
if err := stateMgr.WriteState(terraform.NewState()); err != nil {
|
||||
err = lockUnlock(err)
|
||||
return nil, err
|
||||
}
|
||||
if err := stateMgr.PersistState(); err != nil {
|
||||
err = lockUnlock(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock, the state should now be initialized
|
||||
if err := lockUnlock(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return stateMgr, nil
|
||||
}
|
||||
|
||||
func (b *Backend) client() *RemoteClient {
|
||||
return &RemoteClient{}
|
||||
}
|
||||
|
||||
func (b *Backend) path(name string) string {
|
||||
if name == backend.DefaultStateName {
|
||||
return b.keyName
|
||||
}
|
||||
|
||||
return strings.Join([]string{keyEnvPrefix, name, b.keyName}, "/")
|
||||
}
|
||||
|
||||
const errStateUnlock = `
|
||||
Error unlocking S3 state. Lock ID: %s
|
||||
|
||||
Error: %s
|
||||
|
||||
You may have to force-unlock this state in order to use it again.
|
||||
`
|
209
backend/remote-state/s3/backend_test.go
Normal file
209
backend/remote-state/s3/backend_test.go
Normal file
@ -0,0 +1,209 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
)
|
||||
|
||||
// verify that we are doing ACC tests or the S3 tests specifically
|
||||
func testACC(t *testing.T) {
|
||||
skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_S3_TEST") == ""
|
||||
if skip {
|
||||
t.Log("s3 backend tests require setting TF_ACC or TF_S3_TEST")
|
||||
t.Skip()
|
||||
}
|
||||
if os.Getenv("AWS_DEFAULT_REGION") == "" {
|
||||
os.Setenv("AWS_DEFAULT_REGION", "us-west-2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackend_impl(t *testing.T) {
|
||||
var _ backend.Backend = new(Backend)
|
||||
}
|
||||
|
||||
func TestBackendConfig(t *testing.T) {
|
||||
testACC(t)
|
||||
config := map[string]interface{}{
|
||||
"region": "us-west-1",
|
||||
"bucket": "tf-test",
|
||||
"key": "state",
|
||||
"encrypt": true,
|
||||
"lock_table": "dynamoTable",
|
||||
}
|
||||
|
||||
b := backend.TestBackendConfig(t, New(), config).(*Backend)
|
||||
|
||||
if *b.s3Client.Config.Region != "us-west-1" {
|
||||
t.Fatalf("Incorrect region was populated")
|
||||
}
|
||||
if b.bucketName != "tf-test" {
|
||||
t.Fatalf("Incorrect bucketName was populated")
|
||||
}
|
||||
if b.keyName != "state" {
|
||||
t.Fatalf("Incorrect keyName was populated")
|
||||
}
|
||||
|
||||
credentials, err := b.s3Client.Config.Credentials.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Error when requesting credentials")
|
||||
}
|
||||
if credentials.AccessKeyID == "" {
|
||||
t.Fatalf("No Access Key Id was populated")
|
||||
}
|
||||
if credentials.SecretAccessKey == "" {
|
||||
t.Fatalf("No Secret Access Key was populated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackend(t *testing.T) {
|
||||
testACC(t)
|
||||
|
||||
bucketName := fmt.Sprintf("terraform-remote-s3-test-%x", time.Now().Unix())
|
||||
keyName := "testState"
|
||||
|
||||
b := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
}).(*Backend)
|
||||
|
||||
createS3Bucket(t, b.s3Client, bucketName)
|
||||
defer deleteS3Bucket(t, b.s3Client, bucketName)
|
||||
|
||||
backend.TestBackend(t, b, nil)
|
||||
}
|
||||
|
||||
func TestBackendLocked(t *testing.T) {
|
||||
testACC(t)
|
||||
|
||||
bucketName := fmt.Sprintf("terraform-remote-s3-test-%x", time.Now().Unix())
|
||||
keyName := "testState"
|
||||
|
||||
b1 := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
"lock_table": bucketName,
|
||||
}).(*Backend)
|
||||
|
||||
b2 := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
"lock_table": bucketName,
|
||||
}).(*Backend)
|
||||
|
||||
createS3Bucket(t, b1.s3Client, bucketName)
|
||||
defer deleteS3Bucket(t, b1.s3Client, bucketName)
|
||||
createDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
defer deleteDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
|
||||
backend.TestBackend(t, b1, b2)
|
||||
}
|
||||
|
||||
func createS3Bucket(t *testing.T, s3Client *s3.S3, bucketName string) {
|
||||
createBucketReq := &s3.CreateBucketInput{
|
||||
Bucket: &bucketName,
|
||||
}
|
||||
|
||||
// Be clear about what we're doing in case the user needs to clean
|
||||
// this up later.
|
||||
t.Logf("creating S3 bucket %s in %s", bucketName, *s3Client.Config.Region)
|
||||
_, err := s3Client.CreateBucket(createBucketReq)
|
||||
if err != nil {
|
||||
t.Fatal("failed to create test S3 bucket:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteS3Bucket(t *testing.T, s3Client *s3.S3, bucketName string) {
|
||||
warning := "WARNING: Failed to delete the test S3 bucket. It may have been left in your AWS account and may incur storage charges. (error was %s)"
|
||||
|
||||
// first we have to get rid of the env objects, or we can't delete the bucket
|
||||
resp, err := s3Client.ListObjects(&s3.ListObjectsInput{Bucket: &bucketName})
|
||||
if err != nil {
|
||||
t.Logf(warning, err)
|
||||
return
|
||||
}
|
||||
for _, obj := range resp.Contents {
|
||||
if _, err := s3Client.DeleteObject(&s3.DeleteObjectInput{Bucket: &bucketName, Key: obj.Key}); err != nil {
|
||||
// this will need cleanup no matter what, so just warn and exit
|
||||
t.Logf(warning, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := s3Client.DeleteBucket(&s3.DeleteBucketInput{Bucket: &bucketName}); err != nil {
|
||||
t.Logf(warning, err)
|
||||
}
|
||||
}
|
||||
|
||||
// create the dynamoDB table, and wait until we can query it.
|
||||
func createDynamoDBTable(t *testing.T, dynClient *dynamodb.DynamoDB, tableName string) {
|
||||
createInput := &dynamodb.CreateTableInput{
|
||||
AttributeDefinitions: []*dynamodb.AttributeDefinition{
|
||||
{
|
||||
AttributeName: aws.String("LockID"),
|
||||
AttributeType: aws.String("S"),
|
||||
},
|
||||
},
|
||||
KeySchema: []*dynamodb.KeySchemaElement{
|
||||
{
|
||||
AttributeName: aws.String("LockID"),
|
||||
KeyType: aws.String("HASH"),
|
||||
},
|
||||
},
|
||||
ProvisionedThroughput: &dynamodb.ProvisionedThroughput{
|
||||
ReadCapacityUnits: aws.Int64(5),
|
||||
WriteCapacityUnits: aws.Int64(5),
|
||||
},
|
||||
TableName: aws.String(tableName),
|
||||
}
|
||||
|
||||
_, err := dynClient.CreateTable(createInput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// now wait until it's ACTIVE
|
||||
start := time.Now()
|
||||
time.Sleep(time.Second)
|
||||
|
||||
describeInput := &dynamodb.DescribeTableInput{
|
||||
TableName: aws.String(tableName),
|
||||
}
|
||||
|
||||
for {
|
||||
resp, err := dynClient.DescribeTable(describeInput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if *resp.Table.TableStatus == "ACTIVE" {
|
||||
return
|
||||
}
|
||||
|
||||
if time.Since(start) > time.Minute {
|
||||
t.Fatalf("timed out creating DynamoDB table %s", tableName)
|
||||
}
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func deleteDynamoDBTable(t *testing.T, dynClient *dynamodb.DynamoDB, tableName string) {
|
||||
params := &dynamodb.DeleteTableInput{
|
||||
TableName: aws.String(tableName),
|
||||
}
|
||||
_, err := dynClient.DeleteTable(params)
|
||||
if err != nil {
|
||||
t.Logf("WARNING: Failed to delete the test DynamoDB table %q. It has been left in your AWS account and may incur charges. (error was %s)", tableName, err)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package remote
|
||||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -6,127 +6,32 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
terraformAws "github.com/hashicorp/terraform/builtin/providers/aws"
|
||||
"github.com/hashicorp/terraform/state"
|
||||
"github.com/hashicorp/terraform/state/remote"
|
||||
)
|
||||
|
||||
func s3Factory(conf map[string]string) (Client, error) {
|
||||
bucketName, ok := conf["bucket"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing 'bucket' configuration")
|
||||
}
|
||||
|
||||
keyName, ok := conf["key"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing 'key' configuration")
|
||||
}
|
||||
|
||||
endpoint, ok := conf["endpoint"]
|
||||
if !ok {
|
||||
endpoint = os.Getenv("AWS_S3_ENDPOINT")
|
||||
}
|
||||
|
||||
regionName, ok := conf["region"]
|
||||
if !ok {
|
||||
regionName = os.Getenv("AWS_DEFAULT_REGION")
|
||||
if regionName == "" {
|
||||
return nil, fmt.Errorf(
|
||||
"missing 'region' configuration or AWS_DEFAULT_REGION environment variable")
|
||||
}
|
||||
}
|
||||
|
||||
serverSideEncryption := false
|
||||
if raw, ok := conf["encrypt"]; ok {
|
||||
v, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"'encrypt' field couldn't be parsed as bool: %s", err)
|
||||
}
|
||||
|
||||
serverSideEncryption = v
|
||||
}
|
||||
|
||||
acl := ""
|
||||
if raw, ok := conf["acl"]; ok {
|
||||
acl = raw
|
||||
}
|
||||
kmsKeyID := conf["kms_key_id"]
|
||||
|
||||
var errs []error
|
||||
creds, err := terraformAws.GetCredentials(&terraformAws.Config{
|
||||
AccessKey: conf["access_key"],
|
||||
SecretKey: conf["secret_key"],
|
||||
Token: conf["token"],
|
||||
Profile: conf["profile"],
|
||||
CredsFilename: conf["shared_credentials_file"],
|
||||
AssumeRoleARN: conf["role_arn"],
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Call Get to check for credential provider. If nothing found, we'll get an
|
||||
// error, and we can present it nicely to the user
|
||||
_, err = creds.Get()
|
||||
if err != nil {
|
||||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
|
||||
errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS S3 remote.
|
||||
Please see https://www.terraform.io/docs/state/remote/s3.html for more information on
|
||||
providing credentials for the AWS S3 remote`))
|
||||
} else {
|
||||
errs = append(errs, fmt.Errorf("Error loading credentials for AWS S3 remote: %s", err))
|
||||
}
|
||||
return nil, &multierror.Error{Errors: errs}
|
||||
}
|
||||
|
||||
awsConfig := &aws.Config{
|
||||
Credentials: creds,
|
||||
Endpoint: aws.String(endpoint),
|
||||
Region: aws.String(regionName),
|
||||
HTTPClient: cleanhttp.DefaultClient(),
|
||||
}
|
||||
sess := session.New(awsConfig)
|
||||
nativeClient := s3.New(sess)
|
||||
dynClient := dynamodb.New(sess)
|
||||
|
||||
return &S3Client{
|
||||
nativeClient: nativeClient,
|
||||
bucketName: bucketName,
|
||||
keyName: keyName,
|
||||
serverSideEncryption: serverSideEncryption,
|
||||
acl: acl,
|
||||
kmsKeyID: kmsKeyID,
|
||||
dynClient: dynClient,
|
||||
lockTable: conf["lock_table"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
type S3Client struct {
|
||||
nativeClient *s3.S3
|
||||
type RemoteClient struct {
|
||||
s3Client *s3.S3
|
||||
dynClient *dynamodb.DynamoDB
|
||||
bucketName string
|
||||
keyName string
|
||||
path string
|
||||
serverSideEncryption bool
|
||||
acl string
|
||||
kmsKeyID string
|
||||
dynClient *dynamodb.DynamoDB
|
||||
lockTable string
|
||||
}
|
||||
|
||||
func (c *S3Client) Get() (*Payload, error) {
|
||||
output, err := c.nativeClient.GetObject(&s3.GetObjectInput{
|
||||
func (c *RemoteClient) Get() (*remote.Payload, error) {
|
||||
output, err := c.s3Client.GetObject(&s3.GetObjectInput{
|
||||
Bucket: &c.bucketName,
|
||||
Key: &c.keyName,
|
||||
Key: &c.path,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@ -148,7 +53,7 @@ func (c *S3Client) Get() (*Payload, error) {
|
||||
return nil, fmt.Errorf("Failed to read remote state: %s", err)
|
||||
}
|
||||
|
||||
payload := &Payload{
|
||||
payload := &remote.Payload{
|
||||
Data: buf.Bytes(),
|
||||
}
|
||||
|
||||
@ -160,7 +65,7 @@ func (c *S3Client) Get() (*Payload, error) {
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (c *S3Client) Put(data []byte) error {
|
||||
func (c *RemoteClient) Put(data []byte) error {
|
||||
contentType := "application/json"
|
||||
contentLength := int64(len(data))
|
||||
|
||||
@ -169,7 +74,7 @@ func (c *S3Client) Put(data []byte) error {
|
||||
ContentLength: &contentLength,
|
||||
Body: bytes.NewReader(data),
|
||||
Bucket: &c.bucketName,
|
||||
Key: &c.keyName,
|
||||
Key: &c.path,
|
||||
}
|
||||
|
||||
if c.serverSideEncryption {
|
||||
@ -187,29 +92,28 @@ func (c *S3Client) Put(data []byte) error {
|
||||
|
||||
log.Printf("[DEBUG] Uploading remote state to S3: %#v", i)
|
||||
|
||||
if _, err := c.nativeClient.PutObject(i); err == nil {
|
||||
if _, err := c.s3Client.PutObject(i); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Failed to upload state: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *S3Client) Delete() error {
|
||||
_, err := c.nativeClient.DeleteObject(&s3.DeleteObjectInput{
|
||||
func (c *RemoteClient) Delete() error {
|
||||
_, err := c.s3Client.DeleteObject(&s3.DeleteObjectInput{
|
||||
Bucket: &c.bucketName,
|
||||
Key: &c.keyName,
|
||||
Key: &c.path,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *S3Client) Lock(info *state.LockInfo) (string, error) {
|
||||
func (c *RemoteClient) Lock(info *state.LockInfo) (string, error) {
|
||||
if c.lockTable == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
stateName := fmt.Sprintf("%s/%s", c.bucketName, c.keyName)
|
||||
info.Path = stateName
|
||||
info.Path = c.lockPath()
|
||||
|
||||
if info.ID == "" {
|
||||
lockID, err := uuid.GenerateUUID()
|
||||
@ -222,7 +126,7 @@ func (c *S3Client) Lock(info *state.LockInfo) (string, error) {
|
||||
|
||||
putParams := &dynamodb.PutItemInput{
|
||||
Item: map[string]*dynamodb.AttributeValue{
|
||||
"LockID": {S: aws.String(stateName)},
|
||||
"LockID": {S: aws.String(c.lockPath())},
|
||||
"Info": {S: aws.String(string(info.Marshal()))},
|
||||
},
|
||||
TableName: aws.String(c.lockTable),
|
||||
@ -245,10 +149,10 @@ func (c *S3Client) Lock(info *state.LockInfo) (string, error) {
|
||||
return info.ID, nil
|
||||
}
|
||||
|
||||
func (c *S3Client) getLockInfo() (*state.LockInfo, error) {
|
||||
func (c *RemoteClient) getLockInfo() (*state.LockInfo, error) {
|
||||
getParams := &dynamodb.GetItemInput{
|
||||
Key: map[string]*dynamodb.AttributeValue{
|
||||
"LockID": {S: aws.String(fmt.Sprintf("%s/%s", c.bucketName, c.keyName))},
|
||||
"LockID": {S: aws.String(c.lockPath())},
|
||||
},
|
||||
ProjectionExpression: aws.String("LockID, Info"),
|
||||
TableName: aws.String(c.lockTable),
|
||||
@ -273,7 +177,7 @@ func (c *S3Client) getLockInfo() (*state.LockInfo, error) {
|
||||
return lockInfo, nil
|
||||
}
|
||||
|
||||
func (c *S3Client) Unlock(id string) error {
|
||||
func (c *RemoteClient) Unlock(id string) error {
|
||||
if c.lockTable == "" {
|
||||
return nil
|
||||
}
|
||||
@ -297,7 +201,7 @@ func (c *S3Client) Unlock(id string) error {
|
||||
|
||||
params := &dynamodb.DeleteItemInput{
|
||||
Key: map[string]*dynamodb.AttributeValue{
|
||||
"LockID": {S: aws.String(fmt.Sprintf("%s/%s", c.bucketName, c.keyName))},
|
||||
"LockID": {S: aws.String(c.lockPath())},
|
||||
},
|
||||
TableName: aws.String(c.lockTable),
|
||||
}
|
||||
@ -309,3 +213,7 @@ func (c *S3Client) Unlock(id string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RemoteClient) lockPath() string {
|
||||
return fmt.Sprintf("%s/%s", c.bucketName, c.path)
|
||||
}
|
76
backend/remote-state/s3/client_test.go
Normal file
76
backend/remote-state/s3/client_test.go
Normal file
@ -0,0 +1,76 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/state/remote"
|
||||
)
|
||||
|
||||
func TestRemoteClient_impl(t *testing.T) {
|
||||
var _ remote.Client = new(RemoteClient)
|
||||
var _ remote.ClientLocker = new(RemoteClient)
|
||||
}
|
||||
|
||||
func TestRemoteClient(t *testing.T) {
|
||||
testACC(t)
|
||||
|
||||
bucketName := fmt.Sprintf("terraform-remote-s3-test-%x", time.Now().Unix())
|
||||
keyName := "testState"
|
||||
|
||||
b := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
}).(*Backend)
|
||||
|
||||
state, err := b.State(backend.DefaultStateName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
createS3Bucket(t, b.s3Client, bucketName)
|
||||
defer deleteS3Bucket(t, b.s3Client, bucketName)
|
||||
|
||||
remote.TestClient(t, state.(*remote.State).Client)
|
||||
}
|
||||
|
||||
func TestRemoteClientLocks(t *testing.T) {
|
||||
testACC(t)
|
||||
|
||||
bucketName := fmt.Sprintf("terraform-remote-s3-test-%x", time.Now().Unix())
|
||||
keyName := "testState"
|
||||
|
||||
b1 := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
"lock_table": bucketName,
|
||||
}).(*Backend)
|
||||
|
||||
b2 := backend.TestBackendConfig(t, New(), map[string]interface{}{
|
||||
"bucket": bucketName,
|
||||
"key": keyName,
|
||||
"encrypt": true,
|
||||
"lock_table": bucketName,
|
||||
}).(*Backend)
|
||||
|
||||
s1, err := b1.State(backend.DefaultStateName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s2, err := b2.State(backend.DefaultStateName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
createS3Bucket(t, b1.s3Client, bucketName)
|
||||
defer deleteS3Bucket(t, b1.s3Client, bucketName)
|
||||
createDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
defer deleteDynamoDBTable(t, b1.dynClient, bucketName)
|
||||
|
||||
remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client)
|
||||
}
|
@ -65,57 +65,109 @@ func testBackendStates(t *testing.T, b Backend) {
|
||||
}
|
||||
|
||||
// Create a couple states
|
||||
fooState, err := b.State("foo")
|
||||
foo, err := b.State("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("error: %s", err)
|
||||
}
|
||||
if err := fooState.RefreshState(); err != nil {
|
||||
if err := foo.RefreshState(); err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
if v := fooState.State(); v.HasResources() {
|
||||
if v := foo.State(); v.HasResources() {
|
||||
t.Fatalf("should be empty: %s", v)
|
||||
}
|
||||
|
||||
barState, err := b.State("bar")
|
||||
bar, err := b.State("bar")
|
||||
if err != nil {
|
||||
t.Fatalf("error: %s", err)
|
||||
}
|
||||
if err := barState.RefreshState(); err != nil {
|
||||
if err := bar.RefreshState(); err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
if v := barState.State(); v.HasResources() {
|
||||
if v := bar.State(); v.HasResources() {
|
||||
t.Fatalf("should be empty: %s", v)
|
||||
}
|
||||
|
||||
// Verify they are distinct states
|
||||
// Verify they are distinct states that can be read back from storage
|
||||
{
|
||||
s := barState.State()
|
||||
if s == nil {
|
||||
s = terraform.NewState()
|
||||
// start with a fresh state, and record the lineage being
|
||||
// written to "bar"
|
||||
barState := terraform.NewState()
|
||||
barLineage := barState.Lineage
|
||||
|
||||
// the foo lineage should be distinct from bar, and unchanged after
|
||||
// modifying bar
|
||||
fooState := terraform.NewState()
|
||||
fooLineage := fooState.Lineage
|
||||
|
||||
// write a known state to foo
|
||||
if err := foo.WriteState(fooState); err != nil {
|
||||
t.Fatal("error writing foo state:", err)
|
||||
}
|
||||
if err := foo.PersistState(); err != nil {
|
||||
t.Fatal("error persisting foo state:", err)
|
||||
}
|
||||
|
||||
s.Lineage = "bar"
|
||||
if err := barState.WriteState(s); err != nil {
|
||||
// write a distinct known state to bar
|
||||
if err := bar.WriteState(barState); err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
if err := barState.PersistState(); err != nil {
|
||||
if err := bar.PersistState(); err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
|
||||
if err := fooState.RefreshState(); err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
// verify that foo is unchanged with the existing state manager
|
||||
if err := foo.RefreshState(); err != nil {
|
||||
t.Fatal("error refreshing foo:", err)
|
||||
}
|
||||
if v := fooState.State(); v != nil && v.Lineage == "bar" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
fooState = foo.State()
|
||||
switch {
|
||||
case fooState == nil:
|
||||
t.Fatal("nil state read from foo")
|
||||
case fooState.Lineage == barLineage:
|
||||
t.Fatalf("bar lineage read from foo: %#v", fooState)
|
||||
case fooState.Lineage != fooLineage:
|
||||
t.Fatal("foo lineage alterred")
|
||||
}
|
||||
|
||||
// fetch foo again from the backend
|
||||
foo, err = b.State("foo")
|
||||
if err != nil {
|
||||
t.Fatal("error re-fetching state:", err)
|
||||
}
|
||||
if err := foo.RefreshState(); err != nil {
|
||||
t.Fatal("error refreshing foo:", err)
|
||||
}
|
||||
fooState = foo.State()
|
||||
switch {
|
||||
case fooState == nil:
|
||||
t.Fatal("nil state read from foo")
|
||||
case fooState.Lineage != fooLineage:
|
||||
t.Fatal("incorrect state returned from backend")
|
||||
}
|
||||
|
||||
// fetch the bar again from the backend
|
||||
bar, err = b.State("bar")
|
||||
if err != nil {
|
||||
t.Fatal("error re-fetching state:", err)
|
||||
}
|
||||
if err := bar.RefreshState(); err != nil {
|
||||
t.Fatal("error refreshing bar:", err)
|
||||
}
|
||||
barState = bar.State()
|
||||
switch {
|
||||
case barState == nil:
|
||||
t.Fatal("nil state read from bar")
|
||||
case barState.Lineage != barLineage:
|
||||
t.Fatal("incorrect state returned from backend")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we can now list them
|
||||
{
|
||||
// we determined that named stated are supported earlier
|
||||
states, err := b.States()
|
||||
if err == ErrNamedStatesNotSupported {
|
||||
t.Logf("TestBackend: named states not supported in %T, skipping", b)
|
||||
return
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sort.Strings(states)
|
||||
|
@ -2,6 +2,7 @@ package alicloud
|
||||
|
||||
import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
@ -12,8 +13,12 @@ const (
|
||||
VpcNet = InstanceNetWork("vpc")
|
||||
)
|
||||
|
||||
// timeout for common product, ecs e.g.
|
||||
const defaultTimeout = 120
|
||||
|
||||
// timeout for long time progerss product, rds e.g.
|
||||
const defaultLongTimeout = 800
|
||||
|
||||
func getRegion(d *schema.ResourceData, meta interface{}) common.Region {
|
||||
return meta.(*AliyunClient).Region
|
||||
}
|
||||
@ -50,3 +55,26 @@ func isProtocalValid(value string) bool {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
var DefaultBusinessInfo = ecs.BusinessInfo{
|
||||
Pack: "terraform",
|
||||
}
|
||||
|
||||
// default region for all resource
|
||||
const DEFAULT_REGION = "cn-beijing"
|
||||
|
||||
// default security ip for db
|
||||
const DEFAULT_DB_SECURITY_IP = "127.0.0.1"
|
||||
|
||||
// we the count of create instance is only one
|
||||
const DEFAULT_INSTANCE_COUNT = 1
|
||||
|
||||
// symbol of multiIZ
|
||||
const MULTI_IZ_SYMBOL = "MAZ"
|
||||
|
||||
// default connect port of db
|
||||
const DB_DEFAULT_CONNECT_PORT = "3306"
|
||||
|
||||
const COMMA_SEPARATED = ","
|
||||
|
||||
const LOCAL_HOST_IP = "127.0.0.1"
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
)
|
||||
|
||||
@ -19,8 +20,11 @@ type Config struct {
|
||||
type AliyunClient struct {
|
||||
Region common.Region
|
||||
ecsconn *ecs.Client
|
||||
vpcconn *ecs.Client
|
||||
slbconn *slb.Client
|
||||
rdsconn *rds.Client
|
||||
// use new version
|
||||
ecsNewconn *ecs.Client
|
||||
vpcconn *ecs.Client
|
||||
slbconn *slb.Client
|
||||
}
|
||||
|
||||
// Client for AliyunClient
|
||||
@ -35,6 +39,17 @@ func (c *Config) Client() (*AliyunClient, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ecsNewconn, err := c.ecsConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecsNewconn.SetVersion(EcsApiVersion20160314)
|
||||
|
||||
rdsconn, err := c.rdsConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slbconn, err := c.slbConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -46,13 +61,17 @@ func (c *Config) Client() (*AliyunClient, error) {
|
||||
}
|
||||
|
||||
return &AliyunClient{
|
||||
Region: c.Region,
|
||||
ecsconn: ecsconn,
|
||||
vpcconn: vpcconn,
|
||||
slbconn: slbconn,
|
||||
Region: c.Region,
|
||||
ecsconn: ecsconn,
|
||||
ecsNewconn: ecsNewconn,
|
||||
vpcconn: vpcconn,
|
||||
slbconn: slbconn,
|
||||
rdsconn: rdsconn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
const BusinessInfoKey = "Terraform"
|
||||
|
||||
func (c *Config) loadAndValidate() error {
|
||||
err := c.validateRegion()
|
||||
if err != nil {
|
||||
@ -74,7 +93,9 @@ func (c *Config) validateRegion() error {
|
||||
}
|
||||
|
||||
func (c *Config) ecsConn() (*ecs.Client, error) {
|
||||
client := ecs.NewClient(c.AccessKey, c.SecretKey)
|
||||
client := ecs.NewECSClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
|
||||
_, err := client.DescribeRegions()
|
||||
|
||||
if err != nil {
|
||||
@ -84,20 +105,21 @@ func (c *Config) ecsConn() (*ecs.Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) slbConn() (*slb.Client, error) {
|
||||
client := slb.NewClient(c.AccessKey, c.SecretKey)
|
||||
func (c *Config) rdsConn() (*rds.Client, error) {
|
||||
client := rds.NewRDSClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) slbConn() (*slb.Client, error) {
|
||||
client := slb.NewSLBClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Config) vpcConn() (*ecs.Client, error) {
|
||||
_, err := c.ecsConn()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &ecs.Client{}
|
||||
client.Init("https://vpc.aliyuncs.com/", "2016-04-28", c.AccessKey, c.SecretKey)
|
||||
client := ecs.NewVPCClient(c.AccessKey, c.SecretKey, c.Region)
|
||||
client.SetBusinessInfo(BusinessInfoKey)
|
||||
return client, nil
|
||||
|
||||
}
|
||||
|
@ -5,10 +5,10 @@ import (
|
||||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"time"
|
||||
)
|
||||
|
||||
func dataSourceAlicloudImages() *schema.Resource {
|
||||
@ -175,15 +175,28 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
||||
params.ImageOwnerAlias = ecs.ImageOwnerAlias(owners.(string))
|
||||
}
|
||||
|
||||
resp, _, err := conn.DescribeImages(params)
|
||||
if err != nil {
|
||||
return err
|
||||
var allImages []ecs.ImageType
|
||||
|
||||
for {
|
||||
images, paginationResult, err := conn.DescribeImages(params)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
allImages = append(allImages, images...)
|
||||
|
||||
pagination := paginationResult.NextPage()
|
||||
if pagination == nil {
|
||||
break
|
||||
}
|
||||
|
||||
params.Pagination = *pagination
|
||||
}
|
||||
|
||||
var filteredImages []ecs.ImageType
|
||||
if nameRegexOk {
|
||||
r := regexp.MustCompile(nameRegex.(string))
|
||||
for _, image := range resp {
|
||||
for _, image := range allImages {
|
||||
// Check for a very rare case where the response would include no
|
||||
// image name. No name means nothing to attempt a match against,
|
||||
// therefore we are skipping such image.
|
||||
@ -198,7 +211,7 @@ func dataSourceAlicloudImagesRead(d *schema.ResourceData, meta interface{}) erro
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filteredImages = resp[:]
|
||||
filteredImages = allImages[:]
|
||||
}
|
||||
|
||||
var images []ecs.ImageType
|
||||
|
@ -97,6 +97,22 @@ func TestAccAlicloudImagesDataSource_nameRegexFilter(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudImagesDataSource_imageNotInFirstPage(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_images.name_regex_filtered_image"),
|
||||
resource.TestMatchResourceAttr("data.alicloud_images.name_regex_filtered_image", "images.0.image_id", regexp.MustCompile("^ubuntu_14")),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Instance store test - using centos images
|
||||
const testAccCheckAlicloudImagesDataSourceImagesConfig = `
|
||||
data "alicloud_images" "multi_image" {
|
||||
@ -128,3 +144,12 @@ data "alicloud_images" "name_regex_filtered_image" {
|
||||
name_regex = "^centos_6\\w{1,5}[64]{1}.*"
|
||||
}
|
||||
`
|
||||
|
||||
// Testing image not in first page response
|
||||
const testAccCheckAlicloudImagesDataSourceImageNotInFirstPageConfig = `
|
||||
data "alicloud_images" "name_regex_filtered_image" {
|
||||
most_recent = true
|
||||
owners = "system"
|
||||
name_regex = "^ubuntu_14.*_64"
|
||||
}
|
||||
`
|
||||
|
@ -17,8 +17,6 @@ func TestAccAlicloudInstanceTypesDataSource_basic(t *testing.T) {
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_instance_types.4c8g"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.#", "4"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.cpu_core_count", "4"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.memory_size", "8"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_instance_types.4c8g", "instance_types.0.id", "ecs.s3.large"),
|
||||
|
@ -71,11 +71,6 @@ func TestAccAlicloudRegionsDataSource_empty(t *testing.T) {
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_regions.empty_params_region"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "name", ""),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "current", ""),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.#", "13"),
|
||||
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.id", "cn-shenzhen"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.region_id", "cn-shenzhen"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_regions.empty_params_region", "regions.0.local_name", "华南 1"),
|
||||
|
@ -1,7 +1,10 @@
|
||||
package alicloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -23,6 +26,7 @@ func TestAccAlicloudZonesDataSource_basic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
@ -33,7 +37,7 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||
Config: testAccCheckAlicloudZonesDataSourceFilter,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "2"),
|
||||
testCheckZoneLength("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
|
||||
@ -41,13 +45,59 @@ func TestAccAlicloudZonesDataSource_filter(t *testing.T) {
|
||||
Config: testAccCheckAlicloudZonesDataSourceFilterIoOptimized,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
resource.TestCheckResourceAttr("data.alicloud_zones.foo", "zones.#", "1"),
|
||||
testCheckZoneLength("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudZonesDataSource_unitRegion(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccCheckAlicloudZonesDataSource_unitRegion,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAlicloudDataSourceID("data.alicloud_zones.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// the zone length changed occasionally
|
||||
// check by range to avoid test case failure
|
||||
func testCheckZoneLength(name string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
ms := s.RootModule()
|
||||
rs, ok := ms.Resources[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", name)
|
||||
}
|
||||
|
||||
is := rs.Primary
|
||||
if is == nil {
|
||||
return fmt.Errorf("No primary instance: %s", name)
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(is.Attributes["zones.#"])
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("convert zone length err: %#v", err)
|
||||
}
|
||||
|
||||
if i <= 0 {
|
||||
return fmt.Errorf("zone length expected greater than 0 got err: %d", i)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccCheckAlicloudZonesDataSourceBasicConfig = `
|
||||
data "alicloud_zones" "foo" {
|
||||
}
|
||||
@ -55,16 +105,28 @@ data "alicloud_zones" "foo" {
|
||||
|
||||
const testAccCheckAlicloudZonesDataSourceFilter = `
|
||||
data "alicloud_zones" "foo" {
|
||||
"available_instance_type"= "ecs.c2.xlarge"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
available_instance_type= "ecs.c2.xlarge"
|
||||
available_resource_creation= "VSwitch"
|
||||
available_disk_category= "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCheckAlicloudZonesDataSourceFilterIoOptimized = `
|
||||
data "alicloud_zones" "foo" {
|
||||
"available_instance_type"= "ecs.c2.xlarge"
|
||||
"available_resource_creation"= "IoOptimized"
|
||||
"available_disk_category"= "cloud"
|
||||
available_instance_type= "ecs.c2.xlarge"
|
||||
available_resource_creation= "IoOptimized"
|
||||
available_disk_category= "cloud"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccCheckAlicloudZonesDataSource_unitRegion = `
|
||||
provider "alicloud" {
|
||||
alias = "northeast"
|
||||
region = "ap-northeast-1"
|
||||
}
|
||||
|
||||
data "alicloud_zones" "foo" {
|
||||
provider = "alicloud.northeast"
|
||||
available_resource_creation= "VSwitch"
|
||||
}
|
||||
`
|
||||
|
@ -9,6 +9,7 @@ const (
|
||||
DiskIncorrectStatus = "IncorrectDiskStatus"
|
||||
DiskCreatingSnapshot = "DiskCreatingSnapshot"
|
||||
InstanceLockedForSecurity = "InstanceLockedForSecurity"
|
||||
SystemDiskNotFound = "SystemDiskNotFound"
|
||||
// eip
|
||||
EipIncorrectStatus = "IncorrectEipStatus"
|
||||
InstanceIncorrectStatus = "IncorrectInstanceStatus"
|
||||
|
@ -30,3 +30,8 @@ const (
|
||||
GroupRulePolicyAccept = GroupRulePolicy("accept")
|
||||
GroupRulePolicyDrop = GroupRulePolicy("drop")
|
||||
)
|
||||
|
||||
const (
|
||||
EcsApiVersion20160314 = "2016-03-14"
|
||||
EcsApiVersion20140526 = "2014-05-26"
|
||||
)
|
||||
|
@ -8,13 +8,41 @@ import (
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
slb.HTTPListenerType
|
||||
|
||||
InstancePort int
|
||||
LoadBalancerPort int
|
||||
Protocol string
|
||||
//tcp & udp
|
||||
PersistenceTimeout int
|
||||
|
||||
//https
|
||||
SSLCertificateId string
|
||||
Bandwidth int
|
||||
|
||||
//tcp
|
||||
HealthCheckType slb.HealthCheckType
|
||||
|
||||
//api interface: http & https is HealthCheckTimeout, tcp & udp is HealthCheckConnectTimeout
|
||||
HealthCheckConnectTimeout int
|
||||
}
|
||||
|
||||
type ListenerErr struct {
|
||||
ErrType string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ListenerErr) Error() string {
|
||||
return e.ErrType + " " + e.Err.Error()
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
HealthCheckErrType = "healthCheckErrType"
|
||||
StickySessionErrType = "stickySessionErrType"
|
||||
CookieTimeOutErrType = "cookieTimeoutErrType"
|
||||
CookieErrType = "cookieErrType"
|
||||
)
|
||||
|
||||
// Takes the result of flatmap.Expand for an array of listeners and
|
||||
// returns ELB API compatible objects
|
||||
func expandListeners(configured []interface{}) ([]*Listener, error) {
|
||||
@ -31,13 +59,78 @@ func expandListeners(configured []interface{}) ([]*Listener, error) {
|
||||
InstancePort: ip,
|
||||
LoadBalancerPort: lp,
|
||||
Protocol: data["lb_protocol"].(string),
|
||||
Bandwidth: data["bandwidth"].(int),
|
||||
}
|
||||
|
||||
l.Bandwidth = data["bandwidth"].(int)
|
||||
|
||||
if v, ok := data["scheduler"]; ok {
|
||||
l.Scheduler = slb.SchedulerType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["ssl_certificate_id"]; ok {
|
||||
l.SSLCertificateId = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["sticky_session"]; ok {
|
||||
l.StickySession = slb.FlagType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["sticky_session_type"]; ok {
|
||||
l.StickySessionType = slb.StickySessionType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["cookie_timeout"]; ok {
|
||||
l.CookieTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["cookie"]; ok {
|
||||
l.Cookie = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["persistence_timeout"]; ok {
|
||||
l.PersistenceTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check"]; ok {
|
||||
l.HealthCheck = slb.FlagType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_type"]; ok {
|
||||
l.HealthCheckType = slb.HealthCheckType(v.(string))
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_domain"]; ok {
|
||||
l.HealthCheckDomain = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_uri"]; ok {
|
||||
l.HealthCheckURI = v.(string)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_connect_port"]; ok {
|
||||
l.HealthCheckConnectPort = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["healthy_threshold"]; ok {
|
||||
l.HealthyThreshold = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["unhealthy_threshold"]; ok {
|
||||
l.UnhealthyThreshold = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_timeout"]; ok {
|
||||
l.HealthCheckTimeout = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_interval"]; ok {
|
||||
l.HealthCheckInterval = v.(int)
|
||||
}
|
||||
|
||||
if v, ok := data["health_check_http_code"]; ok {
|
||||
l.HealthCheckHttpCode = slb.HealthCheckHttpCodeType(v.(string))
|
||||
}
|
||||
|
||||
var valid bool
|
||||
if l.SSLCertificateId != "" {
|
||||
// validate the protocol is correct
|
||||
|
@ -26,7 +26,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"region": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", "cn-beijing"),
|
||||
DefaultFunc: schema.EnvDefaultFunc("ALICLOUD_REGION", DEFAULT_REGION),
|
||||
Description: descriptions["region"],
|
||||
},
|
||||
},
|
||||
@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"alicloud_disk_attachment": resourceAliyunDiskAttachment(),
|
||||
"alicloud_security_group": resourceAliyunSecurityGroup(),
|
||||
"alicloud_security_group_rule": resourceAliyunSecurityGroupRule(),
|
||||
"alicloud_db_instance": resourceAlicloudDBInstance(),
|
||||
"alicloud_vpc": resourceAliyunVpc(),
|
||||
"alicloud_nat_gateway": resourceAliyunNatGateway(),
|
||||
//both subnet and vswith exists,cause compatible old version, and compatible aws habit.
|
||||
|
545
builtin/providers/alicloud/resource_alicloud_db_instance.go
Normal file
545
builtin/providers/alicloud/resource_alicloud_db_instance.go
Normal file
@ -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
|
||||
}
|
765
builtin/providers/alicloud/resource_alicloud_db_instance_test.go
Normal file
765
builtin/providers/alicloud/resource_alicloud_db_instance_test.go
Normal file
@ -0,0 +1,765 @@
|
||||
package alicloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccAlicloudDBInstance_basic(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstanceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"Classic"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_vpc(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_vpc,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"VPC"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestC2CAlicloudDBInstance_prepaid_order(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_prepaid_order,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"port",
|
||||
"3306"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_storage",
|
||||
"10"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"instance_network_type",
|
||||
"VPC"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"db_instance_net_type",
|
||||
"Intranet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine_version",
|
||||
"5.6"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_db_instance.foo",
|
||||
"engine",
|
||||
"MySQL"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_multiIZ(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_multiIZ,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
testAccCheckDBInstanceMultiIZ(&instance),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_database(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_database,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_database_update,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "3"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_account(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_grantDatabasePrivilege2Account,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_mappings.#", "2"),
|
||||
testAccCheckAccountHasPrivilege2Database("alicloud_db_instance.foo", "tester", "foo", "ReadWrite"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_allocatePublicConnection(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_allocatePublicConnection,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "connections.#", "2"),
|
||||
testAccCheckHasPublicConnection("alicloud_db_instance.foo"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_backupPolicy(t *testing.T) {
|
||||
var policies []map[string]interface{}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_backup,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckBackupPolicyExists(
|
||||
"alicloud_db_instance.foo", policies),
|
||||
testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_period", "Wednesday,Thursday"),
|
||||
testAccCheckKeyValueInMaps(policies, "backup policy", "preferred_backup_time", "00:00Z-01:00Z"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_securityIps(t *testing.T) {
|
||||
var ips []map[string]interface{}
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_securityIps,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityIpExists(
|
||||
"alicloud_db_instance.foo", ips),
|
||||
testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "127.0.0.1"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_securityIpsConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityIpExists(
|
||||
"alicloud_db_instance.foo", ips),
|
||||
testAccCheckKeyValueInMaps(ips, "security ip", "security_ips", "10.168.1.12,100.69.7.112"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudDBInstance_upgradeClass(t *testing.T) {
|
||||
var instance rds.DBInstanceAttribute
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_db_instance.foo",
|
||||
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckDBInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_class,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.t1.small"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccDBInstance_classUpgrade,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckDBInstanceExists(
|
||||
"alicloud_db_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr("alicloud_db_instance.foo", "db_instance_class", "rds.mysql.s1.small"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckSecurityIpExists(n string, ips []map[string]interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
args := rds.DescribeDBInstanceIPsArgs{
|
||||
DBInstanceId: rs.Primary.ID,
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeDBInstanceIPs(&args)
|
||||
log.Printf("[DEBUG] check instance %s security ip %#v", rs.Primary.ID, resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := resp.Items.DBInstanceIPArray
|
||||
|
||||
if len(p) < 1 {
|
||||
return fmt.Errorf("DB security ip not found")
|
||||
}
|
||||
|
||||
ips = flattenDBSecurityIPs(p)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceMultiIZ(i *rds.DBInstanceAttribute) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if !strings.Contains(i.ZoneId, MULTI_IZ_SYMBOL) {
|
||||
return fmt.Errorf("Current region does not support multiIZ.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAccountHasPrivilege2Database(n, accountName, dbName, privilege string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
if err := conn.WaitForAccountPrivilege(rs.Primary.ID, accountName, dbName, rds.AccountPrivilege(privilege), 50); err != nil {
|
||||
return fmt.Errorf("Failed to grant database %s privilege to account %s: %v", dbName, accountName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHasPublicConnection(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
if err := conn.WaitForPublicConnection(rs.Primary.ID, 50); err != nil {
|
||||
return fmt.Errorf("Failed to allocate public connection: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceExists(n string, d *rds.DBInstanceAttribute) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
attr, err := client.DescribeDBInstanceById(rs.Primary.ID)
|
||||
log.Printf("[DEBUG] check instance %s attribute %#v", rs.Primary.ID, attr)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if attr == nil {
|
||||
return fmt.Errorf("DB Instance not found")
|
||||
}
|
||||
|
||||
*d = *attr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckBackupPolicyExists(n string, ps []map[string]interface{}) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Backup policy not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No DB Instance ID is set")
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*AliyunClient).rdsconn
|
||||
|
||||
args := rds.DescribeBackupPolicyArgs{
|
||||
DBInstanceId: rs.Primary.ID,
|
||||
}
|
||||
resp, err := conn.DescribeBackupPolicy(&args)
|
||||
log.Printf("[DEBUG] check instance %s backup policy %#v", rs.Primary.ID, resp)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bs []rds.BackupPolicy
|
||||
bs = append(bs, resp.BackupPolicy)
|
||||
ps = flattenDBBackup(bs)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckKeyValueInMaps(ps []map[string]interface{}, propName, key, value string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
for _, policy := range ps {
|
||||
if policy[key].(string) != value {
|
||||
return fmt.Errorf("DB %s attribute '%s' expected %#v, got %#v", propName, key, value, policy[key])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckDBInstanceDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "alicloud_db_instance.foo" {
|
||||
continue
|
||||
}
|
||||
|
||||
ins, err := client.DescribeDBInstanceById(rs.Primary.ID)
|
||||
|
||||
if ins != nil {
|
||||
return fmt.Errorf("Error DB Instance still exist")
|
||||
}
|
||||
|
||||
// Verify the error is what we want
|
||||
if err != nil {
|
||||
// Verify the error is what we want
|
||||
e, _ := err.(*common.Error)
|
||||
if e.ErrorResponse.Code == InstanceNotfound {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const testAccDBInstanceConfig = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_vpc = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_multiIZ = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
multi_az = true
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_prepaid_order = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Prepaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_database = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_database_update = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "zzz"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_grantDatabasePrivilege2Account = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
master_user_name = "tester"
|
||||
master_user_password = "Test12345"
|
||||
|
||||
db_mappings = [
|
||||
{
|
||||
"db_name" = "foo"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
},{
|
||||
"db_name" = "bar"
|
||||
"character_set_name" = "utf8"
|
||||
"db_description" = "tf"
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_allocatePublicConnection = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
master_user_name = "tester"
|
||||
master_user_password = "Test12345"
|
||||
|
||||
allocate_public_connection = true
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_backup = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
preferred_backup_period = ["Wednesday","Thursday"]
|
||||
preferred_backup_time = "00:00Z-01:00Z"
|
||||
backup_retention_period = 9
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_securityIps = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_securityIpsConfig = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
instance_charge_type = "Postpaid"
|
||||
db_instance_net_type = "Intranet"
|
||||
|
||||
security_ips = ["10.168.1.12", "100.69.7.112"]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccDBInstance_class = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.t1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
||||
const testAccDBInstance_classUpgrade = `
|
||||
resource "alicloud_db_instance" "foo" {
|
||||
engine = "MySQL"
|
||||
engine_version = "5.6"
|
||||
db_instance_class = "rds.mysql.s1.small"
|
||||
db_instance_storage = "10"
|
||||
db_instance_net_type = "Intranet"
|
||||
}
|
||||
`
|
@ -151,4 +151,5 @@ resource "alicloud_security_group" "group" {
|
||||
name = "terraform-test-group"
|
||||
description = "New security group"
|
||||
}
|
||||
|
||||
`
|
||||
|
@ -136,9 +136,13 @@ func testAccCheckDiskDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
const testAccDiskConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
}
|
||||
|
||||
resource "alicloud_disk" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
name = "New-disk"
|
||||
description = "Hello ecs disk."
|
||||
category = "cloud_efficiency"
|
||||
@ -146,10 +150,15 @@ resource "alicloud_disk" "foo" {
|
||||
}
|
||||
`
|
||||
const testAccDiskConfigWithTags = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
}
|
||||
|
||||
resource "alicloud_disk" "bar" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
size = "10"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
category = "cloud_efficiency"
|
||||
size = "20"
|
||||
tags {
|
||||
Name = "TerraformTest"
|
||||
}
|
||||
|
@ -108,6 +108,10 @@ func testAccCheckEIPAssociationDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
const testAccEIPAssociationConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "main" {
|
||||
cidr_block = "10.1.0.0/21"
|
||||
}
|
||||
@ -115,19 +119,23 @@ resource "alicloud_vpc" "main" {
|
||||
resource "alicloud_vswitch" "main" {
|
||||
vpc_id = "${alicloud_vpc.main.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-a"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
depends_on = [
|
||||
"alicloud_vpc.main"]
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "instance" {
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_type = "ecs.s1.small"
|
||||
availability_zone = "cn-beijing-a"
|
||||
security_groups = ["${alicloud_security_group.group.id}"]
|
||||
# cn-beijing
|
||||
vswitch_id = "${alicloud_vswitch.main.id}"
|
||||
instance_name = "hello"
|
||||
io_optimized = "none"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
|
||||
security_groups = ["${alicloud_security_group.group.id}"]
|
||||
instance_name = "test_foo"
|
||||
|
||||
tags {
|
||||
Name = "TerraformTest-instance"
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
@ -21,8 +22,9 @@ func resourceAliyunInstance() *schema.Resource {
|
||||
Schema: map[string]*schema.Schema{
|
||||
"availability_zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"image_id": &schema.Schema{
|
||||
@ -60,11 +62,6 @@ func resourceAliyunInstance() *schema.Resource {
|
||||
ValidateFunc: validateInstanceDescription,
|
||||
},
|
||||
|
||||
"instance_network_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"internet_charge_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
@ -104,11 +101,19 @@ func resourceAliyunInstance() *schema.Resource {
|
||||
Default: "cloud",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(ecs.DiskCategoryCloud),
|
||||
string(ecs.DiskCategoryCloudSSD),
|
||||
string(ecs.DiskCategoryCloudEfficiency),
|
||||
string(ecs.DiskCategoryEphemeralSSD),
|
||||
}),
|
||||
},
|
||||
"system_disk_size": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateIntegerInRange(40, 500),
|
||||
},
|
||||
|
||||
//subnet_id and vswitch_id both exists, cause compatible old version, and aws habit.
|
||||
@ -145,7 +150,6 @@ func resourceAliyunInstance() *schema.Resource {
|
||||
|
||||
"private_ip": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
@ -168,6 +172,11 @@ func resourceAliyunInstance() *schema.Resource {
|
||||
func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
|
||||
// create postpaid instance by runInstances API
|
||||
if v := d.Get("instance_charge_type").(string); v != string(common.PrePaid) {
|
||||
return resourceAliyunRunInstance(d, meta)
|
||||
}
|
||||
|
||||
args, err := buildAliyunInstanceArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -181,7 +190,8 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
||||
d.SetId(instanceID)
|
||||
|
||||
d.Set("password", d.Get("password"))
|
||||
d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
//d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
//d.Set("system_disk_size", d.Get("system_disk_size"))
|
||||
|
||||
if d.Get("allocate_public_ip").(bool) {
|
||||
_, err := conn.AllocatePublicIpAddress(d.Id())
|
||||
@ -207,11 +217,56 @@ func resourceAliyunInstanceCreate(d *schema.ResourceData, meta interface{}) erro
|
||||
return resourceAliyunInstanceUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceAliyunRunInstance(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
newConn := meta.(*AliyunClient).ecsNewconn
|
||||
|
||||
args, err := buildAliyunInstanceArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runArgs, err := buildAliyunRunInstancesArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runArgs.CreateInstanceArgs = *args
|
||||
|
||||
// runInstances is support in version 2016-03-14
|
||||
instanceIds, err := newConn.RunInstances(runArgs)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating Aliyun ecs instance: %#v", err)
|
||||
}
|
||||
|
||||
d.SetId(instanceIds[0])
|
||||
|
||||
d.Set("password", d.Get("password"))
|
||||
d.Set("system_disk_category", d.Get("system_disk_category"))
|
||||
d.Set("system_disk_size", d.Get("system_disk_size"))
|
||||
|
||||
if d.Get("allocate_public_ip").(bool) {
|
||||
_, err := conn.AllocatePublicIpAddress(d.Id())
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] AllocatePublicIpAddress for instance got error: %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// after instance created, its status change from pending, starting to running
|
||||
if err := conn.WaitForInstanceAsyn(d.Id(), ecs.Running, defaultTimeout); err != nil {
|
||||
log.Printf("[DEBUG] WaitForInstance %s got error: %#v", ecs.Running, err)
|
||||
}
|
||||
|
||||
return resourceAliyunInstanceUpdate(d, meta)
|
||||
}
|
||||
|
||||
func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.ecsconn
|
||||
|
||||
instance, err := client.QueryInstancesById(d.Id())
|
||||
|
||||
if err != nil {
|
||||
if notFoundError(err) {
|
||||
d.SetId("")
|
||||
@ -220,7 +275,15 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
||||
return fmt.Errorf("Error DescribeInstanceAttribute: %#v", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] DescribeInstanceAttribute for instance: %#v", instance)
|
||||
disk, diskErr := client.QueryInstanceSystemDisk(d.Id())
|
||||
|
||||
if diskErr != nil {
|
||||
if notFoundError(diskErr) {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Error DescribeSystemDisk: %#v", err)
|
||||
}
|
||||
|
||||
d.Set("instance_name", instance.InstanceName)
|
||||
d.Set("description", instance.Description)
|
||||
@ -229,6 +292,8 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
||||
d.Set("host_name", instance.HostName)
|
||||
d.Set("image_id", instance.ImageId)
|
||||
d.Set("instance_type", instance.InstanceType)
|
||||
d.Set("system_disk_category", disk.Category)
|
||||
d.Set("system_disk_size", disk.Size)
|
||||
|
||||
// In Classic network, internet_charge_type is valid in any case, and its default value is 'PayByBanwidth'.
|
||||
// In VPC network, internet_charge_type is valid when instance has public ip, and its default value is 'PayByBanwidth'.
|
||||
@ -244,10 +309,6 @@ func resourceAliyunInstanceRead(d *schema.ResourceData, meta interface{}) error
|
||||
d.Set("io_optimized", "none")
|
||||
}
|
||||
|
||||
log.Printf("instance.InternetChargeType: %#v", instance.InternetChargeType)
|
||||
|
||||
d.Set("instance_network_type", instance.InstanceNetworkType)
|
||||
|
||||
if d.Get("subnet_id").(string) != "" || d.Get("vswitch_id").(string) != "" {
|
||||
ipAddress := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
d.Set("private_ip", ipAddress)
|
||||
@ -414,33 +475,71 @@ func resourceAliyunInstanceDelete(d *schema.ResourceData, meta interface{}) erro
|
||||
|
||||
return nil
|
||||
}
|
||||
func buildAliyunRunInstancesArgs(d *schema.ResourceData, meta interface{}) (*ecs.RunInstanceArgs, error) {
|
||||
args := &ecs.RunInstanceArgs{
|
||||
MaxAmount: DEFAULT_INSTANCE_COUNT,
|
||||
MinAmount: DEFAULT_INSTANCE_COUNT,
|
||||
}
|
||||
|
||||
bussStr, err := json.Marshal(DefaultBusinessInfo)
|
||||
if err != nil {
|
||||
log.Printf("Failed to translate bussiness info %#v from json to string", DefaultBusinessInfo)
|
||||
}
|
||||
|
||||
args.BusinessInfo = string(bussStr)
|
||||
|
||||
subnetValue := d.Get("subnet_id").(string)
|
||||
vswitchValue := d.Get("vswitch_id").(string)
|
||||
//networkValue := d.Get("instance_network_type").(string)
|
||||
|
||||
// because runInstance is not compatible with createInstance, force NetworkType value to classic
|
||||
if subnetValue == "" && vswitchValue == "" {
|
||||
args.NetworkType = string(ClassicNet)
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateInstanceArgs, error) {
|
||||
client := meta.(*AliyunClient)
|
||||
|
||||
args := &ecs.CreateInstanceArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
InstanceType: d.Get("instance_type").(string),
|
||||
PrivateIpAddress: d.Get("private_ip").(string),
|
||||
RegionId: getRegion(d, meta),
|
||||
InstanceType: d.Get("instance_type").(string),
|
||||
}
|
||||
|
||||
imageID := d.Get("image_id").(string)
|
||||
|
||||
args.ImageId = imageID
|
||||
|
||||
systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string))
|
||||
systemDiskSize := d.Get("system_disk_size").(int)
|
||||
|
||||
zoneID := d.Get("availability_zone").(string)
|
||||
// check instanceType and systemDiskCategory, when zoneID is not empty
|
||||
if zoneID != "" {
|
||||
zone, err := client.DescribeZone(zoneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args.ZoneId = zoneID
|
||||
|
||||
zone, err := client.DescribeZone(zoneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := client.ResourceAvailable(zone, ecs.ResourceTypeInstance); err != nil {
|
||||
return nil, err
|
||||
args.SystemDisk = ecs.SystemDiskType{
|
||||
Category: systemDiskCategory,
|
||||
Size: systemDiskSize,
|
||||
}
|
||||
|
||||
args.ZoneId = zoneID
|
||||
|
||||
sgs, ok := d.GetOk("security_groups")
|
||||
|
||||
if ok {
|
||||
@ -451,17 +550,6 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||
if err == nil {
|
||||
args.SecurityGroupId = sg0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
systemDiskCategory := ecs.DiskCategory(d.Get("system_disk_category").(string))
|
||||
|
||||
if err := client.DiskAvailable(zone, systemDiskCategory); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args.SystemDisk = ecs.SystemDiskType{
|
||||
Category: systemDiskCategory,
|
||||
}
|
||||
|
||||
if v := d.Get("instance_name").(string); v != "" {
|
||||
@ -472,7 +560,7 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||
args.Description = v
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] internet_charge_type is %s", d.Get("internet_charge_type").(string))
|
||||
log.Printf("[DEBUG] SystemDisk is %d", systemDiskSize)
|
||||
if v := d.Get("internet_charge_type").(string); v != "" {
|
||||
args.InternetChargeType = common.InternetChargeType(v)
|
||||
}
|
||||
@ -490,7 +578,11 @@ func buildAliyunInstanceArgs(d *schema.ResourceData, meta interface{}) (*ecs.Cre
|
||||
}
|
||||
|
||||
if v := d.Get("io_optimized").(string); v != "" {
|
||||
args.IoOptimized = ecs.IoOptimized(v)
|
||||
if v == "optimized" {
|
||||
args.IoOptimized = ecs.IoOptimized("true")
|
||||
} else {
|
||||
args.IoOptimized = ecs.IoOptimized("false")
|
||||
}
|
||||
}
|
||||
|
||||
vswitchValue := d.Get("subnet_id").(string)
|
||||
|
@ -56,6 +56,7 @@ func TestAccAlicloudInstance_basic(t *testing.T) {
|
||||
"alicloud_instance.foo",
|
||||
"internet_charge_type",
|
||||
"PayByBandwidth"),
|
||||
testAccCheckSystemDiskSize("alicloud_instance.foo", 80),
|
||||
),
|
||||
},
|
||||
|
||||
@ -355,10 +356,6 @@ func TestAccAlicloudInstance_tags(t *testing.T) {
|
||||
Config: testAccCheckInstanceConfigTagsUpdate,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_instance.foo",
|
||||
"tags.foo",
|
||||
""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_instance.foo",
|
||||
"tags.bar",
|
||||
@ -418,8 +415,8 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) {
|
||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
if privateIP != "172.16.0.229" {
|
||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
||||
if privateIP == "" {
|
||||
return fmt.Errorf("can't get private IP")
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -445,14 +442,14 @@ func TestAccAlicloudInstance_privateIP(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||
func TestAccAlicloudInstance_associatePublicIP(t *testing.T) {
|
||||
var instance ecs.InstanceAttributesType
|
||||
|
||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
privateIP := instance.VpcAttributes.PrivateIpAddress.IpAddress[0]
|
||||
if privateIP != "172.16.0.229" {
|
||||
return fmt.Errorf("bad private IP: %s", privateIP)
|
||||
if privateIP == "" {
|
||||
return fmt.Errorf("can't get private IP")
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -468,7 +465,7 @@ func TestAccAlicloudInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||
CheckDestroy: testAccCheckInstanceDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccInstanceConfigAssociatePublicIPAndPrivateIP,
|
||||
Config: testAccInstanceConfigAssociatePublicIP,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckInstanceExists("alicloud_instance.foo", &instance),
|
||||
testCheckPrivateIP(),
|
||||
@ -597,6 +594,36 @@ func testAccCheckInstanceDestroyWithProvider(s *terraform.State, provider *schem
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckSystemDiskSize(n string, size int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
providers := []*schema.Provider{testAccProvider}
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
for _, provider := range providers {
|
||||
if provider.Meta() == nil {
|
||||
continue
|
||||
}
|
||||
client := provider.Meta().(*AliyunClient)
|
||||
systemDisk, err := client.QueryInstanceSystemDisk(rs.Primary.ID)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR]get system disk size error: %#v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if systemDisk.Size != size {
|
||||
return fmt.Errorf("system disk size not equal %d, the instance system size is %d",
|
||||
size, systemDisk.Size)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccInstanceConfig = `
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
name = "tf_test_foo"
|
||||
@ -609,11 +636,10 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
system_disk_category = "cloud_ssd"
|
||||
system_disk_size = 80
|
||||
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
@ -628,15 +654,20 @@ resource "alicloud_instance" "foo" {
|
||||
}
|
||||
`
|
||||
const testAccInstanceConfigVPC = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
@ -647,7 +678,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
@ -666,15 +696,20 @@ resource "alicloud_instance" "foo" {
|
||||
`
|
||||
|
||||
const testAccInstanceConfigUserData = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
@ -685,7 +720,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
# series II
|
||||
@ -725,24 +759,22 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
provider = "alicloud.beijing"
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
# cn-beijing
|
||||
provider = "alicloud.beijing"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
instance_type = "ecs.n1.medium"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "bar" {
|
||||
# cn-shanghai
|
||||
provider = "alicloud.shanghai"
|
||||
availability_zone = "cn-shanghai-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
@ -768,7 +800,6 @@ resource "alicloud_security_group" "tf_test_bar" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
@ -776,6 +807,7 @@ resource "alicloud_instance" "foo" {
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}", "${alicloud_security_group.tf_test_bar.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}`
|
||||
|
||||
const testAccInstanceConfig_multiSecurityGroup_add = `
|
||||
@ -796,7 +828,6 @@ resource "alicloud_security_group" "tf_test_add_sg" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
@ -805,6 +836,7 @@ resource "alicloud_instance" "foo" {
|
||||
"${alicloud_security_group.tf_test_add_sg.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
@ -814,9 +846,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
@ -824,6 +877,7 @@ resource "alicloud_instance" "foo" {
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
@ -836,27 +890,32 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
instance_type = "ecs.s2.large"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.*.id}"]
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "none"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccInstanceNetworkInstanceSecurityGroups = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
@ -867,7 +926,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
@ -892,7 +950,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
@ -918,7 +975,6 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
@ -941,9 +997,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
@ -965,9 +1042,30 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_32_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
@ -984,15 +1082,20 @@ resource "alicloud_instance" "foo" {
|
||||
`
|
||||
|
||||
const testAccInstanceConfigPrivateIP = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
@ -1003,11 +1106,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
private_ip = "172.16.0.229"
|
||||
|
||||
# series II
|
||||
instance_type = "ecs.n1.medium"
|
||||
@ -1017,16 +1118,21 @@ resource "alicloud_instance" "foo" {
|
||||
instance_name = "test_foo"
|
||||
}
|
||||
`
|
||||
const testAccInstanceConfigAssociatePublicIPAndPrivateIP = `
|
||||
const testAccInstanceConfigAssociatePublicIP = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "cn-beijing-b"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
@ -1037,11 +1143,9 @@ resource "alicloud_security_group" "tf_test_foo" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
private_ip = "172.16.0.229"
|
||||
allocate_public_ip = "true"
|
||||
internet_max_bandwidth_out = 5
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
@ -1055,52 +1159,56 @@ resource "alicloud_instance" "foo" {
|
||||
}
|
||||
`
|
||||
const testAccVpcInstanceWithSecurityRule = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_disk_category"= "cloud_efficiency"
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
}
|
||||
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-c"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "tf_test_foo" {
|
||||
name = "tf_test_foo"
|
||||
description = "foo"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
name = "tf_test_foo"
|
||||
description = "foo"
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ingress" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.tf_test_foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-c"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
# cn-beijing
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
allocate_public_ip = true
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
allocate_public_ip = true
|
||||
|
||||
# series II
|
||||
instance_charge_type = "PostPaid"
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_max_bandwidth_out = 5
|
||||
# series II
|
||||
instance_charge_type = "PostPaid"
|
||||
instance_type = "ecs.n1.small"
|
||||
internet_charge_type = "PayByBandwidth"
|
||||
internet_max_bandwidth_out = 5
|
||||
|
||||
system_disk_category = "cloud_efficiency"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
system_disk_category = "cloud_efficiency"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
instance_name = "test_foo"
|
||||
io_optimized = "optimized"
|
||||
}
|
||||
|
||||
`
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"log"
|
||||
@ -71,7 +72,7 @@ func resourceAliyunNatGateway() *schema.Resource {
|
||||
func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).vpcconn
|
||||
|
||||
args := &CreateNatGatewayArgs{
|
||||
args := &ecs.CreateNatGatewayArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
VpcId: d.Get("vpc_id").(string),
|
||||
Spec: d.Get("spec").(string),
|
||||
@ -79,11 +80,11 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
||||
|
||||
bandwidthPackages := d.Get("bandwidth_packages").([]interface{})
|
||||
|
||||
bandwidthPackageTypes := []BandwidthPackageType{}
|
||||
bandwidthPackageTypes := []ecs.BandwidthPackageType{}
|
||||
|
||||
for _, e := range bandwidthPackages {
|
||||
pack := e.(map[string]interface{})
|
||||
bandwidthPackage := BandwidthPackageType{
|
||||
bandwidthPackage := ecs.BandwidthPackageType{
|
||||
IpCount: pack["ip_count"].(int),
|
||||
Bandwidth: pack["bandwidth"].(int),
|
||||
}
|
||||
@ -106,8 +107,7 @@ func resourceAliyunNatGatewayCreate(d *schema.ResourceData, meta interface{}) er
|
||||
if v, ok := d.GetOk("description"); ok {
|
||||
args.Description = v.(string)
|
||||
}
|
||||
|
||||
resp, err := CreateNatGateway(conn, args)
|
||||
resp, err := conn.CreateNatGateway(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("CreateNatGateway got error: %#v", err)
|
||||
}
|
||||
@ -142,6 +142,7 @@ func resourceAliyunNatGatewayRead(d *schema.ResourceData, meta interface{}) erro
|
||||
func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.vpcconn
|
||||
|
||||
natGateway, err := client.DescribeNatGateway(d.Id())
|
||||
if err != nil {
|
||||
@ -150,7 +151,7 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
||||
|
||||
d.Partial(true)
|
||||
attributeUpdate := false
|
||||
args := &ModifyNatGatewayAttributeArgs{
|
||||
args := &ecs.ModifyNatGatewayAttributeArgs{
|
||||
RegionId: natGateway.RegionId,
|
||||
NatGatewayId: natGateway.NatGatewayId,
|
||||
}
|
||||
@ -183,28 +184,28 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
||||
}
|
||||
|
||||
if attributeUpdate {
|
||||
if err := ModifyNatGatewayAttribute(client.vpcconn, args); err != nil {
|
||||
if err := conn.ModifyNatGatewayAttribute(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("spec") {
|
||||
d.SetPartial("spec")
|
||||
var spec NatGatewaySpec
|
||||
var spec ecs.NatGatewaySpec
|
||||
if v, ok := d.GetOk("spec"); ok {
|
||||
spec = NatGatewaySpec(v.(string))
|
||||
spec = ecs.NatGatewaySpec(v.(string))
|
||||
} else {
|
||||
// set default to small spec
|
||||
spec = NatGatewaySmallSpec
|
||||
spec = ecs.NatGatewaySmallSpec
|
||||
}
|
||||
|
||||
args := &ModifyNatGatewaySpecArgs{
|
||||
args := &ecs.ModifyNatGatewaySpecArgs{
|
||||
RegionId: natGateway.RegionId,
|
||||
NatGatewayId: natGateway.NatGatewayId,
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
err := ModifyNatGatewaySpec(client.vpcconn, args)
|
||||
err := conn.ModifyNatGatewaySpec(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%#v %#v", err, *args)
|
||||
}
|
||||
@ -218,10 +219,11 @@ func resourceAliyunNatGatewayUpdate(d *schema.ResourceData, meta interface{}) er
|
||||
func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.vpcconn
|
||||
|
||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
|
||||
packages, err := DescribeBandwidthPackages(client.vpcconn, &DescribeBandwidthPackagesArgs{
|
||||
packages, err := conn.DescribeBandwidthPackages(&ecs.DescribeBandwidthPackagesArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
NatGatewayId: d.Id(),
|
||||
})
|
||||
@ -232,7 +234,7 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
||||
|
||||
retry := false
|
||||
for _, pack := range packages {
|
||||
err = DeleteBandwidthPackage(client.vpcconn, &DeleteBandwidthPackageArgs{
|
||||
err = conn.DeleteBandwidthPackage(&ecs.DeleteBandwidthPackageArgs{
|
||||
RegionId: getRegion(d, meta),
|
||||
BandwidthPackageId: pack.BandwidthPackageId,
|
||||
})
|
||||
@ -251,12 +253,12 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
||||
return resource.RetryableError(fmt.Errorf("Bandwidth package in use - trying again while it is deleted."))
|
||||
}
|
||||
|
||||
args := &DeleteNatGatewayArgs{
|
||||
args := &ecs.DeleteNatGatewayArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: d.Id(),
|
||||
}
|
||||
|
||||
err = DeleteNatGateway(client.vpcconn, args)
|
||||
err = conn.DeleteNatGateway(args)
|
||||
if err != nil {
|
||||
er, _ := err.(*common.Error)
|
||||
if er.ErrorResponse.Code == DependencyViolationBandwidthPackages {
|
||||
@ -264,11 +266,11 @@ func resourceAliyunNatGatewayDelete(d *schema.ResourceData, meta interface{}) er
|
||||
}
|
||||
}
|
||||
|
||||
describeArgs := &DescribeNatGatewaysArgs{
|
||||
describeArgs := &ecs.DescribeNatGatewaysArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: d.Id(),
|
||||
}
|
||||
gw, _, gwErr := DescribeNatGateways(client.vpcconn, describeArgs)
|
||||
gw, _, gwErr := conn.DescribeNatGateways(describeArgs)
|
||||
|
||||
if gwErr != nil {
|
||||
log.Printf("[ERROR] Describe NatGateways failed.")
|
||||
|
@ -3,13 +3,14 @@ package alicloud
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
||||
var nat NatGatewaySetType
|
||||
var nat ecs.NatGatewaySetType
|
||||
|
||||
testCheck := func(*terraform.State) error {
|
||||
if nat.BusinessStatus != "Normal" {
|
||||
@ -55,7 +56,7 @@ func TestAccAlicloudNatGateway_basic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
||||
var nat NatGatewaySetType
|
||||
var nat ecs.NatGatewaySetType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
@ -95,7 +96,7 @@ func TestAccAlicloudNatGateway_spec(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckNatGatewayExists(n string, nat *NatGatewaySetType) resource.TestCheckFunc {
|
||||
func testAccCheckNatGatewayExists(n string, nat *ecs.NatGatewaySetType) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
@ -151,6 +152,10 @@ func testAccCheckNatGatewayDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
const testAccNatGatewayConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
@ -159,7 +164,7 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
@ -169,11 +174,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
@ -181,6 +186,10 @@ resource "alicloud_nat_gateway" "foo" {
|
||||
`
|
||||
|
||||
const testAccNatGatewayConfigSpec = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
@ -189,7 +198,7 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
@ -199,11 +208,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
@ -211,6 +220,10 @@ resource "alicloud_nat_gateway" "foo" {
|
||||
`
|
||||
|
||||
const testAccNatGatewayConfigSpecUpgrade = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
@ -219,7 +232,7 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_nat_gateway" "foo" {
|
||||
@ -229,11 +242,11 @@ resource "alicloud_nat_gateway" "foo" {
|
||||
bandwidth_packages = [{
|
||||
ip_count = 1
|
||||
bandwidth = 5
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}, {
|
||||
ip_count = 2
|
||||
bandwidth = 10
|
||||
zone = "cn-beijing-b"
|
||||
zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}]
|
||||
depends_on = [
|
||||
"alicloud_vswitch.foo"]
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"time"
|
||||
)
|
||||
@ -145,6 +144,7 @@ func resourceAliyunSecurityGroupDelete(d *schema.ResourceData, meta interface{})
|
||||
|
||||
return resource.RetryableError(fmt.Errorf("Security group in use - trying again while it is deleted."))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func buildAliyunSecurityGroupArgs(d *schema.ResourceData, meta interface{}) (*ecs.CreateSecurityGroupArgs, error) {
|
||||
|
@ -34,6 +34,7 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validateSecurityRuleNicType,
|
||||
},
|
||||
|
||||
@ -67,7 +68,6 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
Default: "0.0.0.0/0",
|
||||
},
|
||||
|
||||
"source_security_group_id": &schema.Schema{
|
||||
@ -86,15 +86,17 @@ func resourceAliyunSecurityGroupRule() *schema.Resource {
|
||||
}
|
||||
|
||||
func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
client := meta.(*AliyunClient)
|
||||
conn := client.ecsconn
|
||||
|
||||
ruleType := d.Get("type").(string)
|
||||
direction := d.Get("type").(string)
|
||||
sgId := d.Get("security_group_id").(string)
|
||||
ptl := d.Get("ip_protocol").(string)
|
||||
port := d.Get("port_range").(string)
|
||||
nicType := d.Get("nic_type").(string)
|
||||
|
||||
var autherr error
|
||||
switch GroupRuleDirection(ruleType) {
|
||||
switch GroupRuleDirection(direction) {
|
||||
case GroupRuleIngress:
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
if err != nil {
|
||||
@ -114,10 +116,11 @@ func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interfac
|
||||
if autherr != nil {
|
||||
return fmt.Errorf(
|
||||
"Error authorizing security group rule type %s: %s",
|
||||
ruleType, autherr)
|
||||
direction, autherr)
|
||||
}
|
||||
|
||||
d.SetId(sgId + ":" + ruleType + ":" + ptl + ":" + port)
|
||||
d.SetId(sgId + ":" + direction + ":" + ptl + ":" + port + ":" + nicType)
|
||||
|
||||
return resourceAliyunSecurityGroupRuleRead(d, meta)
|
||||
}
|
||||
|
||||
@ -125,10 +128,11 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||
client := meta.(*AliyunClient)
|
||||
parts := strings.Split(d.Id(), ":")
|
||||
sgId := parts[0]
|
||||
types := parts[1]
|
||||
direction := parts[1]
|
||||
ip_protocol := parts[2]
|
||||
port_range := parts[3]
|
||||
rule, err := client.DescribeSecurityGroupRule(sgId, types, ip_protocol, port_range)
|
||||
nic_type := parts[4]
|
||||
rule, err := client.DescribeSecurityGroupRule(sgId, direction, nic_type, ip_protocol, port_range)
|
||||
|
||||
if err != nil {
|
||||
if notFoundError(err) {
|
||||
@ -137,7 +141,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||
}
|
||||
return fmt.Errorf("Error SecurityGroup rule: %#v", err)
|
||||
}
|
||||
log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, types, ip_protocol, port_range, rule)
|
||||
log.Printf("[WARN]sg %s, type %s, protocol %s, port %s, rule %#v", sgId, direction, ip_protocol, port_range, rule)
|
||||
d.Set("type", rule.Direction)
|
||||
d.Set("ip_protocol", strings.ToLower(string(rule.IpProtocol)))
|
||||
d.Set("nic_type", rule.NicType)
|
||||
@ -146,7 +150,7 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||
d.Set("priority", rule.Priority)
|
||||
d.Set("security_group_id", sgId)
|
||||
//support source and desc by type
|
||||
if GroupRuleDirection(types) == GroupRuleIngress {
|
||||
if GroupRuleDirection(direction) == GroupRuleIngress {
|
||||
d.Set("cidr_ip", rule.SourceCidrIp)
|
||||
d.Set("source_security_group_id", rule.SourceGroupId)
|
||||
d.Set("source_group_owner_account", rule.SourceGroupOwnerAccount)
|
||||
@ -161,17 +165,41 @@ func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{
|
||||
|
||||
func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AliyunClient)
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
ruleType := d.Get("type").(string)
|
||||
|
||||
if GroupRuleDirection(ruleType) == GroupRuleIngress {
|
||||
args, err := buildAliyunSecurityIngressArgs(d, meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokeArgs := &ecs.RevokeSecurityGroupArgs{
|
||||
AuthorizeSecurityGroupArgs: *args,
|
||||
}
|
||||
return client.RevokeSecurityGroup(revokeArgs)
|
||||
}
|
||||
|
||||
args, err := buildAliyunSecurityEgressArgs(d, meta)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
revokeArgs := &ecs.RevokeSecurityGroupArgs{
|
||||
AuthorizeSecurityGroupArgs: *args,
|
||||
revokeArgs := &ecs.RevokeSecurityGroupEgressArgs{
|
||||
AuthorizeSecurityGroupEgressArgs: *args,
|
||||
}
|
||||
return client.RevokeSecurityGroup(revokeArgs)
|
||||
return client.RevokeSecurityGroupEgress(revokeArgs)
|
||||
|
||||
}
|
||||
|
||||
func checkCidrAndSourceGroupId(cidrIp, sourceGroupId string) error {
|
||||
if cidrIp == "" && sourceGroupId == "" {
|
||||
return fmt.Errorf("Either cidr_ip or source_security_group_id is required.")
|
||||
}
|
||||
|
||||
if cidrIp != "" && sourceGroupId != "" {
|
||||
return fmt.Errorf("You should set only one value of cidr_ip or source_security_group_id.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*ecs.AuthorizeSecurityGroupArgs, error) {
|
||||
conn := meta.(*AliyunClient).ecsconn
|
||||
|
||||
@ -199,12 +227,17 @@ func buildAliyunSecurityIngressArgs(d *schema.ResourceData, meta interface{}) (*
|
||||
args.NicType = ecs.NicType(v)
|
||||
}
|
||||
|
||||
if v := d.Get("cidr_ip").(string); v != "" {
|
||||
args.SourceCidrIp = v
|
||||
cidrIp := d.Get("cidr_ip").(string)
|
||||
sourceGroupId := d.Get("source_security_group_id").(string)
|
||||
if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cidrIp != "" {
|
||||
args.SourceCidrIp = cidrIp
|
||||
}
|
||||
|
||||
if v := d.Get("source_security_group_id").(string); v != "" {
|
||||
args.SourceGroupId = v
|
||||
if sourceGroupId != "" {
|
||||
args.SourceGroupId = sourceGroupId
|
||||
}
|
||||
|
||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
||||
@ -255,12 +288,17 @@ func buildAliyunSecurityEgressArgs(d *schema.ResourceData, meta interface{}) (*e
|
||||
args.NicType = ecs.NicType(v)
|
||||
}
|
||||
|
||||
if v := d.Get("cidr_ip").(string); v != "" {
|
||||
args.DestCidrIp = v
|
||||
cidrIp := d.Get("cidr_ip").(string)
|
||||
sourceGroupId := d.Get("source_security_group_id").(string)
|
||||
if err := checkCidrAndSourceGroupId(cidrIp, sourceGroupId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cidrIp != "" {
|
||||
args.DestCidrIp = cidrIp
|
||||
}
|
||||
|
||||
if v := d.Get("source_security_group_id").(string); v != "" {
|
||||
args.DestGroupId = v
|
||||
if sourceGroupId != "" {
|
||||
args.DestGroupId = sourceGroupId
|
||||
}
|
||||
|
||||
if v := d.Get("source_group_owner_account").(string); v != "" {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@ -81,6 +82,39 @@ func TestAccAlicloudSecurityGroupRule_Egress(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_EgressDefaultNicType(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.egress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRuleEgress_emptyNicType,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.egress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"port_range",
|
||||
"80/80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"nic_type",
|
||||
"internet"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
@ -114,6 +148,80 @@ func TestAccAlicloudSecurityGroupRule_Vpc_Ingress(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_MissParameterSourceCidrIp(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.egress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRule_missingSourceCidrIp,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.egress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"port_range",
|
||||
"80/80"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"nic_type",
|
||||
"internet"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.egress",
|
||||
"ip_protocol",
|
||||
"udp"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestAccAlicloudSecurityGroupRule_SourceSecurityGroup(t *testing.T) {
|
||||
var pt ecs.PermissionType
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() {
|
||||
testAccPreCheck(t)
|
||||
},
|
||||
|
||||
// module name
|
||||
IDRefreshName: "alicloud_security_group_rule.ingress",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckSecurityGroupRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccSecurityGroupRuleSourceSecurityGroup,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckSecurityGroupRuleExists(
|
||||
"alicloud_security_group_rule.ingress", &pt),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"port_range",
|
||||
"3306/3306"),
|
||||
resource.TestMatchResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"source_security_group_id",
|
||||
regexp.MustCompile("^sg-[a-zA-Z0-9_]+")),
|
||||
resource.TestCheckResourceAttr(
|
||||
"alicloud_security_group_rule.ingress",
|
||||
"cidr_ip",
|
||||
""),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
@ -128,7 +236,8 @@ func testAccCheckSecurityGroupRuleExists(n string, m *ecs.PermissionType) resour
|
||||
client := testAccProvider.Meta().(*AliyunClient)
|
||||
log.Printf("[WARN]get sg rule %s", rs.Primary.ID)
|
||||
parts := strings.Split(rs.Primary.ID, ":")
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3])
|
||||
// securityGroupId, direction, nicType, ipProtocol, portRange
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3])
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@ -152,7 +261,7 @@ func testAccCheckSecurityGroupRuleDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
parts := strings.Split(rs.Primary.ID, ":")
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[2], parts[3])
|
||||
rule, err := client.DescribeSecurityGroupRule(parts[0], parts[1], parts[4], parts[2], parts[3])
|
||||
|
||||
if rule != nil {
|
||||
return fmt.Errorf("Error SecurityGroup Rule still exist")
|
||||
@ -210,6 +319,23 @@ resource "alicloud_security_group_rule" "egress" {
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleEgress_emptyNicType = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "egress" {
|
||||
type = "egress"
|
||||
ip_protocol = "udp"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "10.159.6.18/12"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleVpcIngress = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
vpc_id = "${alicloud_vpc.vpc.id}"
|
||||
@ -231,6 +357,22 @@ resource "alicloud_security_group_rule" "ingress" {
|
||||
cidr_ip = "10.159.6.18/12"
|
||||
}
|
||||
|
||||
`
|
||||
const testAccSecurityGroupRule_missingSourceCidrIp = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "egress" {
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
type = "egress"
|
||||
cidr_ip= "0.0.0.0/0"
|
||||
policy = "accept"
|
||||
ip_protocol= "udp"
|
||||
port_range= "80/80"
|
||||
priority= 1
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleMultiIngress = `
|
||||
@ -260,4 +402,27 @@ resource "alicloud_security_group_rule" "ingress2" {
|
||||
cidr_ip = "127.0.1.18/16"
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const testAccSecurityGroupRuleSourceSecurityGroup = `
|
||||
resource "alicloud_security_group" "foo" {
|
||||
name = "sg_foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group" "bar" {
|
||||
name = "sg_bar"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ingress" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "intranet"
|
||||
policy = "accept"
|
||||
port_range = "3306/3306"
|
||||
priority = 50
|
||||
security_group_id = "${alicloud_security_group.bar.id}"
|
||||
source_security_group_id = "${alicloud_security_group.foo.id}"
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
@ -83,40 +84,124 @@ func resourceAliyunSlb() *schema.Resource {
|
||||
ValidateFunc: validateSlbListenerBandwidth,
|
||||
Required: true,
|
||||
},
|
||||
//http
|
||||
"scheduler": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerScheduler,
|
||||
Optional: true,
|
||||
Default: "wrr",
|
||||
Default: slb.WRRScheduler,
|
||||
},
|
||||
|
||||
//http & https
|
||||
"sticky_session": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerStickySession,
|
||||
Optional: true,
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.OnFlag),
|
||||
string(slb.OffFlag)}),
|
||||
Optional: true,
|
||||
Default: slb.OffFlag,
|
||||
},
|
||||
//http & https
|
||||
"sticky_session_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerStickySessionType,
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.InsertStickySessionType),
|
||||
string(slb.ServerStickySessionType)}),
|
||||
Optional: true,
|
||||
},
|
||||
//http & https
|
||||
"cookie_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerCookieTimeout,
|
||||
Optional: true,
|
||||
},
|
||||
//http & https
|
||||
"cookie": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerCookie,
|
||||
Optional: true,
|
||||
},
|
||||
"PersistenceTimeout": &schema.Schema{
|
||||
//tcp & udp
|
||||
"persistence_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerPersistenceTimeout,
|
||||
Optional: true,
|
||||
Default: 0,
|
||||
},
|
||||
//http & https
|
||||
"health_check": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.OnFlag),
|
||||
string(slb.OffFlag)}),
|
||||
Optional: true,
|
||||
Default: slb.OffFlag,
|
||||
},
|
||||
//tcp
|
||||
"health_check_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedStringValue([]string{
|
||||
string(slb.TCPHealthCheckType),
|
||||
string(slb.HTTPHealthCheckType)}),
|
||||
Optional: true,
|
||||
Default: slb.TCPHealthCheckType,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_domain": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerHealthCheckDomain,
|
||||
Optional: true,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_uri": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateSlbListenerHealthCheckUri,
|
||||
Optional: true,
|
||||
},
|
||||
"health_check_connect_port": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateSlbListenerHealthCheckConnectPort,
|
||||
Optional: true,
|
||||
},
|
||||
"healthy_threshold": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 10),
|
||||
Optional: true,
|
||||
},
|
||||
"unhealthy_threshold": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 10),
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"health_check_timeout": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 50),
|
||||
Optional: true,
|
||||
},
|
||||
"health_check_interval": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
ValidateFunc: validateIntegerInRange(1, 5),
|
||||
Optional: true,
|
||||
},
|
||||
//http & https & tcp
|
||||
"health_check_http_code": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
ValidateFunc: validateAllowedSplitStringValue([]string{
|
||||
string(slb.HTTP_2XX),
|
||||
string(slb.HTTP_3XX),
|
||||
string(slb.HTTP_4XX),
|
||||
string(slb.HTTP_5XX)}, ","),
|
||||
Optional: true,
|
||||
},
|
||||
//https
|
||||
"ssl_certificate_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
//https
|
||||
//"ca_certificate_id": &schema.Schema{
|
||||
// Type: schema.TypeString,
|
||||
// Optional: true,
|
||||
//},
|
||||
},
|
||||
},
|
||||
Set: resourceAliyunSlbListenerHash,
|
||||
@ -349,44 +434,53 @@ func resourceAliyunSlbListenerHash(v interface{}) int {
|
||||
}
|
||||
|
||||
func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
|
||||
|
||||
errTypeJudge := func(err error) error {
|
||||
if err != nil {
|
||||
if listenerType, ok := err.(*ListenerErr); ok {
|
||||
if listenerType.ErrType == HealthCheckErrType {
|
||||
return fmt.Errorf("When the HealthCheck is %s, then related HealthCheck parameter "+
|
||||
"must have.", slb.OnFlag)
|
||||
} else if listenerType.ErrType == StickySessionErrType {
|
||||
return fmt.Errorf("When the StickySession is %s, then StickySessionType parameter "+
|
||||
"must have.", slb.OnFlag)
|
||||
} else if listenerType.ErrType == CookieTimeOutErrType {
|
||||
return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
|
||||
"then CookieTimeout parameter must have.", slb.OnFlag, slb.InsertStickySessionType)
|
||||
} else if listenerType.ErrType == CookieErrType {
|
||||
return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
|
||||
"then Cookie parameter must have.", slb.OnFlag, slb.ServerStickySessionType)
|
||||
}
|
||||
return fmt.Errorf("slb listener check errtype not found.")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("tcp") {
|
||||
args := &slb.CreateLoadBalancerTCPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
}
|
||||
if err := conn.CreateLoadBalancerTCPListener(args); err != nil {
|
||||
|
||||
args := getTcpListenerArgs(loadBalancerId, listener)
|
||||
|
||||
if err := conn.CreateLoadBalancerTCPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("http") {
|
||||
args := &slb.CreateLoadBalancerHTTPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
StickySession: slb.OffFlag,
|
||||
HealthCheck: slb.OffFlag,
|
||||
} else if listener.Protocol == strings.ToLower("http") {
|
||||
args, argsErr := getHttpListenerArgs(loadBalancerId, listener)
|
||||
if paramErr := errTypeJudge(argsErr); paramErr != nil {
|
||||
return paramErr
|
||||
}
|
||||
|
||||
if err := conn.CreateLoadBalancerHTTPListener(args); err != nil {
|
||||
if err := conn.CreateLoadBalancerHTTPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if listener.Protocol == strings.ToLower("https") {
|
||||
listenerType, err := getHttpListenerType(loadBalancerId, listener)
|
||||
if paramErr := errTypeJudge(err); paramErr != nil {
|
||||
return paramErr
|
||||
}
|
||||
|
||||
if listener.Protocol == strings.ToLower("https") {
|
||||
args := &slb.CreateLoadBalancerHTTPSListenerArgs{
|
||||
|
||||
HTTPListenerType: slb.HTTPListenerType{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
StickySession: slb.OffFlag,
|
||||
HealthCheck: slb.OffFlag,
|
||||
},
|
||||
HTTPListenerType: listenerType,
|
||||
}
|
||||
if listener.SSLCertificateId == "" {
|
||||
return fmt.Errorf("Server Certificated Id cann't be null")
|
||||
@ -397,17 +491,10 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener)
|
||||
if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if listener.Protocol == strings.ToLower("udp") {
|
||||
args := getUdpListenerArgs(loadBalancerId, listener)
|
||||
|
||||
if listener.Protocol == strings.ToLower("udp") {
|
||||
args := &slb.CreateLoadBalancerUDPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
}
|
||||
|
||||
if err := conn.CreateLoadBalancerUDPListener(args); err != nil {
|
||||
if err := conn.CreateLoadBalancerUDPListener(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -418,3 +505,102 @@ func createListener(conn *slb.Client, loadBalancerId string, listener *Listener)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTcpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerTCPListenerArgs {
|
||||
args := slb.CreateLoadBalancerTCPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
Scheduler: listener.Scheduler,
|
||||
PersistenceTimeout: listener.PersistenceTimeout,
|
||||
HealthCheckType: listener.HealthCheckType,
|
||||
HealthCheckDomain: listener.HealthCheckDomain,
|
||||
HealthCheckURI: listener.HealthCheckURI,
|
||||
HealthCheckConnectPort: listener.HealthCheckConnectPort,
|
||||
HealthyThreshold: listener.HealthyThreshold,
|
||||
UnhealthyThreshold: listener.UnhealthyThreshold,
|
||||
HealthCheckConnectTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
HealthCheckHttpCode: listener.HealthCheckHttpCode,
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func getUdpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerUDPListenerArgs {
|
||||
args := slb.CreateLoadBalancerUDPListenerArgs{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
PersistenceTimeout: listener.PersistenceTimeout,
|
||||
HealthCheckConnectTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func getHttpListenerType(loadBalancerId string, listener *Listener) (listenType slb.HTTPListenerType, err error) {
|
||||
|
||||
if listener.HealthCheck == slb.OnFlag {
|
||||
if listener.HealthCheckURI == "" || listener.HealthCheckDomain == "" || listener.HealthCheckConnectPort == 0 ||
|
||||
listener.HealthyThreshold == 0 || listener.UnhealthyThreshold == 0 || listener.HealthCheckTimeout == 0 ||
|
||||
listener.HealthCheckHttpCode == "" || listener.HealthCheckInterval == 0 {
|
||||
|
||||
errMsg := errors.New("err: HealthCheck empty.")
|
||||
return listenType, &ListenerErr{HealthCheckErrType, errMsg}
|
||||
}
|
||||
}
|
||||
|
||||
if listener.StickySession == slb.OnFlag {
|
||||
if listener.StickySessionType == "" {
|
||||
errMsg := errors.New("err: stickySession empty.")
|
||||
return listenType, &ListenerErr{StickySessionErrType, errMsg}
|
||||
}
|
||||
|
||||
if listener.StickySessionType == slb.InsertStickySessionType {
|
||||
if listener.CookieTimeout == 0 {
|
||||
errMsg := errors.New("err: cookieTimeout empty.")
|
||||
return listenType, &ListenerErr{CookieTimeOutErrType, errMsg}
|
||||
}
|
||||
} else if listener.StickySessionType == slb.ServerStickySessionType {
|
||||
if listener.Cookie == "" {
|
||||
errMsg := errors.New("err: cookie empty.")
|
||||
return listenType, &ListenerErr{CookieErrType, errMsg}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpListenertType := slb.HTTPListenerType{
|
||||
LoadBalancerId: loadBalancerId,
|
||||
ListenerPort: listener.LoadBalancerPort,
|
||||
BackendServerPort: listener.InstancePort,
|
||||
Bandwidth: listener.Bandwidth,
|
||||
Scheduler: listener.Scheduler,
|
||||
HealthCheck: listener.HealthCheck,
|
||||
StickySession: listener.StickySession,
|
||||
StickySessionType: listener.StickySessionType,
|
||||
CookieTimeout: listener.CookieTimeout,
|
||||
Cookie: listener.Cookie,
|
||||
HealthCheckDomain: listener.HealthCheckDomain,
|
||||
HealthCheckURI: listener.HealthCheckURI,
|
||||
HealthCheckConnectPort: listener.HealthCheckConnectPort,
|
||||
HealthyThreshold: listener.HealthyThreshold,
|
||||
UnhealthyThreshold: listener.UnhealthyThreshold,
|
||||
HealthCheckTimeout: listener.HealthCheckTimeout,
|
||||
HealthCheckInterval: listener.HealthCheckInterval,
|
||||
HealthCheckHttpCode: listener.HealthCheckHttpCode,
|
||||
}
|
||||
|
||||
return httpListenertType, err
|
||||
}
|
||||
|
||||
func getHttpListenerArgs(loadBalancerId string, listener *Listener) (listenType slb.CreateLoadBalancerHTTPListenerArgs, err error) {
|
||||
httpListenerType, err := getHttpListenerType(loadBalancerId, listener)
|
||||
if err != nil {
|
||||
return listenType, err
|
||||
}
|
||||
|
||||
httpArgs := slb.CreateLoadBalancerHTTPListenerArgs(httpListenerType)
|
||||
return httpArgs, err
|
||||
}
|
||||
|
@ -79,9 +79,30 @@ resource "alicloud_security_group" "foo" {
|
||||
description = "foo"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "http-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "80/80"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_security_group_rule" "ssh-in" {
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
nic_type = "internet"
|
||||
policy = "accept"
|
||||
port_range = "22/22"
|
||||
priority = 1
|
||||
security_group_id = "${alicloud_security_group.foo.id}"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
}
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-b"
|
||||
image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
|
||||
|
||||
# series II
|
||||
|
@ -85,7 +85,7 @@ func TestAccAlicloudSlb_listener(t *testing.T) {
|
||||
testListener := func() resource.TestCheckFunc {
|
||||
return func(*terraform.State) error {
|
||||
listenerPorts := slb.ListenerPorts.ListenerPort[0]
|
||||
if listenerPorts != 161 {
|
||||
if listenerPorts != 2001 {
|
||||
return fmt.Errorf("bad loadbalancer listener: %#v", listenerPorts)
|
||||
}
|
||||
|
||||
@ -260,21 +260,49 @@ resource "alicloud_slb" "listener" {
|
||||
"lb_port" = "21"
|
||||
"lb_protocol" = "tcp"
|
||||
"bandwidth" = 1
|
||||
"persistence_timeout" = 500
|
||||
"health_check_type" = "http"
|
||||
},{
|
||||
"instance_port" = "8000"
|
||||
"lb_port" = "80"
|
||||
"lb_protocol" = "http"
|
||||
"sticky_session" = "on"
|
||||
"sticky_session_type" = "insert"
|
||||
"cookie_timeout" = 800
|
||||
"bandwidth" = 1
|
||||
},{
|
||||
"instance_port" = "1611"
|
||||
"lb_port" = "161"
|
||||
"instance_port" = "8001"
|
||||
"lb_port" = "81"
|
||||
"lb_protocol" = "http"
|
||||
"sticky_session" = "on"
|
||||
"sticky_session_type" = "server"
|
||||
"cookie" = "testslblistenercookie"
|
||||
"cookie_timeout" = 1800
|
||||
"health_check" = "on"
|
||||
"health_check_domain" = "$_ip"
|
||||
"health_check_uri" = "/console"
|
||||
"health_check_connect_port" = 20
|
||||
"healthy_threshold" = 8
|
||||
"unhealthy_threshold" = 8
|
||||
"health_check_timeout" = 8
|
||||
"health_check_interval" = 4
|
||||
"health_check_http_code" = "http_2xx"
|
||||
"bandwidth" = 1
|
||||
},{
|
||||
"instance_port" = "2001"
|
||||
"lb_port" = "2001"
|
||||
"lb_protocol" = "udp"
|
||||
"bandwidth" = 1
|
||||
"persistence_timeout" = 700
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testAccSlb4Vpc = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
@ -283,7 +311,7 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_slb" "vpc" {
|
||||
|
@ -124,6 +124,10 @@ func testAccCheckRouteEntryDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
const testAccRouteEntryConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "10.1.0.0/21"
|
||||
@ -132,7 +136,7 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "10.1.1.0/24"
|
||||
availability_zone = "cn-beijing-c"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
|
||||
resource "alicloud_route_entry" "foo" {
|
||||
@ -162,7 +166,6 @@ resource "alicloud_security_group_rule" "ingress" {
|
||||
|
||||
resource "alicloud_instance" "foo" {
|
||||
# cn-beijing
|
||||
availability_zone = "cn-beijing-c"
|
||||
security_groups = ["${alicloud_security_group.tf_test_foo.id}"]
|
||||
|
||||
vswitch_id = "${alicloud_vswitch.foo.id}"
|
||||
|
@ -92,6 +92,10 @@ func testAccCheckVswitchDestroy(s *terraform.State) error {
|
||||
}
|
||||
|
||||
const testAccVswitchConfig = `
|
||||
data "alicloud_zones" "default" {
|
||||
"available_resource_creation"= "VSwitch"
|
||||
}
|
||||
|
||||
resource "alicloud_vpc" "foo" {
|
||||
name = "tf_test_foo"
|
||||
cidr_block = "172.16.0.0/12"
|
||||
@ -100,6 +104,6 @@ resource "alicloud_vpc" "foo" {
|
||||
resource "alicloud_vswitch" "foo" {
|
||||
vpc_id = "${alicloud_vpc.foo.id}"
|
||||
cidr_block = "172.16.0.0/21"
|
||||
availability_zone = "cn-beijing-b"
|
||||
availability_zone = "${data.alicloud_zones.default.zones.0.id}"
|
||||
}
|
||||
`
|
||||
|
@ -84,6 +84,24 @@ func (client *AliyunClient) DescribeZone(zoneID string) (*ecs.ZoneType, error) {
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
// return multiIZ list of current region
|
||||
func (client *AliyunClient) DescribeMultiIZByRegion() (izs []string, err error) {
|
||||
resp, err := client.rdsconn.DescribeRegions()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error to list regions not found")
|
||||
}
|
||||
regions := resp.Regions.RDSRegion
|
||||
|
||||
zoneIds := []string{}
|
||||
for _, r := range regions {
|
||||
if r.RegionId == string(client.Region) && strings.Contains(r.ZoneId, MULTI_IZ_SYMBOL) {
|
||||
zoneIds = append(zoneIds, r.ZoneId)
|
||||
}
|
||||
}
|
||||
|
||||
return zoneIds, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) QueryInstancesByIds(ids []string) (instances []ecs.InstanceAttributesType, err error) {
|
||||
idsStr, jerr := json.Marshal(ids)
|
||||
if jerr != nil {
|
||||
@ -119,6 +137,23 @@ func (client *AliyunClient) QueryInstancesById(id string) (instance *ecs.Instanc
|
||||
return &instances[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) QueryInstanceSystemDisk(id string) (disk *ecs.DiskItemType, err error) {
|
||||
args := ecs.DescribeDisksArgs{
|
||||
RegionId: client.Region,
|
||||
InstanceId: string(id),
|
||||
DiskType: ecs.DiskTypeAllSystem,
|
||||
}
|
||||
disks, _, err := client.ecsconn.DescribeDisks(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(disks) == 0 {
|
||||
return nil, common.GetClientErrorFromString(SystemDiskNotFound)
|
||||
}
|
||||
|
||||
return &disks[0], nil
|
||||
}
|
||||
|
||||
// ResourceAvailable check resource available for zone
|
||||
func (client *AliyunClient) ResourceAvailable(zone *ecs.ZoneType, resourceType ecs.ResourceType) error {
|
||||
available := false
|
||||
@ -186,15 +221,26 @@ func (client *AliyunClient) DescribeSecurity(securityGroupId string) (*ecs.Descr
|
||||
return client.ecsconn.DescribeSecurityGroupAttribute(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip_protocol, port_range string) (*ecs.PermissionType, error) {
|
||||
func (client *AliyunClient) DescribeSecurityByAttr(securityGroupId, direction, nicType string) (*ecs.DescribeSecurityGroupAttributeResponse, error) {
|
||||
|
||||
sg, err := client.DescribeSecurity(securityGroupId)
|
||||
args := &ecs.DescribeSecurityGroupAttributeArgs{
|
||||
RegionId: client.Region,
|
||||
SecurityGroupId: securityGroupId,
|
||||
Direction: direction,
|
||||
NicType: ecs.NicType(nicType),
|
||||
}
|
||||
|
||||
return client.ecsconn.DescribeSecurityGroupAttribute(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, direction, nicType, ipProtocol, portRange string) (*ecs.PermissionType, error) {
|
||||
sg, err := client.DescribeSecurityByAttr(securityGroupId, direction, nicType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, p := range sg.Permissions.Permission {
|
||||
if strings.ToLower(string(p.IpProtocol)) == ip_protocol && p.PortRange == port_range {
|
||||
if strings.ToLower(string(p.IpProtocol)) == ipProtocol && p.PortRange == portRange {
|
||||
return &p, nil
|
||||
}
|
||||
}
|
||||
@ -203,6 +249,11 @@ func (client *AliyunClient) DescribeSecurityGroupRule(securityGroupId, types, ip
|
||||
}
|
||||
|
||||
func (client *AliyunClient) RevokeSecurityGroup(args *ecs.RevokeSecurityGroupArgs) error {
|
||||
//todo: handle the specal err
|
||||
//when the rule is not exist, api will return success(200)
|
||||
return client.ecsconn.RevokeSecurityGroup(args)
|
||||
}
|
||||
|
||||
func (client *AliyunClient) RevokeSecurityGroupEgress(args *ecs.RevokeSecurityGroupEgressArgs) error {
|
||||
//when the rule is not exist, api will return success(200)
|
||||
return client.ecsconn.RevokeSecurityGroupEgress(args)
|
||||
}
|
||||
|
278
builtin/providers/alicloud/service_alicloud_rds.go
Normal file
278
builtin/providers/alicloud/service_alicloud_rds.go
Normal file
@ -0,0 +1,278 @@
|
||||
package alicloud
|
||||
|
||||
import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/rds"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// when getInstance is empty, then throw InstanceNotfound error
|
||||
func (client *AliyunClient) DescribeDBInstanceById(id string) (instance *rds.DBInstanceAttribute, err error) {
|
||||
arrtArgs := rds.DescribeDBInstancesArgs{
|
||||
DBInstanceId: id,
|
||||
}
|
||||
resp, err := client.rdsconn.DescribeDBInstanceAttribute(&arrtArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attr := resp.Items.DBInstanceAttribute
|
||||
|
||||
if len(attr) <= 0 {
|
||||
return nil, common.GetClientErrorFromString(InstanceNotfound)
|
||||
}
|
||||
|
||||
return &attr[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) CreateAccountByInfo(instanceId, username, pwd string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.CreateAccountArgs{
|
||||
DBInstanceId: instanceId,
|
||||
AccountName: username,
|
||||
AccountPassword: pwd,
|
||||
}
|
||||
|
||||
if _, err := conn.CreateAccount(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForAccount(instanceId, username, rds.Available, 200); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) CreateDatabaseByInfo(instanceId, dbName, charset, desp string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.CreateDatabaseArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBName: dbName,
|
||||
CharacterSetName: charset,
|
||||
DBDescription: desp,
|
||||
}
|
||||
_, err := conn.CreateDatabase(&args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeDatabaseByName(instanceId, dbName string) (ds []rds.Database, err error) {
|
||||
conn := client.rdsconn
|
||||
args := rds.DescribeDatabasesArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBName: dbName,
|
||||
}
|
||||
|
||||
resp, err := conn.DescribeDatabases(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Databases.Database, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GrantDBPrivilege2Account(instanceId, username, dbName string) error {
|
||||
conn := client.rdsconn
|
||||
pargs := rds.GrantAccountPrivilegeArgs{
|
||||
DBInstanceId: instanceId,
|
||||
AccountName: username,
|
||||
DBName: dbName,
|
||||
AccountPrivilege: rds.ReadWrite,
|
||||
}
|
||||
if _, err := conn.GrantAccountPrivilege(&pargs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForAccountPrivilege(instanceId, username, dbName, rds.ReadWrite, 200); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) AllocateDBPublicConnection(instanceId, port string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.AllocateInstancePublicConnectionArgs{
|
||||
DBInstanceId: instanceId,
|
||||
ConnectionStringPrefix: instanceId + "o",
|
||||
Port: port,
|
||||
}
|
||||
|
||||
if _, err := conn.AllocateInstancePublicConnection(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForPublicConnection(instanceId, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ConfigDBBackup(instanceId, backupTime, backupPeriod string, retentionPeriod int) error {
|
||||
bargs := rds.BackupPolicy{
|
||||
PreferredBackupTime: backupTime,
|
||||
PreferredBackupPeriod: backupPeriod,
|
||||
BackupRetentionPeriod: retentionPeriod,
|
||||
}
|
||||
args := rds.ModifyBackupPolicyArgs{
|
||||
DBInstanceId: instanceId,
|
||||
BackupPolicy: bargs,
|
||||
}
|
||||
|
||||
if _, err := client.rdsconn.ModifyBackupPolicy(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ModifyDBSecurityIps(instanceId, ips string) error {
|
||||
sargs := rds.DBInstanceIPArray{
|
||||
SecurityIps: ips,
|
||||
}
|
||||
|
||||
args := rds.ModifySecurityIpsArgs{
|
||||
DBInstanceId: instanceId,
|
||||
DBInstanceIPArray: sargs,
|
||||
}
|
||||
|
||||
if _, err := client.rdsconn.ModifySecurityIps(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.rdsconn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeDBSecurityIps(instanceId string) (ips []rds.DBInstanceIPList, err error) {
|
||||
args := rds.DescribeDBInstanceIPsArgs{
|
||||
DBInstanceId: instanceId,
|
||||
}
|
||||
|
||||
resp, err := client.rdsconn.DescribeDBInstanceIPs(&args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Items.DBInstanceIPArray, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GetSecurityIps(instanceId string) ([]string, error) {
|
||||
arr, err := client.DescribeDBSecurityIps(instanceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ips := ""
|
||||
for i, ip := range arr {
|
||||
if i == 0 {
|
||||
ips += ip.SecurityIPList
|
||||
} else {
|
||||
ips += COMMA_SEPARATED + ip.SecurityIPList
|
||||
}
|
||||
}
|
||||
return strings.Split(ips, COMMA_SEPARATED), nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) ModifyDBClassStorage(instanceId, class, storage string) error {
|
||||
conn := client.rdsconn
|
||||
args := rds.ModifyDBInstanceSpecArgs{
|
||||
DBInstanceId: instanceId,
|
||||
PayType: rds.Postpaid,
|
||||
DBInstanceClass: class,
|
||||
DBInstanceStorage: storage,
|
||||
}
|
||||
|
||||
if _, err := conn.ModifyDBInstanceSpec(&args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conn.WaitForInstance(instanceId, rds.Running, 600); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// turn period to TimeType
|
||||
func TransformPeriod2Time(period int, chargeType string) (ut int, tt common.TimeType) {
|
||||
if chargeType == string(rds.Postpaid) {
|
||||
return 1, common.Day
|
||||
}
|
||||
|
||||
if period >= 1 && period <= 9 {
|
||||
return period, common.Month
|
||||
}
|
||||
|
||||
if period == 12 {
|
||||
return 1, common.Year
|
||||
}
|
||||
|
||||
if period == 24 {
|
||||
return 2, common.Year
|
||||
}
|
||||
return 0, common.Day
|
||||
|
||||
}
|
||||
|
||||
// turn TimeType to Period
|
||||
func TransformTime2Period(ut int, tt common.TimeType) (period int) {
|
||||
if tt == common.Year {
|
||||
return 12 * ut
|
||||
}
|
||||
|
||||
return ut
|
||||
|
||||
}
|
||||
|
||||
// Flattens an array of databases into a []map[string]interface{}
|
||||
func flattenDatabaseMappings(list []rds.Database) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"db_name": i.DBName,
|
||||
"character_set_name": i.CharacterSetName,
|
||||
"db_description": i.DBDescription,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenDBBackup(list []rds.BackupPolicy) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"preferred_backup_period": i.PreferredBackupPeriod,
|
||||
"preferred_backup_time": i.PreferredBackupTime,
|
||||
"backup_retention_period": i.LogBackupRetentionPeriod,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func flattenDBSecurityIPs(list []rds.DBInstanceIPList) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"security_ips": i.SecurityIPList,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Flattens an array of databases connection into a []map[string]interface{}
|
||||
func flattenDBConnections(list []rds.DBInstanceNetInfo) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(list))
|
||||
for _, i := range list {
|
||||
l := map[string]interface{}{
|
||||
"connection_string": i.ConnectionString,
|
||||
"ip_type": i.IPType,
|
||||
"ip_address": i.IPAddress,
|
||||
}
|
||||
result = append(result, l)
|
||||
}
|
||||
return result
|
||||
}
|
@ -24,14 +24,14 @@ func (client *AliyunClient) DescribeEipAddress(allocationId string) (*ecs.EipAdd
|
||||
return &eips[0], nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*NatGatewaySetType, error) {
|
||||
func (client *AliyunClient) DescribeNatGateway(natGatewayId string) (*ecs.NatGatewaySetType, error) {
|
||||
|
||||
args := &DescribeNatGatewaysArgs{
|
||||
args := &ecs.DescribeNatGatewaysArgs{
|
||||
RegionId: client.Region,
|
||||
NatGatewayId: natGatewayId,
|
||||
}
|
||||
|
||||
natGateways, _, err := DescribeNatGateways(client.ecsconn, args)
|
||||
natGateways, _, err := client.vpcconn.DescribeNatGateways(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -132,3 +132,23 @@ func (client *AliyunClient) QueryRouteEntry(routeTableId, cidrBlock, nextHopType
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (client *AliyunClient) GetVpcIdByVSwitchId(vswitchId string) (vpcId string, err error) {
|
||||
|
||||
vs, _, err := client.ecsconn.DescribeVpcs(&ecs.DescribeVpcsArgs{
|
||||
RegionId: client.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, v := range vs {
|
||||
for _, sw := range v.VSwitchIds.VSwitchId {
|
||||
if sw == vswitchId {
|
||||
return v.VpcId, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", &common.Error{ErrorResponse: common.ErrorResponse{Message: Notfound}}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/denverdino/aliyungo/common"
|
||||
"github.com/denverdino/aliyungo/ecs"
|
||||
"github.com/denverdino/aliyungo/slb"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
@ -355,48 +356,29 @@ func validateSlbListenerScheduler(v interface{}, k string) (ws []string, errors
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerStickySession(v interface{}, k string) (ws []string, errors []error) {
|
||||
if value := v.(string); value != "" {
|
||||
flag := slb.FlagType(value)
|
||||
|
||||
if flag != "on" && flag != "off" {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid StickySession, expected %s or %s, got %q",
|
||||
k, "on", "off", value))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerStickySessionType(v interface{}, k string) (ws []string, errors []error) {
|
||||
if value := v.(string); value != "" {
|
||||
flag := slb.StickySessionType(value)
|
||||
|
||||
if flag != "insert" && flag != "server" {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid StickySessionType, expected %s or %s, got %q",
|
||||
k, "insert", "server", value))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateSlbListenerCookie(v interface{}, k string) (ws []string, errors []error) {
|
||||
if value := v.(string); value != "" {
|
||||
flag := slb.StickySessionType(value)
|
||||
|
||||
if flag != "insert" && flag != "server" {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must contain a valid StickySessionType, expected %s or %s, got %q",
|
||||
k, "insert", "server", 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) {
|
||||
value := v.(int)
|
||||
if value < 0 || value > 86400 {
|
||||
if value < 0 || value > 3600 {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q must be a valid load balancer persistence timeout between 0 and 86400",
|
||||
k))
|
||||
@ -405,6 +387,138 @@ func validateSlbListenerPersistenceTimeout(v interface{}, k string) (ws []string
|
||||
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_alicloud_image
|
||||
func validateNameRegex(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func GetCredentials(c *Config) (*awsCredentials.Credentials, error) {
|
||||
if usedEndpoint == "" {
|
||||
usedEndpoint = "default location"
|
||||
}
|
||||
log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+
|
||||
log.Printf("[INFO] Ignoring AWS metadata API endpoint at %s "+
|
||||
"as it doesn't return any instance-id", usedEndpoint)
|
||||
}
|
||||
}
|
||||
|
@ -443,10 +443,10 @@ func expandLambdaFunctionAssociation(lf map[string]interface{}) *cloudfront.Lamb
|
||||
return &lfa
|
||||
}
|
||||
|
||||
func flattenLambdaFunctionAssociations(lfa *cloudfront.LambdaFunctionAssociations) []interface{} {
|
||||
s := make([]interface{}, len(lfa.Items))
|
||||
for i, v := range lfa.Items {
|
||||
s[i] = flattenLambdaFunctionAssociation(v)
|
||||
func flattenLambdaFunctionAssociations(lfa *cloudfront.LambdaFunctionAssociations) *schema.Set {
|
||||
s := schema.NewSet(lambdaFunctionAssociationHash, []interface{}{})
|
||||
for _, v := range lfa.Items {
|
||||
s.Add(flattenLambdaFunctionAssociation(v))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
@ -364,14 +364,8 @@ func TestCloudFrontStructure_flattenCacheBehavior(t *testing.T) {
|
||||
t.Fatalf("Expected out[target_origin_id] to be myS3Origin, got %v", out["target_origin_id"])
|
||||
}
|
||||
|
||||
// the flattened lambda function associations are a slice of maps,
|
||||
// where as the default cache behavior LFAs are a set. Here we double check
|
||||
// that and conver the slice to a set, and use Set's Equal() method to check
|
||||
// equality
|
||||
var outSet *schema.Set
|
||||
if outSlice, ok := out["lambda_function_association"].([]interface{}); ok {
|
||||
outSet = schema.NewSet(lambdaFunctionAssociationHash, outSlice)
|
||||
} else {
|
||||
var outSet, ok = out["lambda_function_association"].(*schema.Set)
|
||||
if !ok {
|
||||
t.Fatalf("out['lambda_function_association'] is not a slice as expected: %#v", out["lambda_function_association"])
|
||||
}
|
||||
|
||||
@ -496,7 +490,7 @@ func TestCloudFrontStructure_flattenlambdaFunctionAssociations(t *testing.T) {
|
||||
lfa := expandLambdaFunctionAssociations(in.List())
|
||||
out := flattenLambdaFunctionAssociations(lfa)
|
||||
|
||||
if reflect.DeepEqual(in.List(), out) != true {
|
||||
if reflect.DeepEqual(in.List(), out.List()) != true {
|
||||
t.Fatalf("Expected out to be %v, got %v", in, out)
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +160,14 @@ type AWSClient struct {
|
||||
wafconn *waf.WAF
|
||||
}
|
||||
|
||||
func (c *AWSClient) S3() *s3.S3 {
|
||||
return c.s3conn
|
||||
}
|
||||
|
||||
func (c *AWSClient) DynamoDB() *dynamodb.DynamoDB {
|
||||
return c.dynamodbconn
|
||||
}
|
||||
|
||||
// Client configures and returns a fully initialized AWSClient
|
||||
func (c *Config) Client() (interface{}, error) {
|
||||
// Get the auth and region. This can fail if keys/regions were not
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/sts"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
@ -17,24 +18,33 @@ func dataSourceAwsCallerIdentity() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"arn": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"user_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsCallerIdentityRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*AWSClient)
|
||||
client := meta.(*AWSClient).stsconn
|
||||
|
||||
log.Printf("[DEBUG] Reading Caller Identity.")
|
||||
d.SetId(time.Now().UTC().String())
|
||||
|
||||
if client.accountid == "" {
|
||||
log.Println("[DEBUG] No Account ID available, failing")
|
||||
return fmt.Errorf("No AWS Account ID is available to the provider. Please ensure that\n" +
|
||||
"skip_requesting_account_id is not set on the AWS provider.")
|
||||
res, err := client.GetCallerIdentity(&sts.GetCallerIdentityInput{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error getting Caller Identity: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Setting AWS Account ID to %s.", client.accountid)
|
||||
d.Set("account_id", meta.(*AWSClient).accountid)
|
||||
log.Printf("[DEBUG] Received Caller Identity: %s", res)
|
||||
|
||||
d.SetId(time.Now().UTC().String())
|
||||
d.Set("account_id", res.Account)
|
||||
d.Set("arn", res.Arn)
|
||||
d.Set("user_id", res.UserId)
|
||||
return nil
|
||||
}
|
||||
|
@ -39,6 +39,14 @@ func testAccCheckAwsCallerIdentityAccountId(n string) resource.TestCheckFunc {
|
||||
return fmt.Errorf("Incorrect Account ID: expected %q, got %q", expected, rs.Primary.Attributes["account_id"])
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["user_id"] == "" {
|
||||
return fmt.Errorf("UserID expected to not be nil")
|
||||
}
|
||||
|
||||
if rs.Primary.Attributes["arn"] == "" {
|
||||
return fmt.Errorf("ARN expected to not be nil")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ func dataSourceAwsCloudFormationStack() *schema.Resource {
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
"iam_role_arn": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"tags": {
|
||||
Type: schema.TypeMap,
|
||||
Computed: true,
|
||||
@ -86,6 +90,7 @@ func dataSourceAwsCloudFormationStackRead(d *schema.ResourceData, meta interface
|
||||
d.Set("description", stack.Description)
|
||||
d.Set("disable_rollback", stack.DisableRollback)
|
||||
d.Set("timeout_in_minutes", stack.TimeoutInMinutes)
|
||||
d.Set("iam_role_arn", stack.RoleARN)
|
||||
|
||||
if len(stack.NotificationARNs) > 0 {
|
||||
d.Set("notification_arns", schema.NewSet(schema.HashString, flattenStringList(stack.NotificationARNs)))
|
||||
|
@ -15,10 +15,10 @@ func TestAccAWSEcsDataSource_ecsTaskDefinition(t *testing.T) {
|
||||
resource.TestStep{
|
||||
Config: testAccCheckAwsEcsTaskDefinitionDataSourceConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "id", regexp.MustCompile("^arn:aws:ecs:us-west-2:[0-9]{12}:task-definition/mongodb:[1-9]*[0-9]$")),
|
||||
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "id", regexp.MustCompile("^arn:aws:ecs:us-west-2:[0-9]{12}:task-definition/mongodb:[1-9][0-9]*$")),
|
||||
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "family", "mongodb"),
|
||||
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "network_mode", "bridge"),
|
||||
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "revision", regexp.MustCompile("^[1-9]*[0-9]$")),
|
||||
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "revision", regexp.MustCompile("^[1-9][0-9]*$")),
|
||||
resource.TestCheckResourceAttr("data.aws_ecs_task_definition.mongo", "status", "ACTIVE"),
|
||||
resource.TestMatchResourceAttr("data.aws_ecs_task_definition.mongo", "task_role_arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/mongo_role$")),
|
||||
),
|
||||
|
67
builtin/providers/aws/data_source_aws_iam_role.go
Normal file
67
builtin/providers/aws/data_source_aws_iam_role.go
Normal file
@ -0,0 +1,67 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsIAMRole() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsIAMRoleRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"arn": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"assume_role_policy_document": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"path": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"role_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"role_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsIAMRoleRead(d *schema.ResourceData, meta interface{}) error {
|
||||
iamconn := meta.(*AWSClient).iamconn
|
||||
|
||||
roleName := d.Get("role_name").(string)
|
||||
|
||||
req := &iam.GetRoleInput{
|
||||
RoleName: aws.String(roleName),
|
||||
}
|
||||
|
||||
resp, err := iamconn.GetRole(req)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error getting roles: {{err}}", err)
|
||||
}
|
||||
if resp == nil {
|
||||
return fmt.Errorf("no IAM role found")
|
||||
}
|
||||
|
||||
role := resp.Role
|
||||
|
||||
d.SetId(*role.RoleId)
|
||||
d.Set("arn", role.Arn)
|
||||
d.Set("assume_role_policy_document", role.AssumeRolePolicyDocument)
|
||||
d.Set("path", role.Path)
|
||||
d.Set("role_id", role.RoleId)
|
||||
|
||||
return nil
|
||||
}
|
59
builtin/providers/aws/data_source_aws_iam_role_test.go
Normal file
59
builtin/providers/aws/data_source_aws_iam_role_test.go
Normal file
@ -0,0 +1,59 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSDataSourceIAMRole_basic(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsIAMRoleConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("data.aws_iam_role.test", "role_id"),
|
||||
resource.TestCheckResourceAttr("data.aws_iam_role.test", "assume_role_policy_document", "%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D"),
|
||||
resource.TestCheckResourceAttr("data.aws_iam_role.test", "path", "/testpath/"),
|
||||
resource.TestCheckResourceAttr("data.aws_iam_role.test", "role_name", "TestRole"),
|
||||
resource.TestMatchResourceAttr("data.aws_iam_role.test", "arn", regexp.MustCompile("^arn:aws:iam::[0-9]{12}:role/testpath/TestRole$")),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const testAccAwsIAMRoleConfig = `
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "test_role" {
|
||||
name = "TestRole"
|
||||
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
path = "/testpath/"
|
||||
}
|
||||
|
||||
data "aws_iam_role" "test" {
|
||||
role_name = "${aws_iam_role.test_role.name}"
|
||||
}
|
||||
`
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
@ -18,15 +19,15 @@ func timePtr(t time.Time) *time.Time {
|
||||
|
||||
func TestResourceSortByExpirationDate(t *testing.T) {
|
||||
certs := []*iam.ServerCertificateMetadata{
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("oldest"),
|
||||
Expiration: timePtr(time.Now()),
|
||||
},
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("latest"),
|
||||
Expiration: timePtr(time.Now().Add(3 * time.Hour)),
|
||||
},
|
||||
&iam.ServerCertificateMetadata{
|
||||
{
|
||||
ServerCertificateName: aws.String("in between"),
|
||||
Expiration: timePtr(time.Now().Add(2 * time.Hour)),
|
||||
},
|
||||
@ -38,13 +39,18 @@ func TestResourceSortByExpirationDate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccAWSDataSourceIAMServerCertificate_basic(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsDataIAMServerCertConfig,
|
||||
Config: testAccIAMServerCertConfig(rInt),
|
||||
},
|
||||
{
|
||||
Config: testAccAwsDataIAMServerCertConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("aws_iam_server_certificate.test_cert", "arn"),
|
||||
resource.TestCheckResourceAttrSet("data.aws_iam_server_certificate.test", "arn"),
|
||||
@ -71,12 +77,16 @@ func TestAccAWSDataSourceIAMServerCertificate_matchNamePrefix(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
var testAccAwsDataIAMServerCertConfig = fmt.Sprintf(`%s
|
||||
func testAccAwsDataIAMServerCertConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "aws_iam_server_certificate" "test" {
|
||||
name = "${aws_iam_server_certificate.test_cert.name}"
|
||||
latest = true
|
||||
}
|
||||
`, testAccIAMServerCertConfig)
|
||||
`, testAccIAMServerCertConfig(rInt))
|
||||
}
|
||||
|
||||
var testAccAwsDataIAMServerCertConfigMatchNamePrefix = `
|
||||
data "aws_iam_server_certificate" "test" {
|
||||
|
@ -136,7 +136,7 @@ func dataSourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) erro
|
||||
|
||||
if matchingTags && matchingVPC {
|
||||
if hostedZoneFound != nil {
|
||||
return fmt.Errorf("multplie Route53Zone found please use vpc_id option to filter")
|
||||
return fmt.Errorf("multiple Route53Zone found please use vpc_id option to filter")
|
||||
} else {
|
||||
hostedZoneFound = hostedZone
|
||||
}
|
||||
|
@ -4,69 +4,52 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsRoute53Zone(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
publicResourceName := "aws_route53_zone.test"
|
||||
publicDomain := fmt.Sprintf("terraformtestacchz-%d.com.", rInt)
|
||||
privateResourceName := "aws_route53_zone.test_private"
|
||||
privateDomain := fmt.Sprintf("test.acc-%d.", rInt)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsRoute53ZoneConfig,
|
||||
{
|
||||
Config: testAccDataSourceAwsRoute53ZoneConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsRoute53ZoneCheck("data.aws_route53_zone.by_zone_id"),
|
||||
testAccDataSourceAwsRoute53ZoneCheck("data.aws_route53_zone.by_name"),
|
||||
testAccDataSourceAwsRoute53ZoneCheckPrivate("data.aws_route53_zone.by_vpc"),
|
||||
testAccDataSourceAwsRoute53ZoneCheckPrivate("data.aws_route53_zone.by_tag"),
|
||||
testAccDataSourceAwsRoute53ZoneCheck(
|
||||
publicResourceName, "data.aws_route53_zone.by_zone_id", publicDomain),
|
||||
testAccDataSourceAwsRoute53ZoneCheck(
|
||||
publicResourceName, "data.aws_route53_zone.by_name", publicDomain),
|
||||
testAccDataSourceAwsRoute53ZoneCheck(
|
||||
privateResourceName, "data.aws_route53_zone.by_vpc", privateDomain),
|
||||
testAccDataSourceAwsRoute53ZoneCheck(
|
||||
privateResourceName, "data.aws_route53_zone.by_tag", privateDomain),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsRoute53ZoneCheck(name string) resource.TestCheckFunc {
|
||||
// rsName for the name of the created resource
|
||||
// dsName for the name of the created data source
|
||||
// zName for the name of the domain
|
||||
func testAccDataSourceAwsRoute53ZoneCheck(rsName, dsName, zName string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[name]
|
||||
rs, ok := s.RootModule().Resources[rsName]
|
||||
if !ok {
|
||||
return fmt.Errorf("root module has no resource called %s", name)
|
||||
return fmt.Errorf("root module has no resource called %s", rsName)
|
||||
}
|
||||
|
||||
hostedZone, ok := s.RootModule().Resources["aws_route53_zone.test"]
|
||||
hostedZone, ok := s.RootModule().Resources[dsName]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't find aws_hosted_zone.test in state")
|
||||
}
|
||||
attr := rs.Primary.Attributes
|
||||
if attr["id"] != hostedZone.Primary.Attributes["id"] {
|
||||
return fmt.Errorf(
|
||||
"id is %s; want %s",
|
||||
attr["id"],
|
||||
hostedZone.Primary.Attributes["id"],
|
||||
)
|
||||
}
|
||||
|
||||
if attr["name"] != "terraformtestacchz.com." {
|
||||
return fmt.Errorf(
|
||||
"Route53 Zone name is %s; want terraformtestacchz.com.",
|
||||
attr["name"],
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsRoute53ZoneCheckPrivate(name string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("root module has no resource called %s", name)
|
||||
}
|
||||
|
||||
hostedZone, ok := s.RootModule().Resources["aws_route53_zone.test_private"]
|
||||
if !ok {
|
||||
return fmt.Errorf("can't find aws_hosted_zone.test in state")
|
||||
return fmt.Errorf("can't find zone %q in state", dsName)
|
||||
}
|
||||
|
||||
attr := rs.Primary.Attributes
|
||||
@ -78,56 +61,54 @@ func testAccDataSourceAwsRoute53ZoneCheckPrivate(name string) resource.TestCheck
|
||||
)
|
||||
}
|
||||
|
||||
if attr["name"] != "test.acc." {
|
||||
return fmt.Errorf(
|
||||
"Route53 Zone name is %s; want test.acc.",
|
||||
attr["name"],
|
||||
)
|
||||
if attr["name"] != zName {
|
||||
return fmt.Errorf("Route53 Zone name is %q; want %q", attr["name"], zName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsRoute53ZoneConfig = `
|
||||
func testAccDataSourceAwsRoute53ZoneConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-east-2"
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = "us-east-2"
|
||||
}
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.16.0.0/16"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.16.0.0/16"
|
||||
}
|
||||
resource "aws_route53_zone" "test_private" {
|
||||
name = "test.acc-%d."
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
tags {
|
||||
Environment = "dev-%d"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route53_zone" "test_private" {
|
||||
name = "test.acc."
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
tags {
|
||||
Environment = "dev"
|
||||
}
|
||||
}
|
||||
data "aws_route53_zone" "by_vpc" {
|
||||
name = "${aws_route53_zone.test_private.name}"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
}
|
||||
data "aws_route53_zone" "by_vpc" {
|
||||
name = "${aws_route53_zone.test_private.name}"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
}
|
||||
|
||||
data "aws_route53_zone" "by_tag" {
|
||||
name = "${aws_route53_zone.test_private.name}"
|
||||
private_zone = true
|
||||
tags {
|
||||
Environment = "dev"
|
||||
}
|
||||
}
|
||||
data "aws_route53_zone" "by_tag" {
|
||||
name = "${aws_route53_zone.test_private.name}"
|
||||
private_zone = true
|
||||
tags {
|
||||
Environment = "dev-%d"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route53_zone" "test" {
|
||||
name = "terraformtestacchz.com."
|
||||
}
|
||||
data "aws_route53_zone" "by_zone_id" {
|
||||
zone_id = "${aws_route53_zone.test.zone_id}"
|
||||
}
|
||||
resource "aws_route53_zone" "test" {
|
||||
name = "terraformtestacchz-%d.com."
|
||||
}
|
||||
|
||||
data "aws_route53_zone" "by_name" {
|
||||
name = "${data.aws_route53_zone.by_zone_id.name}"
|
||||
}
|
||||
data "aws_route53_zone" "by_zone_id" {
|
||||
zone_id = "${aws_route53_zone.test.zone_id}"
|
||||
}
|
||||
|
||||
`
|
||||
data "aws_route53_zone" "by_name" {
|
||||
name = "${data.aws_route53_zone.by_zone_id.name}"
|
||||
}`, rInt, rInt, rInt, rInt)
|
||||
}
|
||||
|
60
builtin/providers/aws/data_source_aws_subnet_ids.go
Normal file
60
builtin/providers/aws/data_source_aws_subnet_ids.go
Normal file
@ -0,0 +1,60 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func dataSourceAwsSubnetIDs() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsSubnetIDsRead,
|
||||
Schema: map[string]*schema.Schema{
|
||||
"vpc_id": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"ids": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Set: schema.HashString,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsSubnetIDsRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).ec2conn
|
||||
|
||||
req := &ec2.DescribeSubnetsInput{}
|
||||
|
||||
req.Filters = buildEC2AttributeFilterList(
|
||||
map[string]string{
|
||||
"vpc-id": d.Get("vpc_id").(string),
|
||||
},
|
||||
)
|
||||
|
||||
log.Printf("[DEBUG] DescribeSubnets %s\n", req)
|
||||
resp, err := conn.DescribeSubnets(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp == nil || len(resp.Subnets) == 0 {
|
||||
return fmt.Errorf("no matching subnet found for vpc with id %s", d.Get("vpc_id").(string))
|
||||
}
|
||||
|
||||
subnets := make([]string, 0)
|
||||
|
||||
for _, subnet := range resp.Subnets {
|
||||
subnets = append(subnets, *subnet.SubnetId)
|
||||
}
|
||||
|
||||
d.SetId(d.Get("vpc_id").(string))
|
||||
d.Set("ids", subnets)
|
||||
|
||||
return nil
|
||||
}
|
77
builtin/providers/aws/data_source_aws_subnet_ids_test.go
Normal file
77
builtin/providers/aws/data_source_aws_subnet_ids_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsSubnetIDs(t *testing.T) {
|
||||
rInt := acctest.RandIntRange(0, 256)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccDataSourceAwsSubnetIDsConfig(rInt),
|
||||
},
|
||||
{
|
||||
Config: testAccDataSourceAwsSubnetIDsConfigWithDataSource(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("data.aws_subnet_ids.selected", "ids.#", "1"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsSubnetIDsConfigWithDataSource(rInt int) string {
|
||||
return fmt.Sprintf(
|
||||
`
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.%d.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-ids-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "test" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
cidr_block = "172.%d.123.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-ids-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet_ids" "selected" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
}
|
||||
`, rInt, rInt)
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsSubnetIDsConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.%d.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-ids-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "test" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
cidr_block = "172.%d.123.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-ids-data-source"
|
||||
}
|
||||
}
|
||||
`, rInt, rInt)
|
||||
}
|
@ -4,30 +4,33 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccDataSourceAwsSubnet(t *testing.T) {
|
||||
rInt := acctest.RandIntRange(0, 256)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccDataSourceAwsSubnetConfig,
|
||||
Config: testAccDataSourceAwsSubnetConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_id"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_cidr"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_tag"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_vpc"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_filter"),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_id", rInt),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_cidr", rInt),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_tag", rInt),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_vpc", rInt),
|
||||
testAccDataSourceAwsSubnetCheck("data.aws_subnet.by_filter", rInt),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccDataSourceAwsSubnetCheck(name string) resource.TestCheckFunc {
|
||||
func testAccDataSourceAwsSubnetCheck(name string, rInt int) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[name]
|
||||
if !ok {
|
||||
@ -61,13 +64,13 @@ func testAccDataSourceAwsSubnetCheck(name string) resource.TestCheckFunc {
|
||||
)
|
||||
}
|
||||
|
||||
if attr["cidr_block"] != "172.16.123.0/24" {
|
||||
if attr["cidr_block"] != fmt.Sprintf("172.%d.123.0/24", rInt) {
|
||||
return fmt.Errorf("bad cidr_block %s", attr["cidr_block"])
|
||||
}
|
||||
if attr["availability_zone"] != "us-west-2a" {
|
||||
return fmt.Errorf("bad availability_zone %s", attr["availability_zone"])
|
||||
}
|
||||
if attr["tags.Name"] != "terraform-testacc-subnet-data-source" {
|
||||
if attr["tags.Name"] != fmt.Sprintf("terraform-testacc-subnet-data-source-%d", rInt) {
|
||||
return fmt.Errorf("bad Name tag %s", attr["tags.Name"])
|
||||
}
|
||||
|
||||
@ -75,51 +78,53 @@ func testAccDataSourceAwsSubnetCheck(name string) resource.TestCheckFunc {
|
||||
}
|
||||
}
|
||||
|
||||
const testAccDataSourceAwsSubnetConfig = `
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
func testAccDataSourceAwsSubnetConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.%d.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "test" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
cidr_block = "172.%d.123.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source-%d"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_id" {
|
||||
id = "${aws_subnet.test.id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_cidr" {
|
||||
cidr_block = "${aws_subnet.test.cidr_block}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_tag" {
|
||||
tags {
|
||||
Name = "${aws_subnet.test.tags["Name"]}"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_vpc" {
|
||||
vpc_id = "${aws_subnet.test.vpc_id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_filter" {
|
||||
filter {
|
||||
name = "vpc-id"
|
||||
values = ["${aws_subnet.test.vpc_id}"]
|
||||
}
|
||||
}
|
||||
`, rInt, rInt, rInt)
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "172.16.0.0/16"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "test" {
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
cidr_block = "172.16.123.0/24"
|
||||
availability_zone = "us-west-2a"
|
||||
|
||||
tags {
|
||||
Name = "terraform-testacc-subnet-data-source"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_id" {
|
||||
id = "${aws_subnet.test.id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_cidr" {
|
||||
cidr_block = "${aws_subnet.test.cidr_block}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_tag" {
|
||||
tags {
|
||||
Name = "${aws_subnet.test.tags["Name"]}"
|
||||
}
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_vpc" {
|
||||
vpc_id = "${aws_subnet.test.vpc_id}"
|
||||
}
|
||||
|
||||
data "aws_subnet" "by_filter" {
|
||||
filter {
|
||||
name = "vpc-id"
|
||||
values = ["${aws_subnet.test.vpc_id}"]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
@ -58,3 +59,19 @@ func suppressEquivalentJsonDiffs(k, old, new string, d *schema.ResourceData) boo
|
||||
|
||||
return jsonBytesEqual(ob.Bytes(), nb.Bytes())
|
||||
}
|
||||
|
||||
func suppressOpenIdURL(k, old, new string, d *schema.ResourceData) bool {
|
||||
oldUrl, err := url.Parse(old)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
newUrl, err := url.Parse(new)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
oldUrl.Scheme = "https"
|
||||
|
||||
return oldUrl.String() == newUrl.String()
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ import (
|
||||
)
|
||||
|
||||
func resourceAwsCloudFrontDistributionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
||||
// This is a non API attribute
|
||||
// We are merely setting this to the same value as the Default setting in the schema
|
||||
d.Set("retain_on_delete", false)
|
||||
|
||||
conn := meta.(*AWSClient).cloudfrontconn
|
||||
id := d.Id()
|
||||
resp, err := conn.GetDistributionConfig(&cloudfront.GetDistributionConfigInput{
|
||||
|
@ -19,16 +19,13 @@ func TestAccAWSCloudFrontDistribution_importBasic(t *testing.T) {
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckCloudFrontDistributionDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testConfig,
|
||||
},
|
||||
resource.TestStep{
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
// Ignore retain_on_delete since it doesn't come from the AWS
|
||||
// API.
|
||||
ImportStateVerifyIgnore: []string{"retain_on_delete"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -3,10 +3,12 @@ package aws
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
resourceName := "aws_cloudwatch_metric_alarm.foobar"
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
@ -14,11 +16,11 @@ func TestAccAWSCloudWatchMetricAlarm_importBasic(t *testing.T) {
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSCloudWatchMetricAlarmConfig,
|
||||
{
|
||||
Config: testAccAWSCloudWatchMetricAlarmConfig(rInt),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
|
@ -3,11 +3,14 @@ package aws
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSCustomerGateway_importBasic(t *testing.T) {
|
||||
resourceName := "aws_customer_gateway.foo"
|
||||
rInt := acctest.RandInt()
|
||||
rBgpAsn := acctest.RandIntRange(64512, 65534)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@ -15,7 +18,7 @@ func TestAccAWSCustomerGateway_importBasic(t *testing.T) {
|
||||
CheckDestroy: testAccCheckCustomerGatewayDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCustomerGatewayConfig,
|
||||
Config: testAccCustomerGatewayConfig(rInt, rBgpAsn),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
|
@ -3,11 +3,13 @@ package aws
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
||||
resourceName := "aws_efs_file_system.foo-with-tags"
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@ -15,7 +17,7 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
|
||||
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags,
|
||||
Config: testAccAWSEFSFileSystemConfigWithTags(rInt),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
|
@ -1,13 +1,16 @@
|
||||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
|
||||
resourceName := "aws_elasticache_parameter_group.bar"
|
||||
rName := fmt.Sprintf("parameter-group-test-terraform-%d", acctest.RandInt())
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@ -15,7 +18,7 @@ func TestAccAWSElasticacheParameterGroup_importBasic(t *testing.T) {
|
||||
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSElasticacheParameterGroupConfig,
|
||||
Config: testAccAWSElasticacheParameterGroupConfig(rName),
|
||||
},
|
||||
|
||||
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"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
@ -3,11 +3,13 @@ package aws
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSVpnConnection_importBasic(t *testing.T) {
|
||||
resourceName := "aws_vpn_connection.foo"
|
||||
rBgpAsn := acctest.RandIntRange(64512, 65534)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@ -15,7 +17,7 @@ func TestAccAWSVpnConnection_importBasic(t *testing.T) {
|
||||
CheckDestroy: testAccAwsVpnConnectionDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAwsVpnConnectionConfig,
|
||||
Config: testAccAwsVpnConnectionConfig(rBgpAsn),
|
||||
},
|
||||
{
|
||||
ResourceName: resourceName,
|
||||
|
@ -174,6 +174,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_elb_service_account": dataSourceAwsElbServiceAccount(),
|
||||
"aws_iam_account_alias": dataSourceAwsIamAccountAlias(),
|
||||
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
|
||||
"aws_iam_role": dataSourceAwsIAMRole(),
|
||||
"aws_iam_server_certificate": dataSourceAwsIAMServerCertificate(),
|
||||
"aws_instance": dataSourceAwsInstance(),
|
||||
"aws_ip_ranges": dataSourceAwsIPRanges(),
|
||||
@ -187,6 +188,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_s3_bucket_object": dataSourceAwsS3BucketObject(),
|
||||
"aws_sns_topic": dataSourceAwsSnsTopic(),
|
||||
"aws_subnet": dataSourceAwsSubnet(),
|
||||
"aws_subnet_ids": dataSourceAwsSubnetIDs(),
|
||||
"aws_security_group": dataSourceAwsSecurityGroup(),
|
||||
"aws_vpc": dataSourceAwsVpc(),
|
||||
"aws_vpc_endpoint": dataSourceAwsVpcEndpoint(),
|
||||
@ -220,6 +222,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_api_gateway_resource": resourceAwsApiGatewayResource(),
|
||||
"aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(),
|
||||
"aws_api_gateway_usage_plan": resourceAwsApiGatewayUsagePlan(),
|
||||
"aws_api_gateway_usage_plan_key": resourceAwsApiGatewayUsagePlanKey(),
|
||||
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
|
||||
"aws_appautoscaling_target": resourceAwsAppautoscalingTarget(),
|
||||
"aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(),
|
||||
@ -306,6 +309,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_iam_group_membership": resourceAwsIamGroupMembership(),
|
||||
"aws_iam_group_policy_attachment": resourceAwsIamGroupPolicyAttachment(),
|
||||
"aws_iam_instance_profile": resourceAwsIamInstanceProfile(),
|
||||
"aws_iam_openid_connect_provider": resourceAwsIamOpenIDConnectProvider(),
|
||||
"aws_iam_policy": resourceAwsIamPolicy(),
|
||||
"aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(),
|
||||
"aws_iam_role_policy_attachment": resourceAwsIamRolePolicyAttachment(),
|
||||
@ -336,6 +340,8 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_lightsail_domain": resourceAwsLightsailDomain(),
|
||||
"aws_lightsail_instance": resourceAwsLightsailInstance(),
|
||||
"aws_lightsail_key_pair": resourceAwsLightsailKeyPair(),
|
||||
"aws_lightsail_static_ip": resourceAwsLightsailStaticIp(),
|
||||
"aws_lightsail_static_ip_attachment": resourceAwsLightsailStaticIpAttachment(),
|
||||
"aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(),
|
||||
"aws_load_balancer_policy": resourceAwsLoadBalancerPolicy(),
|
||||
"aws_load_balancer_backend_server_policy": resourceAwsLoadBalancerBackendServerPolicies(),
|
||||
@ -382,6 +388,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"aws_route_table": resourceAwsRouteTable(),
|
||||
"aws_route_table_association": resourceAwsRouteTableAssociation(),
|
||||
"aws_ses_active_receipt_rule_set": resourceAwsSesActiveReceiptRuleSet(),
|
||||
"aws_ses_domain_identity": resourceAwsSesDomainIdentity(),
|
||||
"aws_ses_receipt_filter": resourceAwsSesReceiptFilter(),
|
||||
"aws_ses_receipt_rule": resourceAwsSesReceiptRule(),
|
||||
"aws_ses_receipt_rule_set": resourceAwsSesReceiptRuleSet(),
|
||||
|
@ -69,7 +69,6 @@ func resourceAwsAlb() *schema.Resource {
|
||||
"subnets": {
|
||||
Type: schema.TypeSet,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
ForceNew: true,
|
||||
Required: true,
|
||||
Set: schema.HashString,
|
||||
},
|
||||
@ -109,6 +108,12 @@ func resourceAwsAlb() *schema.Resource {
|
||||
Default: 60,
|
||||
},
|
||||
|
||||
"ip_address_type": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"vpc_id": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
@ -159,6 +164,10 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
elbOpts.Subnets = expandStringList(v.(*schema.Set).List())
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("ip_address_type"); ok {
|
||||
elbOpts.IpAddressType = aws.String(v.(string))
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] ALB create configuration: %#v", elbOpts)
|
||||
|
||||
resp, err := elbconn.CreateLoadBalancer(elbOpts)
|
||||
@ -175,7 +184,7 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
log.Printf("[INFO] ALB ID: %s", d.Id())
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"active", "provisioning", "failed"},
|
||||
Pending: []string{"provisioning", "failed"},
|
||||
Target: []string{"active"},
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
describeResp, err := elbconn.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{
|
||||
@ -194,7 +203,7 @@ func resourceAwsAlbCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
return describeResp, *dLb.State.Code, nil
|
||||
},
|
||||
Timeout: 5 * time.Minute,
|
||||
Timeout: 10 * time.Minute,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
_, err = stateConf.WaitForState()
|
||||
@ -312,6 +321,62 @@ func resourceAwsAlbUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
}
|
||||
|
||||
if d.HasChange("subnets") {
|
||||
subnets := expandStringList(d.Get("subnets").(*schema.Set).List())
|
||||
|
||||
params := &elbv2.SetSubnetsInput{
|
||||
LoadBalancerArn: aws.String(d.Id()),
|
||||
Subnets: subnets,
|
||||
}
|
||||
|
||||
_, err := elbconn.SetSubnets(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failure Setting ALB Subnets: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("ip_address_type") {
|
||||
|
||||
params := &elbv2.SetIpAddressTypeInput{
|
||||
LoadBalancerArn: aws.String(d.Id()),
|
||||
IpAddressType: aws.String(d.Get("ip_address_type").(string)),
|
||||
}
|
||||
|
||||
_, err := elbconn.SetIpAddressType(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failure Setting ALB IP Address Type: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stateConf := &resource.StateChangeConf{
|
||||
Pending: []string{"active", "provisioning", "failed"},
|
||||
Target: []string{"active"},
|
||||
Refresh: func() (interface{}, string, error) {
|
||||
describeResp, err := elbconn.DescribeLoadBalancers(&elbv2.DescribeLoadBalancersInput{
|
||||
LoadBalancerArns: []*string{aws.String(d.Id())},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(describeResp.LoadBalancers) != 1 {
|
||||
return nil, "", fmt.Errorf("No load balancers returned for %s", d.Id())
|
||||
}
|
||||
dLb := describeResp.LoadBalancers[0]
|
||||
|
||||
log.Printf("[INFO] ALB state: %s", *dLb.State.Code)
|
||||
|
||||
return describeResp, *dLb.State.Code, nil
|
||||
},
|
||||
Timeout: 10 * time.Minute,
|
||||
MinTimeout: 3 * time.Second,
|
||||
}
|
||||
_, err := stateConf.WaitForState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return resourceAwsAlbRead(d, meta)
|
||||
}
|
||||
|
||||
@ -368,6 +433,7 @@ func flattenAwsAlbResource(d *schema.ResourceData, meta interface{}, alb *elbv2.
|
||||
d.Set("vpc_id", alb.VpcId)
|
||||
d.Set("zone_id", alb.CanonicalHostedZoneId)
|
||||
d.Set("dns_name", alb.DNSName)
|
||||
d.Set("ip_address_type", alb.IpAddressType)
|
||||
|
||||
respTags, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{
|
||||
ResourceArns: []*string{alb.LoadBalancerArn},
|
||||
|
@ -31,10 +31,12 @@ func resourceAwsAlbListenerRule() *schema.Resource {
|
||||
"listener_arn": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"priority": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateAwsAlbListenerRulePriority,
|
||||
},
|
||||
"action": {
|
||||
@ -66,6 +68,7 @@ func resourceAwsAlbListenerRule() *schema.Resource {
|
||||
},
|
||||
"values": {
|
||||
Type: schema.TypeList,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
@ -183,42 +186,75 @@ func resourceAwsAlbListenerRuleRead(d *schema.ResourceData, meta interface{}) er
|
||||
func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
d.Partial(true)
|
||||
|
||||
if d.HasChange("priority") {
|
||||
params := &elbv2.SetRulePrioritiesInput{
|
||||
RulePriorities: []*elbv2.RulePriorityPair{
|
||||
{
|
||||
RuleArn: aws.String(d.Id()),
|
||||
Priority: aws.Int64(int64(d.Get("priority").(int))),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := elbconn.SetRulePriorities(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.SetPartial("priority")
|
||||
}
|
||||
|
||||
requestUpdate := false
|
||||
params := &elbv2.ModifyRuleInput{
|
||||
RuleArn: aws.String(d.Id()),
|
||||
}
|
||||
|
||||
actions := d.Get("action").([]interface{})
|
||||
params.Actions = make([]*elbv2.Action, len(actions))
|
||||
for i, action := range actions {
|
||||
actionMap := action.(map[string]interface{})
|
||||
params.Actions[i] = &elbv2.Action{
|
||||
TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
|
||||
Type: aws.String(actionMap["type"].(string)),
|
||||
if d.HasChange("action") {
|
||||
actions := d.Get("action").([]interface{})
|
||||
params.Actions = make([]*elbv2.Action, len(actions))
|
||||
for i, action := range actions {
|
||||
actionMap := action.(map[string]interface{})
|
||||
params.Actions[i] = &elbv2.Action{
|
||||
TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
|
||||
Type: aws.String(actionMap["type"].(string)),
|
||||
}
|
||||
}
|
||||
requestUpdate = true
|
||||
d.SetPartial("action")
|
||||
}
|
||||
|
||||
if d.HasChange("condition") {
|
||||
conditions := d.Get("condition").([]interface{})
|
||||
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
||||
for i, condition := range conditions {
|
||||
conditionMap := condition.(map[string]interface{})
|
||||
values := conditionMap["values"].([]interface{})
|
||||
params.Conditions[i] = &elbv2.RuleCondition{
|
||||
Field: aws.String(conditionMap["field"].(string)),
|
||||
Values: make([]*string, len(values)),
|
||||
}
|
||||
for j, value := range values {
|
||||
params.Conditions[i].Values[j] = aws.String(value.(string))
|
||||
}
|
||||
}
|
||||
requestUpdate = true
|
||||
d.SetPartial("condition")
|
||||
}
|
||||
|
||||
if requestUpdate {
|
||||
resp, err := elbconn.ModifyRule(params)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
||||
}
|
||||
|
||||
if len(resp.Rules) == 0 {
|
||||
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
||||
}
|
||||
}
|
||||
|
||||
conditions := d.Get("condition").([]interface{})
|
||||
params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
|
||||
for i, condition := range conditions {
|
||||
conditionMap := condition.(map[string]interface{})
|
||||
values := conditionMap["values"].([]interface{})
|
||||
params.Conditions[i] = &elbv2.RuleCondition{
|
||||
Field: aws.String(conditionMap["field"].(string)),
|
||||
Values: make([]*string, len(values)),
|
||||
}
|
||||
for j, value := range values {
|
||||
params.Conditions[i].Values[j] = aws.String(value.(string))
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := elbconn.ModifyRule(params)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
|
||||
}
|
||||
|
||||
if len(resp.Rules) == 0 {
|
||||
return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
|
||||
}
|
||||
d.Partial(false)
|
||||
|
||||
return resourceAwsAlbListenerRuleRead(d, meta)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package aws
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@ -43,6 +44,90 @@ func TestAccAWSALBListenerRule_basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_updateRulePriority(t *testing.T) {
|
||||
var rule elbv2.Rule
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_listener_rule.static",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &rule),
|
||||
resource.TestCheckResourceAttr("aws_alb_listener_rule.static", "priority", "100"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_updateRulePriority(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &rule),
|
||||
resource.TestCheckResourceAttr("aws_alb_listener_rule.static", "priority", "101"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_changeListenerRuleArnForcesNew(t *testing.T) {
|
||||
var before, after elbv2.Rule
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_listener_rule.static",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &before),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_changeRuleArn(albName, targetGroupName),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBListenerRuleExists("aws_alb_listener_rule.static", &after),
|
||||
testAccCheckAWSAlbListenerRuleRecreated(t, &before, &after),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBListenerRule_multipleConditionThrowsError(t *testing.T) {
|
||||
albName := fmt.Sprintf("testrule-basic-%s", acctest.RandStringFromCharSet(13, acctest.CharSetAlphaNum))
|
||||
targetGroupName := fmt.Sprintf("testtargetgroup-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBListenerRuleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName),
|
||||
ExpectError: regexp.MustCompile(`attribute supports 1 item maximum`),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSAlbListenerRuleRecreated(t *testing.T,
|
||||
before, after *elbv2.Rule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if *before.RuleArn == *after.RuleArn {
|
||||
t.Fatalf("Expected change of Listener Rule ARNs, but both were %v", before.RuleArn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSALBListenerRuleExists(n string, res *elbv2.Rule) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
@ -104,6 +189,117 @@ func testAccCheckAWSALBListenerRuleDestroy(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_multipleConditions(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
priority = 100
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*", "static"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_basic(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
@ -214,3 +410,238 @@ resource "aws_security_group" "alb_test" {
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_updateRulePriority(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end.arn}"
|
||||
priority = 101
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBListenerRuleConfig_changeRuleArn(albName, targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_listener_rule" "static" {
|
||||
listener_arn = "${aws_alb_listener.front_end_ruleupdate.arn}"
|
||||
priority = 101
|
||||
|
||||
action {
|
||||
type = "forward"
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
}
|
||||
|
||||
condition {
|
||||
field = "path-pattern"
|
||||
values = ["/static/*"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "80"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "front_end_ruleupdate" {
|
||||
load_balancer_arn = "${aws_alb.alb_test.id}"
|
||||
protocol = "HTTP"
|
||||
port = "8080"
|
||||
|
||||
default_action {
|
||||
target_group_arn = "${aws_alb_target_group.test.id}"
|
||||
type = "forward"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
internal = true
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test.*.id}"]
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 8080
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24"]
|
||||
type = "list"
|
||||
}
|
||||
|
||||
data "aws_availability_zones" "available" {}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test" {
|
||||
count = 2
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "${element(var.subnets, count.index)}"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "${element(data.aws_availability_zones.available.names, count.index)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_security_group" "alb_test" {
|
||||
name = "allow_all_alb_test"
|
||||
description = "Used for ALB Testing"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
ingress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
egress {
|
||||
from_port = 0
|
||||
to_port = 0
|
||||
protocol = "-1"
|
||||
cidr_blocks = ["0.0.0.0/0"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, targetGroupName)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
@ -37,10 +38,18 @@ func resourceAwsAlbTargetGroup() *schema.Resource {
|
||||
},
|
||||
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ConflictsWith: []string{"name_prefix"},
|
||||
ValidateFunc: validateAwsAlbTargetGroupName,
|
||||
},
|
||||
"name_prefix": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateAwsAlbTargetGroupName,
|
||||
ValidateFunc: validateAwsAlbTargetGroupNamePrefix,
|
||||
},
|
||||
|
||||
"port": {
|
||||
@ -73,6 +82,7 @@ func resourceAwsAlbTargetGroup() *schema.Resource {
|
||||
"stickiness": {
|
||||
Type: schema.TypeList,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
@ -171,8 +181,17 @@ func resourceAwsAlbTargetGroup() *schema.Resource {
|
||||
func resourceAwsAlbTargetGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
var groupName string
|
||||
if v, ok := d.GetOk("name"); ok {
|
||||
groupName = v.(string)
|
||||
} else if v, ok := d.GetOk("name_prefix"); ok {
|
||||
groupName = resource.PrefixedUniqueId(v.(string))
|
||||
} else {
|
||||
groupName = resource.PrefixedUniqueId("tf-")
|
||||
}
|
||||
|
||||
params := &elbv2.CreateTargetGroupInput{
|
||||
Name: aws.String(d.Get("name").(string)),
|
||||
Name: aws.String(groupName),
|
||||
Port: aws.Int64(int64(d.Get("port").(int))),
|
||||
Protocol: aws.String(d.Get("protocol").(string)),
|
||||
VpcId: aws.String(d.Get("vpc_id").(string)),
|
||||
@ -258,11 +277,19 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err
|
||||
for _, attr := range attrResp.Attributes {
|
||||
switch *attr.Key {
|
||||
case "stickiness.enabled":
|
||||
stickinessMap["enabled"] = *attr.Value
|
||||
enabled, err := strconv.ParseBool(*attr.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting stickiness.enabled to bool: %s", *attr.Value)
|
||||
}
|
||||
stickinessMap["enabled"] = enabled
|
||||
case "stickiness.type":
|
||||
stickinessMap["type"] = *attr.Value
|
||||
case "stickiness.lb_cookie.duration_seconds":
|
||||
stickinessMap["cookie_duration"] = *attr.Value
|
||||
duration, err := strconv.Atoi(*attr.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting stickiness.lb_cookie.duration_seconds to int: %s", *attr.Value)
|
||||
}
|
||||
stickinessMap["cookie_duration"] = duration
|
||||
case "deregistration_delay.timeout_seconds":
|
||||
timeout, err := strconv.Atoi(*attr.Value)
|
||||
if err != nil {
|
||||
@ -271,7 +298,24 @@ func resourceAwsAlbTargetGroupRead(d *schema.ResourceData, meta interface{}) err
|
||||
d.Set("deregistration_delay", timeout)
|
||||
}
|
||||
}
|
||||
d.Set("stickiness", []interface{}{stickinessMap})
|
||||
|
||||
if err := d.Set("stickiness", []interface{}{stickinessMap}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tagsResp, err := elbconn.DescribeTags(&elbv2.DescribeTagsInput{
|
||||
ResourceArns: []*string{aws.String(d.Id())},
|
||||
})
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error retrieving Target Group Tags: {{err}}", err)
|
||||
}
|
||||
for _, t := range tagsResp.TagDescriptions {
|
||||
if *t.ResourceArn == d.Id() {
|
||||
if err := d.Set("tags", tagsToMapELBv2(t.Tags)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -437,14 +481,6 @@ func validateAwsAlbTargetGroupHealthCheckProtocol(v interface{}, k string) (ws [
|
||||
return
|
||||
}
|
||||
|
||||
func validateAwsAlbTargetGroupName(v interface{}, k string) (ws []string, errors []error) {
|
||||
name := v.(string)
|
||||
if len(name) > 32 {
|
||||
errors = append(errors, fmt.Errorf("%q (%q) cannot be longer than '32' characters", k, name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateAwsAlbTargetGroupPort(v interface{}, k string) (ws []string, errors []error) {
|
||||
port := v.(int)
|
||||
if port < 1 || port > 65536 {
|
||||
|
@ -34,7 +34,7 @@ func resourceAwsAlbTargetGroupAttachment() *schema.Resource {
|
||||
"port": {
|
||||
Type: schema.TypeInt,
|
||||
ForceNew: true,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -43,18 +43,21 @@ func resourceAwsAlbTargetGroupAttachment() *schema.Resource {
|
||||
func resourceAwsAlbAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
params := &elbv2.RegisterTargetsInput{
|
||||
TargetGroupArn: aws.String(d.Get("target_group_arn").(string)),
|
||||
Targets: []*elbv2.TargetDescription{
|
||||
{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
Port: aws.Int64(int64(d.Get("port").(int))),
|
||||
},
|
||||
},
|
||||
target := &elbv2.TargetDescription{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Registering Target %s (%d) with Target Group %s", d.Get("target_id").(string),
|
||||
d.Get("port").(int), d.Get("target_group_arn").(string))
|
||||
if v, ok := d.GetOk("port"); ok {
|
||||
target.Port = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
|
||||
params := &elbv2.RegisterTargetsInput{
|
||||
TargetGroupArn: aws.String(d.Get("target_group_arn").(string)),
|
||||
Targets: []*elbv2.TargetDescription{target},
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Registering Target %s with Target Group %s", d.Get("target_id").(string),
|
||||
d.Get("target_group_arn").(string))
|
||||
|
||||
_, err := elbconn.RegisterTargets(params)
|
||||
if err != nil {
|
||||
@ -69,14 +72,17 @@ func resourceAwsAlbAttachmentCreate(d *schema.ResourceData, meta interface{}) er
|
||||
func resourceAwsAlbAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
target := &elbv2.TargetDescription{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("port"); ok {
|
||||
target.Port = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
|
||||
params := &elbv2.DeregisterTargetsInput{
|
||||
TargetGroupArn: aws.String(d.Get("target_group_arn").(string)),
|
||||
Targets: []*elbv2.TargetDescription{
|
||||
{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
Port: aws.Int64(int64(d.Get("port").(int))),
|
||||
},
|
||||
},
|
||||
Targets: []*elbv2.TargetDescription{target},
|
||||
}
|
||||
|
||||
_, err := elbconn.DeregisterTargets(params)
|
||||
@ -93,14 +99,18 @@ func resourceAwsAlbAttachmentDelete(d *schema.ResourceData, meta interface{}) er
|
||||
// target, so there is no work to do beyond ensuring that the target and group still exist.
|
||||
func resourceAwsAlbAttachmentRead(d *schema.ResourceData, meta interface{}) error {
|
||||
elbconn := meta.(*AWSClient).elbv2conn
|
||||
|
||||
target := &elbv2.TargetDescription{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
}
|
||||
|
||||
if v, ok := d.GetOk("port"); ok {
|
||||
target.Port = aws.Int64(int64(v.(int)))
|
||||
}
|
||||
|
||||
resp, err := elbconn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{
|
||||
TargetGroupArn: aws.String(d.Get("target_group_arn").(string)),
|
||||
Targets: []*elbv2.TargetDescription{
|
||||
{
|
||||
Id: aws.String(d.Get("target_id").(string)),
|
||||
Port: aws.Int64(int64(d.Get("port").(int))),
|
||||
},
|
||||
},
|
||||
Targets: []*elbv2.TargetDescription{target},
|
||||
})
|
||||
if err != nil {
|
||||
if isTargetGroupNotFound(err) {
|
||||
|
@ -3,14 +3,15 @@ package aws
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/elbv2"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccAWSALBTargetGroupAttachment_basic(t *testing.T) {
|
||||
@ -32,6 +33,25 @@ func TestAccAWSALBTargetGroupAttachment_basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBTargetGroupAttachment_withoutPort(t *testing.T) {
|
||||
targetGroupName := fmt.Sprintf("test-target-group-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_target_group.test",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBTargetGroupAttachmentDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBTargetGroupAttachmentConfigWithoutPort(targetGroupName),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
testAccCheckAWSALBTargetGroupAttachmentExists("aws_alb_target_group_attachment.test"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSALBTargetGroupAttachmentExists(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
@ -45,15 +65,20 @@ func testAccCheckAWSALBTargetGroupAttachmentExists(n string) resource.TestCheckF
|
||||
|
||||
conn := testAccProvider.Meta().(*AWSClient).elbv2conn
|
||||
|
||||
port, _ := strconv.Atoi(rs.Primary.Attributes["port"])
|
||||
_, hasPort := rs.Primary.Attributes["port"]
|
||||
targetGroupArn, _ := rs.Primary.Attributes["target_group_arn"]
|
||||
|
||||
target := &elbv2.TargetDescription{
|
||||
Id: aws.String(rs.Primary.Attributes["target_id"]),
|
||||
}
|
||||
if hasPort == true {
|
||||
port, _ := strconv.Atoi(rs.Primary.Attributes["port"])
|
||||
target.Port = aws.Int64(int64(port))
|
||||
}
|
||||
|
||||
describe, err := conn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{
|
||||
TargetGroupArn: aws.String(rs.Primary.Attributes["target_group_arn"]),
|
||||
Targets: []*elbv2.TargetDescription{
|
||||
{
|
||||
Id: aws.String(rs.Primary.Attributes["target_id"]),
|
||||
Port: aws.Int64(int64(port)),
|
||||
},
|
||||
},
|
||||
TargetGroupArn: aws.String(targetGroupArn),
|
||||
Targets: []*elbv2.TargetDescription{target},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@ -76,15 +101,20 @@ func testAccCheckAWSALBTargetGroupAttachmentDestroy(s *terraform.State) error {
|
||||
continue
|
||||
}
|
||||
|
||||
port, _ := strconv.Atoi(rs.Primary.Attributes["port"])
|
||||
_, hasPort := rs.Primary.Attributes["port"]
|
||||
targetGroupArn, _ := rs.Primary.Attributes["target_group_arn"]
|
||||
|
||||
target := &elbv2.TargetDescription{
|
||||
Id: aws.String(rs.Primary.Attributes["target_id"]),
|
||||
}
|
||||
if hasPort == true {
|
||||
port, _ := strconv.Atoi(rs.Primary.Attributes["port"])
|
||||
target.Port = aws.Int64(int64(port))
|
||||
}
|
||||
|
||||
describe, err := conn.DescribeTargetHealth(&elbv2.DescribeTargetHealthInput{
|
||||
TargetGroupArn: aws.String(rs.Primary.Attributes["target_group_arn"]),
|
||||
Targets: []*elbv2.TargetDescription{
|
||||
{
|
||||
Id: aws.String(rs.Primary.Attributes["target_id"]),
|
||||
Port: aws.Int64(int64(port)),
|
||||
},
|
||||
},
|
||||
TargetGroupArn: aws.String(targetGroupArn),
|
||||
Targets: []*elbv2.TargetDescription{target},
|
||||
})
|
||||
if err == nil {
|
||||
if len(describe.TargetHealthDescriptions) != 0 {
|
||||
@ -103,6 +133,55 @@ func testAccCheckAWSALBTargetGroupAttachmentDestroy(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSALBTargetGroupAttachmentConfigWithoutPort(targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_target_group_attachment" "test" {
|
||||
target_group_arn = "${aws_alb_target_group.test.arn}"
|
||||
target_id = "${aws_instance.test.id}"
|
||||
}
|
||||
|
||||
resource "aws_instance" "test" {
|
||||
ami = "ami-f701cb97"
|
||||
instance_type = "t2.micro"
|
||||
subnet_id = "${aws_subnet.subnet.id}"
|
||||
}
|
||||
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 443
|
||||
protocol = "HTTPS"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
|
||||
deregistration_delay = 200
|
||||
|
||||
stickiness {
|
||||
type = "lb_cookie"
|
||||
cookie_duration = 10000
|
||||
}
|
||||
|
||||
health_check {
|
||||
path = "/health"
|
||||
interval = 60
|
||||
port = 8081
|
||||
protocol = "HTTP"
|
||||
timeout = 3
|
||||
healthy_threshold = 3
|
||||
unhealthy_threshold = 3
|
||||
matcher = "200-299"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "subnet" {
|
||||
cidr_block = "10.0.1.0/24"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
}`, targetGroupName)
|
||||
}
|
||||
|
||||
func testAccAWSALBTargetGroupAttachmentConfig_basic(targetGroupName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb_target_group_attachment" "test" {
|
||||
|
@ -3,6 +3,7 @@ package aws
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
@ -77,6 +78,47 @@ func TestAccAWSALBTargetGroup_basic(t *testing.T) {
|
||||
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.healthy_threshold", "3"),
|
||||
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.unhealthy_threshold", "3"),
|
||||
resource.TestCheckResourceAttr("aws_alb_target_group.test", "health_check.0.matcher", "200-299"),
|
||||
resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.%", "1"),
|
||||
resource.TestCheckResourceAttr("aws_alb_target_group.test", "tags.TestName", "TestAccAWSALBTargetGroup_basic"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBTargetGroup_namePrefix(t *testing.T) {
|
||||
var conf elbv2.TargetGroup
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_target_group.test",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBTargetGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBTargetGroupConfig_namePrefix,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBTargetGroupExists("aws_alb_target_group.test", &conf),
|
||||
resource.TestMatchResourceAttr("aws_alb_target_group.test", "name", regexp.MustCompile("^tf-")),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALBTargetGroup_generatedName(t *testing.T) {
|
||||
var conf elbv2.TargetGroup
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb_target_group.test",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBTargetGroupDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBTargetGroupConfig_generatedName,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSALBTargetGroupExists("aws_alb_target_group.test", &conf),
|
||||
),
|
||||
},
|
||||
},
|
||||
@ -713,3 +755,28 @@ resource "aws_vpc" "test" {
|
||||
}
|
||||
}`, targetGroupName, stickinessBlock)
|
||||
}
|
||||
|
||||
const testAccAWSALBTargetGroupConfig_namePrefix = `
|
||||
resource "aws_alb_target_group" "test" {
|
||||
name_prefix = "tf-"
|
||||
port = 80
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSALBTargetGroupConfig_generatedName = `
|
||||
resource "aws_alb_target_group" "test" {
|
||||
port = 80
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.test.id}"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
}
|
||||
`
|
||||
|
@ -67,6 +67,7 @@ func TestAccAWSALB_basic(t *testing.T) {
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "tags.TestName", "TestAccAWSALB_basic"),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "enable_deletion_protection", "false"),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "idle_timeout", "30"),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "ipv4"),
|
||||
resource.TestCheckResourceAttrSet("aws_alb.alb_test", "vpc_id"),
|
||||
resource.TestCheckResourceAttrSet("aws_alb.alb_test", "zone_id"),
|
||||
resource.TestCheckResourceAttrSet("aws_alb.alb_test", "dns_name"),
|
||||
@ -179,6 +180,63 @@ func TestAccAWSALB_updatedSecurityGroups(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALB_updatedSubnets(t *testing.T) {
|
||||
var pre, post elbv2.LoadBalancer
|
||||
albName := fmt.Sprintf("testaccawsalb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb.alb_test",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBConfig_basic(albName),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
testAccCheckAWSALBExists("aws_alb.alb_test", &pre),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "subnets.#", "2"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBConfig_updateSubnets(albName),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
testAccCheckAWSALBExists("aws_alb.alb_test", &post),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "subnets.#", "3"),
|
||||
testAccCheckAWSAlbARNs(&pre, &post),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccAWSALB_updatedIpAddressType(t *testing.T) {
|
||||
var pre, post elbv2.LoadBalancer
|
||||
albName := fmt.Sprintf("testaccawsalb-basic-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
IDRefreshName: "aws_alb.alb_test",
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSALBDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSALBConfigWithIpAddressType(albName),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
testAccCheckAWSALBExists("aws_alb.alb_test", &pre),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "ipv4"),
|
||||
),
|
||||
},
|
||||
{
|
||||
Config: testAccAWSALBConfigWithIpAddressTypeUpdated(albName),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
testAccCheckAWSALBExists("aws_alb.alb_test", &post),
|
||||
resource.TestCheckResourceAttr("aws_alb.alb_test", "ip_address_type", "dualstack"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestAccAWSALB_noSecurityGroup regression tests the issue in #8264,
|
||||
// where if an ALB is created without a security group, a default one
|
||||
// is assigned.
|
||||
@ -359,6 +417,228 @@ func testAccCheckAWSALBDestroy(s *terraform.State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAWSALBConfigWithIpAddressTypeUpdated(albName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test_1.id}", "${aws_subnet.alb_test_2.id}"]
|
||||
|
||||
ip_address_type = "dualstack"
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "test" {
|
||||
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_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 80
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
deregistration_delay = 200
|
||||
|
||||
stickiness {
|
||||
type = "lb_cookie"
|
||||
cookie_duration = 10000
|
||||
}
|
||||
|
||||
health_check {
|
||||
path = "/health2"
|
||||
interval = 30
|
||||
port = 8082
|
||||
protocol = "HTTPS"
|
||||
timeout = 4
|
||||
healthy_threshold = 4
|
||||
unhealthy_threshold = 4
|
||||
matcher = "200"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_egress_only_internet_gateway" "igw" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
assign_generated_ipv6_cidr_block = true
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "foo" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test_1" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "10.0.1.0/24"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "us-west-2a"
|
||||
ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 1)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test_2" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "10.0.2.0/24"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "us-west-2b"
|
||||
ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 2)}"
|
||||
|
||||
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"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, albName)
|
||||
}
|
||||
|
||||
func testAccAWSALBConfigWithIpAddressType(albName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
security_groups = ["${aws_security_group.alb_test.id}"]
|
||||
subnets = ["${aws_subnet.alb_test_1.id}", "${aws_subnet.alb_test_2.id}"]
|
||||
|
||||
ip_address_type = "ipv4"
|
||||
|
||||
idle_timeout = 30
|
||||
enable_deletion_protection = false
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_alb_listener" "test" {
|
||||
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_target_group" "test" {
|
||||
name = "%s"
|
||||
port = 80
|
||||
protocol = "HTTP"
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
|
||||
deregistration_delay = 200
|
||||
|
||||
stickiness {
|
||||
type = "lb_cookie"
|
||||
cookie_duration = 10000
|
||||
}
|
||||
|
||||
health_check {
|
||||
path = "/health2"
|
||||
interval = 30
|
||||
port = 8082
|
||||
protocol = "HTTPS"
|
||||
timeout = 4
|
||||
healthy_threshold = 4
|
||||
unhealthy_threshold = 4
|
||||
matcher = "200"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_egress_only_internet_gateway" "igw" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
}
|
||||
|
||||
resource "aws_vpc" "alb_test" {
|
||||
cidr_block = "10.0.0.0/16"
|
||||
assign_generated_ipv6_cidr_block = true
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_internet_gateway" "foo" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test_1" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "10.0.1.0/24"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "us-west-2a"
|
||||
ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 1)}"
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_subnet" "alb_test_2" {
|
||||
vpc_id = "${aws_vpc.alb_test.id}"
|
||||
cidr_block = "10.0.2.0/24"
|
||||
map_public_ip_on_launch = true
|
||||
availability_zone = "us-west-2b"
|
||||
ipv6_cidr_block = "${cidrsubnet(aws_vpc.alb_test.ipv6_cidr_block, 8, 2)}"
|
||||
|
||||
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"]
|
||||
}
|
||||
|
||||
tags {
|
||||
TestName = "TestAccAWSALB_basic"
|
||||
}
|
||||
}`, albName, albName)
|
||||
}
|
||||
|
||||
func testAccAWSALBConfig_basic(albName string) string {
|
||||
return fmt.Sprintf(`resource "aws_alb" "alb_test" {
|
||||
name = "%s"
|
||||
@ -426,6 +706,73 @@ resource "aws_security_group" "alb_test" {
|
||||
}`, albName)
|
||||
}
|
||||
|
||||
func testAccAWSALBConfig_updateSubnets(albName string) string {
|
||||
return fmt.Sprintf(`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"
|
||||
}
|
||||
}
|
||||
|
||||
variable "subnets" {
|
||||
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.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 = 3
|
||||
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)
|
||||
}
|
||||
|
||||
func testAccAWSALBConfig_generatedName() string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_alb" "alb_test" {
|
||||
|
@ -18,9 +18,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AWSAMIRetryTimeout = 10 * time.Minute
|
||||
AWSAMIRetryDelay = 5 * time.Second
|
||||
AWSAMIRetryMinTimeout = 3 * time.Second
|
||||
AWSAMIRetryTimeout = 40 * time.Minute
|
||||
AWSAMIDeleteRetryTimeout = 90 * time.Minute
|
||||
AWSAMIRetryDelay = 5 * time.Second
|
||||
AWSAMIRetryMinTimeout = 3 * time.Second
|
||||
)
|
||||
|
||||
func resourceAwsAmi() *schema.Resource {
|
||||
@ -329,7 +330,7 @@ func resourceAwsAmiWaitForDestroy(id string, client *ec2.EC2) error {
|
||||
Pending: []string{"available", "pending", "failed"},
|
||||
Target: []string{"destroyed"},
|
||||
Refresh: AMIStateRefreshFunc(client, id),
|
||||
Timeout: AWSAMIRetryTimeout,
|
||||
Timeout: AWSAMIDeleteRetryTimeout,
|
||||
Delay: AWSAMIRetryDelay,
|
||||
MinTimeout: AWSAMIRetryTimeout,
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
@ -16,13 +17,14 @@ import (
|
||||
func TestAccAWSAMIFromInstance(t *testing.T) {
|
||||
var amiId string
|
||||
snapshots := []string{}
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSAMIFromInstanceConfig,
|
||||
{
|
||||
Config: testAccAWSAMIFromInstanceConfig(rInt),
|
||||
Check: func(state *terraform.State) error {
|
||||
rs, ok := state.RootModule().Resources["aws_ami_from_instance.test"]
|
||||
if !ok {
|
||||
@ -51,13 +53,13 @@ func TestAccAWSAMIFromInstance(t *testing.T) {
|
||||
|
||||
image := describe.Images[0]
|
||||
if expected := "available"; *image.State != expected {
|
||||
return fmt.Errorf("invalid image state; expected %v, got %v", expected, image.State)
|
||||
return fmt.Errorf("invalid image state; expected %v, got %v", expected, *image.State)
|
||||
}
|
||||
if expected := "machine"; *image.ImageType != expected {
|
||||
return fmt.Errorf("wrong image type; expected %v, got %v", expected, image.ImageType)
|
||||
return fmt.Errorf("wrong image type; expected %v, got %v", expected, *image.ImageType)
|
||||
}
|
||||
if expected := "terraform-acc-ami-from-instance"; *image.Name != expected {
|
||||
return fmt.Errorf("wrong name; expected %v, got %v", expected, image.Name)
|
||||
if expected := fmt.Sprintf("terraform-acc-ami-from-instance-%d", rInt); *image.Name != expected {
|
||||
return fmt.Errorf("wrong name; expected %v, got %v", expected, *image.Name)
|
||||
}
|
||||
|
||||
for _, bdm := range image.BlockDeviceMappings {
|
||||
@ -137,24 +139,25 @@ func TestAccAWSAMIFromInstance(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
var testAccAWSAMIFromInstanceConfig = `
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
func testAccAWSAMIFromInstanceConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
}
|
||||
|
||||
resource "aws_instance" "test" {
|
||||
// This AMI has one block device mapping, so we expect to have
|
||||
// one snapshot in our created AMI.
|
||||
ami = "ami-408c7f28"
|
||||
instance_type = "t1.micro"
|
||||
tags {
|
||||
Name = "testAccAWSAMIFromInstanceConfig_TestAMI"
|
||||
}
|
||||
}
|
||||
resource "aws_instance" "test" {
|
||||
// This AMI has one block device mapping, so we expect to have
|
||||
// one snapshot in our created AMI.
|
||||
ami = "ami-408c7f28"
|
||||
instance_type = "t1.micro"
|
||||
tags {
|
||||
Name = "testAccAWSAMIFromInstanceConfig_TestAMI"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_ami_from_instance" "test" {
|
||||
name = "terraform-acc-ami-from-instance"
|
||||
description = "Testing Terraform aws_ami_from_instance resource"
|
||||
source_instance_id = "${aws_instance.test.id}"
|
||||
resource "aws_ami_from_instance" "test" {
|
||||
name = "terraform-acc-ami-from-instance-%d"
|
||||
description = "Testing Terraform aws_ami_from_instance resource"
|
||||
source_instance_id = "${aws_instance.test.id}"
|
||||
}`, rInt)
|
||||
}
|
||||
`
|
||||
|
@ -2,7 +2,11 @@ package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
@ -92,6 +96,12 @@ func hasLaunchPermission(conn *ec2.EC2, image_id string, account_id string) (boo
|
||||
Attribute: aws.String("launchPermission"),
|
||||
})
|
||||
if err != nil {
|
||||
// When an AMI disappears out from under a launch permission resource, we will
|
||||
// see either InvalidAMIID.NotFound or InvalidAMIID.Unavailable.
|
||||
if ec2err, ok := err.(awserr.Error); ok && strings.HasPrefix(ec2err.Code(), "InvalidAMIID") {
|
||||
log.Printf("[DEBUG] %s no longer exists, so we'll drop launch permission for %s from the state", image_id, account_id)
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
@ -2,15 +2,18 @@ package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
r "github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
r "github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSAMILaunchPermission_Basic(t *testing.T) {
|
||||
image_id := ""
|
||||
account_id := os.Getenv("AWS_ACCOUNT_ID")
|
||||
imageID := ""
|
||||
accountID := os.Getenv("AWS_ACCOUNT_ID")
|
||||
|
||||
r.Test(t, r.TestCase{
|
||||
PreCheck: func() {
|
||||
@ -23,19 +26,36 @@ func TestAccAWSAMILaunchPermission_Basic(t *testing.T) {
|
||||
Steps: []r.TestStep{
|
||||
// Scaffold everything
|
||||
r.TestStep{
|
||||
Config: testAccAWSAMILaunchPermissionConfig(account_id, true),
|
||||
Config: testAccAWSAMILaunchPermissionConfig(accountID, true),
|
||||
Check: r.ComposeTestCheckFunc(
|
||||
testCheckResourceGetAttr("aws_ami_copy.test", "id", &image_id),
|
||||
testAccAWSAMILaunchPermissionExists(account_id, &image_id),
|
||||
testCheckResourceGetAttr("aws_ami_copy.test", "id", &imageID),
|
||||
testAccAWSAMILaunchPermissionExists(accountID, &imageID),
|
||||
),
|
||||
},
|
||||
// Drop just launch permission to test destruction
|
||||
r.TestStep{
|
||||
Config: testAccAWSAMILaunchPermissionConfig(account_id, false),
|
||||
Config: testAccAWSAMILaunchPermissionConfig(accountID, false),
|
||||
Check: r.ComposeTestCheckFunc(
|
||||
testAccAWSAMILaunchPermissionDestroyed(account_id, &image_id),
|
||||
testAccAWSAMILaunchPermissionDestroyed(accountID, &imageID),
|
||||
),
|
||||
},
|
||||
// Re-add everything so we can test when AMI disappears
|
||||
r.TestStep{
|
||||
Config: testAccAWSAMILaunchPermissionConfig(accountID, true),
|
||||
Check: r.ComposeTestCheckFunc(
|
||||
testCheckResourceGetAttr("aws_ami_copy.test", "id", &imageID),
|
||||
testAccAWSAMILaunchPermissionExists(accountID, &imageID),
|
||||
),
|
||||
},
|
||||
// Here we delete the AMI to verify the follow-on refresh after this step
|
||||
// should not error.
|
||||
r.TestStep{
|
||||
Config: testAccAWSAMILaunchPermissionConfig(accountID, true),
|
||||
Check: r.ComposeTestCheckFunc(
|
||||
testAccAWSAMIDisappears(&imageID),
|
||||
),
|
||||
ExpectNonEmptyPlan: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -58,31 +78,53 @@ func testCheckResourceGetAttr(name, key string, value *string) r.TestCheckFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSAMILaunchPermissionExists(account_id string, image_id *string) r.TestCheckFunc {
|
||||
func testAccAWSAMILaunchPermissionExists(accountID string, imageID *string) r.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
if has, err := hasLaunchPermission(conn, *image_id, account_id); err != nil {
|
||||
if has, err := hasLaunchPermission(conn, *imageID, accountID); err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
return fmt.Errorf("launch permission does not exist for '%s' on '%s'", account_id, *image_id)
|
||||
return fmt.Errorf("launch permission does not exist for '%s' on '%s'", accountID, *imageID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSAMILaunchPermissionDestroyed(account_id string, image_id *string) r.TestCheckFunc {
|
||||
func testAccAWSAMILaunchPermissionDestroyed(accountID string, imageID *string) r.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
if has, err := hasLaunchPermission(conn, *image_id, account_id); err != nil {
|
||||
if has, err := hasLaunchPermission(conn, *imageID, accountID); err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
return fmt.Errorf("launch permission still exists for '%s' on '%s'", account_id, *image_id)
|
||||
return fmt.Errorf("launch permission still exists for '%s' on '%s'", accountID, *imageID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSAMILaunchPermissionConfig(account_id string, includeLaunchPermission bool) string {
|
||||
// testAccAWSAMIDisappears is technically a "test check function" but really it
|
||||
// exists to perform a side effect of deleting an AMI out from under a resource
|
||||
// so we can test that Terraform will react properly
|
||||
func testAccAWSAMIDisappears(imageID *string) r.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
||||
req := &ec2.DeregisterImageInput{
|
||||
ImageId: aws.String(*imageID),
|
||||
}
|
||||
|
||||
_, err := conn.DeregisterImage(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := resourceAwsAmiWaitForDestroy(*imageID, conn); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSAMILaunchPermissionConfig(accountID string, includeLaunchPermission bool) string {
|
||||
base := `
|
||||
resource "aws_ami_copy" "test" {
|
||||
name = "launch-permission-test"
|
||||
@ -101,5 +143,5 @@ resource "aws_ami_launch_permission" "self-test" {
|
||||
image_id = "${aws_ami_copy.test.id}"
|
||||
account_id = "%s"
|
||||
}
|
||||
`, account_id)
|
||||
`, accountID)
|
||||
}
|
||||
|
@ -42,8 +42,9 @@ func resourceAwsApiGatewayApiKey() *schema.Resource {
|
||||
},
|
||||
|
||||
"stage_key": {
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Type: schema.TypeSet,
|
||||
Optional: true,
|
||||
Deprecated: "Since the API Gateway usage plans feature was launched on August 11, 2016, usage plans are now required to associate an API key with an API stage",
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"rest_api_id": {
|
||||
@ -68,6 +69,15 @@ func resourceAwsApiGatewayApiKey() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"value": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
Sensitive: true,
|
||||
ValidateFunc: validateApiGatewayApiKeyValue,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -80,6 +90,7 @@ func resourceAwsApiGatewayApiKeyCreate(d *schema.ResourceData, meta interface{})
|
||||
Name: aws.String(d.Get("name").(string)),
|
||||
Description: aws.String(d.Get("description").(string)),
|
||||
Enabled: aws.Bool(d.Get("enabled").(bool)),
|
||||
Value: aws.String(d.Get("value").(string)),
|
||||
StageKeys: expandApiGatewayStageKeys(d),
|
||||
})
|
||||
if err != nil {
|
||||
@ -96,7 +107,8 @@ func resourceAwsApiGatewayApiKeyRead(d *schema.ResourceData, meta interface{}) e
|
||||
log.Printf("[DEBUG] Reading API Gateway API Key: %s", d.Id())
|
||||
|
||||
apiKey, err := conn.GetApiKey(&apigateway.GetApiKeyInput{
|
||||
ApiKey: aws.String(d.Id()),
|
||||
ApiKey: aws.String(d.Id()),
|
||||
IncludeValue: aws.Bool(true),
|
||||
})
|
||||
if err != nil {
|
||||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" {
|
||||
@ -111,6 +123,7 @@ func resourceAwsApiGatewayApiKeyRead(d *schema.ResourceData, meta interface{}) e
|
||||
d.Set("description", apiKey.Description)
|
||||
d.Set("enabled", apiKey.Enabled)
|
||||
d.Set("stage_key", flattenApiGatewayStageKeys(apiKey.StageKeys))
|
||||
d.Set("value", apiKey.Value)
|
||||
|
||||
if err := d.Set("created_date", apiKey.CreatedDate.Format(time.RFC3339)); err != nil {
|
||||
log.Printf("[DEBUG] Error setting created_date: %s", err)
|
||||
|
@ -33,6 +33,8 @@ func TestAccAWSAPIGatewayApiKey_basic(t *testing.T) {
|
||||
"aws_api_gateway_api_key.test", "created_date"),
|
||||
resource.TestCheckResourceAttrSet(
|
||||
"aws_api_gateway_api_key.test", "last_updated_date"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_api_key.custom", "value", "MyCustomToken#@&\"'(§!ç)-_*$€¨^£%ù+=/:.;?,|"),
|
||||
),
|
||||
},
|
||||
},
|
||||
@ -176,4 +178,15 @@ resource "aws_api_gateway_api_key" "test" {
|
||||
stage_name = "${aws_api_gateway_deployment.test.stage_name}"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_api_key" "custom" {
|
||||
name = "bar"
|
||||
enabled = true
|
||||
value = "MyCustomToken#@&\"'(§!ç)-_*$€¨^£%ù+=/:.;?,|"
|
||||
|
||||
stage_key {
|
||||
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
|
||||
stage_name = "${aws_api_gateway_deployment.test.stage_name}"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
@ -48,6 +48,7 @@ func resourceAwsApiGatewayDomainName() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
ForceNew: true,
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
ConflictsWith: []string{"certificate_arn"},
|
||||
},
|
||||
|
||||
|
@ -11,87 +11,94 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/apigateway"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func resourceAwsApiGatewayIntegration() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceAwsApiGatewayIntegrationCreate,
|
||||
Read: resourceAwsApiGatewayIntegrationRead,
|
||||
Update: resourceAwsApiGatewayIntegrationCreate,
|
||||
Update: resourceAwsApiGatewayIntegrationUpdate,
|
||||
Delete: resourceAwsApiGatewayIntegrationDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"rest_api_id": &schema.Schema{
|
||||
"rest_api_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"resource_id": &schema.Schema{
|
||||
"resource_id": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"http_method": &schema.Schema{
|
||||
"http_method": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateHTTPMethod,
|
||||
},
|
||||
|
||||
"type": &schema.Schema{
|
||||
"type": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateApiGatewayIntegrationType,
|
||||
},
|
||||
|
||||
"uri": &schema.Schema{
|
||||
"uri": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"credentials": &schema.Schema{
|
||||
"credentials": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"integration_http_method": &schema.Schema{
|
||||
"integration_http_method": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateHTTPMethod,
|
||||
},
|
||||
|
||||
"request_templates": &schema.Schema{
|
||||
"request_templates": {
|
||||
Type: schema.TypeMap,
|
||||
Optional: true,
|
||||
Elem: schema.TypeString,
|
||||
},
|
||||
|
||||
"request_parameters": &schema.Schema{
|
||||
"request_parameters": {
|
||||
Type: schema.TypeMap,
|
||||
Elem: schema.TypeString,
|
||||
Optional: true,
|
||||
ConflictsWith: []string{"request_parameters_in_json"},
|
||||
},
|
||||
|
||||
"request_parameters_in_json": &schema.Schema{
|
||||
"request_parameters_in_json": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ConflictsWith: []string{"request_parameters"},
|
||||
Deprecated: "Use field request_parameters instead",
|
||||
},
|
||||
|
||||
"content_handling": &schema.Schema{
|
||||
"content_handling": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateApiGatewayIntegrationContentHandling,
|
||||
},
|
||||
|
||||
"passthrough_behavior": &schema.Schema{
|
||||
"passthrough_behavior": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ForceNew: true,
|
||||
ValidateFunc: validateApiGatewayIntegrationPassthroughBehavior,
|
||||
},
|
||||
},
|
||||
@ -101,6 +108,7 @@ func resourceAwsApiGatewayIntegration() *schema.Resource {
|
||||
func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).apigateway
|
||||
|
||||
log.Print("[DEBUG] Creating API Gateway Integration")
|
||||
var integrationHttpMethod *string
|
||||
if v, ok := d.GetOk("integration_http_method"); ok {
|
||||
integrationHttpMethod = aws.String(v.(string))
|
||||
@ -163,13 +171,13 @@ func resourceAwsApiGatewayIntegrationCreate(d *schema.ResourceData, meta interfa
|
||||
|
||||
d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string)))
|
||||
|
||||
return nil
|
||||
return resourceAwsApiGatewayIntegrationRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).apigateway
|
||||
|
||||
log.Printf("[DEBUG] Reading API Gateway Integration %s", d.Id())
|
||||
log.Printf("[DEBUG] Reading API Gateway Integration: %s", d.Id())
|
||||
integration, err := conn.GetIntegration(&apigateway.GetIntegrationInput{
|
||||
HttpMethod: aws.String(d.Get("http_method").(string)),
|
||||
ResourceId: aws.String(d.Get("resource_id").(string)),
|
||||
@ -191,17 +199,127 @@ func resourceAwsApiGatewayIntegrationRead(d *schema.ResourceData, meta interface
|
||||
}
|
||||
|
||||
d.Set("request_templates", aws.StringValueMap(integration.RequestTemplates))
|
||||
d.Set("credentials", integration.Credentials)
|
||||
d.Set("type", integration.Type)
|
||||
d.Set("uri", integration.Uri)
|
||||
d.Set("request_parameters", aws.StringValueMap(integration.RequestParameters))
|
||||
d.Set("request_parameters_in_json", aws.StringValueMap(integration.RequestParameters))
|
||||
d.Set("passthrough_behavior", integration.PassthroughBehavior)
|
||||
d.Set("content_handling", integration.ContentHandling)
|
||||
|
||||
if integration.Uri != nil {
|
||||
d.Set("uri", integration.Uri)
|
||||
}
|
||||
|
||||
if integration.Credentials != nil {
|
||||
d.Set("credentials", integration.Credentials)
|
||||
}
|
||||
|
||||
if integration.ContentHandling != nil {
|
||||
d.Set("content_handling", integration.ContentHandling)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsApiGatewayIntegrationUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).apigateway
|
||||
|
||||
log.Printf("[DEBUG] Updating API Gateway Integration: %s", d.Id())
|
||||
operations := make([]*apigateway.PatchOperation, 0)
|
||||
|
||||
// https://docs.aws.amazon.com/apigateway/api-reference/link-relation/integration-update/#remarks
|
||||
// According to the above documentation, only a few parts are addable / removable.
|
||||
if d.HasChange("request_templates") {
|
||||
o, n := d.GetChange("request_templates")
|
||||
prefix := "requestTemplates"
|
||||
|
||||
os := o.(map[string]interface{})
|
||||
ns := n.(map[string]interface{})
|
||||
|
||||
// Handle Removal
|
||||
for k := range os {
|
||||
if _, ok := ns[k]; !ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("remove"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range ns {
|
||||
// Handle replaces
|
||||
if _, ok := os[k]; ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("replace"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
Value: aws.String(v.(string)),
|
||||
})
|
||||
}
|
||||
|
||||
// Handle additions
|
||||
if _, ok := os[k]; !ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("add"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
Value: aws.String(v.(string)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if d.HasChange("request_parameters") {
|
||||
o, n := d.GetChange("request_parameters")
|
||||
prefix := "requestParameters"
|
||||
|
||||
os := o.(map[string]interface{})
|
||||
ns := n.(map[string]interface{})
|
||||
|
||||
// Handle Removal
|
||||
for k := range os {
|
||||
if _, ok := ns[k]; !ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("remove"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range ns {
|
||||
// Handle replaces
|
||||
if _, ok := os[k]; ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("replace"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
Value: aws.String(v.(string)),
|
||||
})
|
||||
}
|
||||
|
||||
// Handle additions
|
||||
if _, ok := os[k]; !ok {
|
||||
operations = append(operations, &apigateway.PatchOperation{
|
||||
Op: aws.String("add"),
|
||||
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, strings.Replace(k, "/", "~1", -1))),
|
||||
Value: aws.String(v.(string)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params := &apigateway.UpdateIntegrationInput{
|
||||
HttpMethod: aws.String(d.Get("http_method").(string)),
|
||||
ResourceId: aws.String(d.Get("resource_id").(string)),
|
||||
RestApiId: aws.String(d.Get("rest_api_id").(string)),
|
||||
PatchOperations: operations,
|
||||
}
|
||||
|
||||
_, err := conn.UpdateIntegration(params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error updating API Gateway Integration: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string)))
|
||||
|
||||
return resourceAwsApiGatewayIntegrationRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceAwsApiGatewayIntegrationDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
conn := meta.(*AWSClient).apigateway
|
||||
log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id())
|
||||
|
@ -19,88 +19,80 @@ func TestAccAWSAPIGatewayIntegration_basic(t *testing.T) {
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSAPIGatewayIntegrationDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSAPIGatewayIntegrationConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
|
||||
testAccCheckAWSAPIGatewayIntegrationAttributes(&conf),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "type", "HTTP"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "integration_http_method", "GET"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "uri", "https://www.google.de"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "request_templates.application/json", ""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
|
||||
resource.TestCheckNoResourceAttr(
|
||||
"aws_api_gateway_integration.test", "content_handling"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
|
||||
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Foo", "'Bar'"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSAPIGatewayIntegrationConfigUpdate,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
|
||||
testAccCheckAWSAPIGatewayMockIntegrationAttributes(&conf),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "type", "MOCK"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "integration_http_method", ""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "uri", ""),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "passthrough_behavior", "NEVER"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_BINARY"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
|
||||
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'updated'"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-FooBar", "'Baz'"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", "{'foobar': 'bar}"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.text/html", "<html>Foo</html>"),
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
|
||||
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "0"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "0"),
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccAWSAPIGatewayIntegrationConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayIntegrationExists("aws_api_gateway_integration.test", &conf),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "type", "HTTP"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "integration_http_method", "GET"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "uri", "https://www.google.de"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "passthrough_behavior", "WHEN_NO_MATCH"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "content_handling", "CONVERT_TO_TEXT"),
|
||||
resource.TestCheckNoResourceAttr("aws_api_gateway_integration.test", "credentials"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_parameters.integration.request.header.X-Authorization", "'static'"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.%", "2"),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/json", ""),
|
||||
resource.TestCheckResourceAttr("aws_api_gateway_integration.test", "request_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckAWSAPIGatewayMockIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if *conf.Type != "MOCK" {
|
||||
return fmt.Errorf("Wrong Type: %q", *conf.Type)
|
||||
}
|
||||
if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'updated'" {
|
||||
return fmt.Errorf("wrong updated RequestParameters for header.X-Authorization")
|
||||
}
|
||||
if *conf.ContentHandling != "CONVERT_TO_BINARY" {
|
||||
return fmt.Errorf("wrong ContentHandling: %q", *conf.ContentHandling)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSAPIGatewayIntegrationAttributes(conf *apigateway.Integration) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
if *conf.HttpMethod == "" {
|
||||
return fmt.Errorf("empty HttpMethod")
|
||||
}
|
||||
if *conf.Uri != "https://www.google.de" {
|
||||
return fmt.Errorf("wrong Uri")
|
||||
}
|
||||
if *conf.Type != "HTTP" {
|
||||
return fmt.Errorf("wrong Type")
|
||||
}
|
||||
if conf.RequestTemplates["application/json"] != nil {
|
||||
return fmt.Errorf("wrong RequestTemplate for application/json")
|
||||
}
|
||||
if *conf.RequestTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" {
|
||||
return fmt.Errorf("wrong RequestTemplate for application/xml")
|
||||
}
|
||||
if *conf.RequestParameters["integration.request.header.X-Authorization"] != "'static'" {
|
||||
return fmt.Errorf("wrong RequestParameters for header.X-Authorization")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSAPIGatewayIntegrationExists(n string, res *apigateway.Integration) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
@ -196,13 +188,15 @@ resource "aws_api_gateway_integration" "test" {
|
||||
}
|
||||
|
||||
request_parameters = {
|
||||
"integration.request.header.X-Authorization" = "'static'"
|
||||
"integration.request.header.X-Authorization" = "'static'"
|
||||
"integration.request.header.X-Foo" = "'Bar'"
|
||||
}
|
||||
|
||||
type = "HTTP"
|
||||
uri = "https://www.google.de"
|
||||
integration_http_method = "GET"
|
||||
passthrough_behavior = "WHEN_NO_MATCH"
|
||||
content_handling = "CONVERT_TO_TEXT"
|
||||
}
|
||||
`
|
||||
|
||||
@ -233,13 +227,55 @@ resource "aws_api_gateway_integration" "test" {
|
||||
resource_id = "${aws_api_gateway_resource.test.id}"
|
||||
http_method = "${aws_api_gateway_method.test.http_method}"
|
||||
|
||||
request_parameters = {
|
||||
"integration.request.header.X-Authorization" = "'updated'"
|
||||
request_templates = {
|
||||
"application/json" = "{'foobar': 'bar}"
|
||||
"text/html" = "<html>Foo</html>"
|
||||
}
|
||||
|
||||
type = "MOCK"
|
||||
passthrough_behavior = "NEVER"
|
||||
content_handling = "CONVERT_TO_BINARY"
|
||||
request_parameters = {
|
||||
"integration.request.header.X-Authorization" = "'updated'"
|
||||
"integration.request.header.X-FooBar" = "'Baz'"
|
||||
}
|
||||
|
||||
type = "HTTP"
|
||||
uri = "https://www.google.de"
|
||||
integration_http_method = "GET"
|
||||
passthrough_behavior = "WHEN_NO_MATCH"
|
||||
content_handling = "CONVERT_TO_TEXT"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSAPIGatewayIntegrationConfigUpdateNoTemplates = `
|
||||
resource "aws_api_gateway_rest_api" "test" {
|
||||
name = "test"
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_resource" "test" {
|
||||
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
|
||||
parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}"
|
||||
path_part = "test"
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_method" "test" {
|
||||
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
|
||||
resource_id = "${aws_api_gateway_resource.test.id}"
|
||||
http_method = "GET"
|
||||
authorization = "NONE"
|
||||
|
||||
request_models = {
|
||||
"application/json" = "Error"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_integration" "test" {
|
||||
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
|
||||
resource_id = "${aws_api_gateway_resource.test.id}"
|
||||
http_method = "${aws_api_gateway_method.test.http_method}"
|
||||
|
||||
type = "HTTP"
|
||||
uri = "https://www.google.de"
|
||||
integration_http_method = "GET"
|
||||
passthrough_behavior = "WHEN_NO_MATCH"
|
||||
content_handling = "CONVERT_TO_TEXT"
|
||||
}
|
||||
`
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
|
||||
func TestAccAWSAPIGatewayMethod_basic(t *testing.T) {
|
||||
var conf apigateway.Method
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
@ -22,7 +23,7 @@ func TestAccAWSAPIGatewayMethod_basic(t *testing.T) {
|
||||
CheckDestroy: testAccCheckAWSAPIGatewayMethodDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAWSAPIGatewayMethodConfig,
|
||||
Config: testAccAWSAPIGatewayMethodConfig(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf),
|
||||
testAccCheckAWSAPIGatewayMethodAttributes(&conf),
|
||||
@ -36,7 +37,7 @@ func TestAccAWSAPIGatewayMethod_basic(t *testing.T) {
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccAWSAPIGatewayMethodConfigUpdate,
|
||||
Config: testAccAWSAPIGatewayMethodConfigUpdate(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf),
|
||||
testAccCheckAWSAPIGatewayMethodAttributesUpdate(&conf),
|
||||
@ -72,7 +73,7 @@ func TestAccAWSAPIGatewayMethod_customauthorizer(t *testing.T) {
|
||||
},
|
||||
|
||||
{
|
||||
Config: testAccAWSAPIGatewayMethodConfigUpdate,
|
||||
Config: testAccAWSAPIGatewayMethodConfigUpdate(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf),
|
||||
testAccCheckAWSAPIGatewayMethodAttributesUpdate(&conf),
|
||||
@ -199,7 +200,7 @@ func testAccCheckAWSAPIGatewayMethodDestroy(s *terraform.State) error {
|
||||
func testAccAWSAPIGatewayMethodConfigWithCustomAuthorizer(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_api_gateway_rest_api" "test" {
|
||||
name = "tf-acc-test-custom-auth"
|
||||
name = "tf-acc-test-custom-auth-%d"
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "invocation_role" {
|
||||
@ -261,7 +262,7 @@ EOF
|
||||
resource "aws_lambda_function" "authorizer" {
|
||||
filename = "test-fixtures/lambdatest.zip"
|
||||
source_code_hash = "${base64sha256(file("test-fixtures/lambdatest.zip"))}"
|
||||
function_name = "tf_acc_api_gateway_authorizer"
|
||||
function_name = "tf_acc_api_gateway_authorizer_%d"
|
||||
role = "${aws_iam_role.iam_for_lambda.arn}"
|
||||
handler = "exports.example"
|
||||
runtime = "nodejs4.3"
|
||||
@ -295,12 +296,13 @@ resource "aws_api_gateway_method" "test" {
|
||||
"method.request.header.Content-Type" = false
|
||||
"method.request.querystring.page" = true
|
||||
}
|
||||
}`, rInt, rInt, rInt)
|
||||
}`, rInt, rInt, rInt, rInt, rInt)
|
||||
}
|
||||
|
||||
const testAccAWSAPIGatewayMethodConfig = `
|
||||
func testAccAWSAPIGatewayMethodConfig(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_api_gateway_rest_api" "test" {
|
||||
name = "test"
|
||||
name = "tf-acc-test-apig-method-%d"
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_resource" "test" {
|
||||
@ -324,11 +326,13 @@ resource "aws_api_gateway_method" "test" {
|
||||
"method.request.querystring.page" = true
|
||||
}
|
||||
}
|
||||
`
|
||||
`, rInt)
|
||||
}
|
||||
|
||||
const testAccAWSAPIGatewayMethodConfigUpdate = `
|
||||
func testAccAWSAPIGatewayMethodConfigUpdate(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_api_gateway_rest_api" "test" {
|
||||
name = "test"
|
||||
name = "tf-acc-test-apig-method-%d"
|
||||
}
|
||||
|
||||
resource "aws_api_gateway_resource" "test" {
|
||||
@ -351,4 +355,5 @@ resource "aws_api_gateway_method" "test" {
|
||||
"method.request.querystring.page" = false
|
||||
}
|
||||
}
|
||||
`
|
||||
`, rInt)
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ func resourceAwsApiGatewayRestApiDelete(d *schema.ResourceData, meta interface{}
|
||||
conn := meta.(*AWSClient).apigateway
|
||||
log.Printf("[DEBUG] Deleting API Gateway: %s", d.Id())
|
||||
|
||||
return resource.Retry(5*time.Minute, func() *resource.RetryError {
|
||||
return resource.Retry(10*time.Minute, func() *resource.RetryError {
|
||||
_, err := conn.DeleteRestApi(&apigateway.DeleteRestApiInput{
|
||||
RestApiId: aws.String(d.Id()),
|
||||
})
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user