Fixing the merge conflicts on the s3 bucket object resource after another PR had been merged
This commit is contained in:
commit
3809cb5b88
41
CHANGELOG.md
41
CHANGELOG.md
|
@ -3,31 +3,57 @@
|
||||||
FEATURES:
|
FEATURES:
|
||||||
|
|
||||||
* **New provider: `rundeck`** [GH-2412]
|
* **New provider: `rundeck`** [GH-2412]
|
||||||
|
* **New provider: `packet`** [GH-2260], [GH-3472]
|
||||||
|
* **New provider: `vsphere`** [GH-3419]
|
||||||
* **New resource: `cloudstack_loadbalancer_rule`** [GH-2934]
|
* **New resource: `cloudstack_loadbalancer_rule`** [GH-2934]
|
||||||
* **New resource: `google_compute_project_metadata`** [GH-3065]
|
* **New resource: `google_compute_project_metadata`** [GH-3065]
|
||||||
* **New resources: `aws_ami`, `aws_ami_copy`, `aws_ami_from_instance`** [GH-2874]
|
* **New resources: `aws_ami`, `aws_ami_copy`, `aws_ami_from_instance`** [GH-2784]
|
||||||
|
* **New resources: `aws_cloudwatch_log_group`** [GH-2415]
|
||||||
* **New resource: `google_storage_bucket_object`** [GH-3192]
|
* **New resource: `google_storage_bucket_object`** [GH-3192]
|
||||||
* **New resources: `google_compute_vpn_gateway`, `google_compute_vpn_tunnel`** [GH-3213]
|
* **New resources: `google_compute_vpn_gateway`, `google_compute_vpn_tunnel`** [GH-3213]
|
||||||
|
* **New resources: `google_storage_bucket_acl`, `google_storage_object_acl`** [GH-3272]
|
||||||
|
* **New resource: `aws_iam_saml_provider`** [GH-3156]
|
||||||
|
* **New resources: `aws_efs_file_system` and `aws_efs_mount_target`** [GH-2196]
|
||||||
|
* **New resources: `aws_opsworks_*`** [GH-2162]
|
||||||
|
* **New resource: `aws_elasticsearch_domain`** [GH-3443]
|
||||||
|
* **New resource: `aws_directory_service_directory`** [GH-3228]
|
||||||
|
* **New resource: `aws_autoscaling_lifecycle_hook`** [GH-3351]
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
|
|
||||||
* core: Add a function to find the index of an element in a list. [GH-2704]
|
* core: Add a function to find the index of an element in a list. [GH-2704]
|
||||||
* core: Print all outputs when `terraform output` is called with no arguments [GH-2920]
|
* core: Print all outputs when `terraform output` is called with no arguments [GH-2920]
|
||||||
* core: In plan output summary, count resource replacement as Add/Remove instead of Change [GH-3173]
|
* core: In plan output summary, count resource replacement as Add/Remove instead of Change [GH-3173]
|
||||||
|
* core: Add interpolation functions for base64 encoding and decoding. [GH-3325]
|
||||||
|
* core: Expose parallelism as a CLI option instead of a hard-coding the default of 10 [GH-3365]
|
||||||
|
* core: Add interpolation function `compact`, to remove empty elements from a list. [GH-3239], [GH-3479]
|
||||||
|
* core: Allow filtering of log output by level, using e.g. ``TF_LOG=INFO`` [GH-3380]
|
||||||
* provider/aws: Add `instance_initiated_shutdown_behavior` to AWS Instance [GH-2887]
|
* provider/aws: Add `instance_initiated_shutdown_behavior` to AWS Instance [GH-2887]
|
||||||
* provider/aws: Support IAM role names (previously just ARNs) in `aws_ecs_service.iam_role` [GH-3061]
|
* provider/aws: Support IAM role names (previously just ARNs) in `aws_ecs_service.iam_role` [GH-3061]
|
||||||
* provider/aws: Add update method to RDS Subnet groups, can modify subnets without recreating [GH-3053]
|
* provider/aws: Add update method to RDS Subnet groups, can modify subnets without recreating [GH-3053]
|
||||||
* provider/aws: Paginate notifications returned for ASG Notifications [GH-3043]
|
* provider/aws: Paginate notifications returned for ASG Notifications [GH-3043]
|
||||||
* provider/aws: add `ses_smtp_password` to `aws_iam_access_key` [GH-3165]
|
* provider/aws: add `ses_smtp_password` to `aws_iam_access_key` [GH-3165]
|
||||||
* provider/aws: read `iam_instance_profile` for `aws_instance` and save to state [GH-3167]
|
* provider/aws: read `iam_instance_profile` for `aws_instance` and save to state [GH-3167]
|
||||||
|
* provider/aws: allow `instance` to be computed in `aws_eip` [GH-3036]
|
||||||
* provider/aws: Add `versioning` option to `aws_s3_bucket` [GH-2942]
|
* provider/aws: Add `versioning` option to `aws_s3_bucket` [GH-2942]
|
||||||
|
* provider/aws: Add `configuation_endpoint` to `aws_elasticache_cluster` [GH-3250]
|
||||||
|
* provider/aws: Add validation for `app_cookie_stickiness_policy.name` [GH-3277]
|
||||||
|
* provider/aws: Add validation for `db_parameter_group.name` [GH-3279]
|
||||||
|
* provider/aws: `aws_s3_bucket_object` allows interpolated content to be set with new `content` attribute. [GH-3200]
|
||||||
* provider/cloudstack: Add `project` parameter to `cloudstack_vpc`, `cloudstack_network`, `cloudstack_ipaddress` and `cloudstack_disk` [GH-3035]
|
* provider/cloudstack: Add `project` parameter to `cloudstack_vpc`, `cloudstack_network`, `cloudstack_ipaddress` and `cloudstack_disk` [GH-3035]
|
||||||
|
* provider/openstack: add functionality to attach FloatingIP to Port [GH-1788]
|
||||||
|
* provider/google: Can now do multi-region deployments without using multiple providers [GH-3258]
|
||||||
|
* remote/s3: Allow canned ACLs to be set on state objects. [GH-3233]
|
||||||
|
* remote/s3: Remote state is stored in S3 with `Content-Type: application/json` [GH-3385]
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
||||||
* core: Fix problems referencing list attributes in interpolations [GH-2157]
|
* core: Fix problems referencing list attributes in interpolations [GH-2157]
|
||||||
|
* core: don't error on computed value during input walk [GH-2988]
|
||||||
|
* core: Ignore missing variables during destroy phase [GH-3393]
|
||||||
* provider/google: Crashes with interface conversion in GCE Instance Template [GH-3027]
|
* provider/google: Crashes with interface conversion in GCE Instance Template [GH-3027]
|
||||||
* provider/google: Convert int to int64 when building the GKE cluster.NodeConfig struct [GH-2978]
|
* provider/google: Convert int to int64 when building the GKE cluster.NodeConfig struct [GH-2978]
|
||||||
|
* provider/google: google_compute_instance_template.network_interface.network should be a URL [GH-3226]
|
||||||
* provider/aws: Retry creation of `aws_ecs_service` if IAM policy isn't ready yet [GH-3061]
|
* provider/aws: Retry creation of `aws_ecs_service` if IAM policy isn't ready yet [GH-3061]
|
||||||
* provider/aws: Fix issue with mixed capitalization for RDS Instances [GH-3053]
|
* provider/aws: Fix issue with mixed capitalization for RDS Instances [GH-3053]
|
||||||
* provider/aws: Fix issue with RDS to allow major version upgrades [GH-3053]
|
* provider/aws: Fix issue with RDS to allow major version upgrades [GH-3053]
|
||||||
|
@ -37,8 +63,21 @@ BUG FIXES:
|
||||||
by AWS [GH-3120]
|
by AWS [GH-3120]
|
||||||
* provider/aws: Read instance source_dest_check and save to state [GH-3152]
|
* provider/aws: Read instance source_dest_check and save to state [GH-3152]
|
||||||
* provider/aws: Allow `weight = 0` in Route53 records [GH-3196]
|
* provider/aws: Allow `weight = 0` in Route53 records [GH-3196]
|
||||||
|
* provider/aws: Normalize aws_elasticache_cluster id to lowercase, allowing convergence. [GH-3235]
|
||||||
|
* provider/aws: Fix ValidateAccountId for IAM Instance Profiles [GH-3313]
|
||||||
|
* provider/docker: Fix issue preventing private images from being referenced [GH-2619]
|
||||||
|
* provider/digitalocean: Fix issue causing unnecessary diffs based on droplet slugsize case [GH-3284]
|
||||||
* provider/openstack: add state 'downloading' to list of expected states in
|
* provider/openstack: add state 'downloading' to list of expected states in
|
||||||
`blockstorage_volume_v1` creation [GH-2866]
|
`blockstorage_volume_v1` creation [GH-2866]
|
||||||
|
* provider/openstack: remove security groups (by name) before adding security
|
||||||
|
groups (by id) [GH-2008]
|
||||||
|
|
||||||
|
INTERNAL IMPROVEMENTS:
|
||||||
|
|
||||||
|
* core: Makefile target "plugin-dev" for building just one plugin. [GH-3229]
|
||||||
|
* helper/schema: Don't allow ``Update`` func if no attributes can actually be updated, per schema. [GH-3288]
|
||||||
|
* helper/schema: Default hashing function for sets [GH-3018]
|
||||||
|
* helper/multierror: Remove in favor of [github.com/hashicorp/go-multierror](http://github.com/hashicorp/go-multierror). [GH-3336]
|
||||||
|
|
||||||
## 0.6.3 (August 11, 2015)
|
## 0.6.3 (August 11, 2015)
|
||||||
|
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -15,6 +15,12 @@ dev: generate
|
||||||
quickdev: generate
|
quickdev: generate
|
||||||
@TF_QUICKDEV=1 TF_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'"
|
@TF_QUICKDEV=1 TF_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'"
|
||||||
|
|
||||||
|
# Shorthand for building and installing just one plugin for local testing.
|
||||||
|
# Run as (for example): make plugin-dev PLUGIN=provider-aws
|
||||||
|
plugin-dev: generate
|
||||||
|
go install github.com/hashicorp/terraform/builtin/bins/$(PLUGIN)
|
||||||
|
mv $(GOPATH)/bin/$(PLUGIN) $(GOPATH)/bin/terraform-$(PLUGIN)
|
||||||
|
|
||||||
release: updatedeps
|
release: updatedeps
|
||||||
gox -build-toolchain
|
gox -build-toolchain
|
||||||
@$(MAKE) bin
|
@$(MAKE) bin
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/builtin/providers/packet"
|
||||||
|
"github.com/hashicorp/terraform/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.Serve(&plugin.ServeOpts{
|
||||||
|
ProviderFunc: packet.Provider,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/builtin/providers/vsphere"
|
||||||
|
"github.com/hashicorp/terraform/plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.Serve(&plugin.ServeOpts{
|
||||||
|
ProviderFunc: vsphere.Provider,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package main
|
|
@ -19,7 +19,6 @@ func resourceArtifact() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceArtifactRead,
|
Create: resourceArtifactRead,
|
||||||
Read: resourceArtifactRead,
|
Read: resourceArtifactRead,
|
||||||
Update: resourceArtifactRead,
|
|
||||||
Delete: resourceArtifactDelete,
|
Delete: resourceArtifactDelete,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
|
|
@ -5,21 +5,26 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/service/autoscaling"
|
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
|
"github.com/aws/aws-sdk-go/service/directoryservice"
|
||||||
"github.com/aws/aws-sdk-go/service/dynamodb"
|
"github.com/aws/aws-sdk-go/service/dynamodb"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ecs"
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
"github.com/aws/aws-sdk-go/service/elasticache"
|
"github.com/aws/aws-sdk-go/service/elasticache"
|
||||||
|
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
|
||||||
"github.com/aws/aws-sdk-go/service/elb"
|
"github.com/aws/aws-sdk-go/service/elb"
|
||||||
"github.com/aws/aws-sdk-go/service/iam"
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
"github.com/aws/aws-sdk-go/service/kinesis"
|
"github.com/aws/aws-sdk-go/service/kinesis"
|
||||||
"github.com/aws/aws-sdk-go/service/lambda"
|
"github.com/aws/aws-sdk-go/service/lambda"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
"github.com/aws/aws-sdk-go/service/rds"
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
"github.com/aws/aws-sdk-go/service/route53"
|
"github.com/aws/aws-sdk-go/service/route53"
|
||||||
"github.com/aws/aws-sdk-go/service/s3"
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
|
@ -41,22 +46,27 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AWSClient struct {
|
type AWSClient struct {
|
||||||
cloudwatchconn *cloudwatch.CloudWatch
|
cloudwatchconn *cloudwatch.CloudWatch
|
||||||
dynamodbconn *dynamodb.DynamoDB
|
cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs
|
||||||
ec2conn *ec2.EC2
|
dsconn *directoryservice.DirectoryService
|
||||||
ecsconn *ecs.ECS
|
dynamodbconn *dynamodb.DynamoDB
|
||||||
elbconn *elb.ELB
|
ec2conn *ec2.EC2
|
||||||
autoscalingconn *autoscaling.AutoScaling
|
ecsconn *ecs.ECS
|
||||||
s3conn *s3.S3
|
efsconn *efs.EFS
|
||||||
sqsconn *sqs.SQS
|
elbconn *elb.ELB
|
||||||
snsconn *sns.SNS
|
esconn *elasticsearch.ElasticsearchService
|
||||||
r53conn *route53.Route53
|
autoscalingconn *autoscaling.AutoScaling
|
||||||
region string
|
s3conn *s3.S3
|
||||||
rdsconn *rds.RDS
|
sqsconn *sqs.SQS
|
||||||
iamconn *iam.IAM
|
snsconn *sns.SNS
|
||||||
kinesisconn *kinesis.Kinesis
|
r53conn *route53.Route53
|
||||||
elasticacheconn *elasticache.ElastiCache
|
region string
|
||||||
lambdaconn *lambda.Lambda
|
rdsconn *rds.RDS
|
||||||
|
iamconn *iam.IAM
|
||||||
|
kinesisconn *kinesis.Kinesis
|
||||||
|
elasticacheconn *elasticache.ElastiCache
|
||||||
|
lambdaconn *lambda.Lambda
|
||||||
|
opsworksconn *opsworks.OpsWorks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client configures and returns a fully initialized AWSClient
|
// Client configures and returns a fully initialized AWSClient
|
||||||
|
@ -102,6 +112,16 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
MaxRetries: aws.Int(c.MaxRetries),
|
MaxRetries: aws.Int(c.MaxRetries),
|
||||||
Endpoint: aws.String(c.DynamoDBEndpoint),
|
Endpoint: aws.String(c.DynamoDBEndpoint),
|
||||||
}
|
}
|
||||||
|
// Some services exist only in us-east-1, e.g. because they manage
|
||||||
|
// resources that can span across multiple regions, or because
|
||||||
|
// signature format v4 requires region to be us-east-1 for global
|
||||||
|
// endpoints:
|
||||||
|
// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
|
||||||
|
usEast1AwsConfig := &aws.Config{
|
||||||
|
Credentials: creds,
|
||||||
|
Region: aws.String("us-east-1"),
|
||||||
|
MaxRetries: aws.Int(c.MaxRetries),
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("[INFO] Initializing DynamoDB connection")
|
log.Println("[INFO] Initializing DynamoDB connection")
|
||||||
client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)
|
client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)
|
||||||
|
@ -138,15 +158,14 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
log.Println("[INFO] Initializing ECS Connection")
|
log.Println("[INFO] Initializing ECS Connection")
|
||||||
client.ecsconn = ecs.New(awsConfig)
|
client.ecsconn = ecs.New(awsConfig)
|
||||||
|
|
||||||
// aws-sdk-go uses v4 for signing requests, which requires all global
|
log.Println("[INFO] Initializing EFS Connection")
|
||||||
// endpoints to use 'us-east-1'.
|
client.efsconn = efs.New(awsConfig)
|
||||||
// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
|
|
||||||
|
log.Println("[INFO] Initializing ElasticSearch Connection")
|
||||||
|
client.esconn = elasticsearch.New(awsConfig)
|
||||||
|
|
||||||
log.Println("[INFO] Initializing Route 53 connection")
|
log.Println("[INFO] Initializing Route 53 connection")
|
||||||
client.r53conn = route53.New(&aws.Config{
|
client.r53conn = route53.New(usEast1AwsConfig)
|
||||||
Credentials: creds,
|
|
||||||
Region: aws.String("us-east-1"),
|
|
||||||
MaxRetries: aws.Int(c.MaxRetries),
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Println("[INFO] Initializing Elasticache Connection")
|
log.Println("[INFO] Initializing Elasticache Connection")
|
||||||
client.elasticacheconn = elasticache.New(awsConfig)
|
client.elasticacheconn = elasticache.New(awsConfig)
|
||||||
|
@ -156,6 +175,15 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
|
|
||||||
log.Println("[INFO] Initializing CloudWatch SDK connection")
|
log.Println("[INFO] Initializing CloudWatch SDK connection")
|
||||||
client.cloudwatchconn = cloudwatch.New(awsConfig)
|
client.cloudwatchconn = cloudwatch.New(awsConfig)
|
||||||
|
|
||||||
|
log.Println("[INFO] Initializing CloudWatch Logs connection")
|
||||||
|
client.cloudwatchlogsconn = cloudwatchlogs.New(awsConfig)
|
||||||
|
|
||||||
|
log.Println("[INFO] Initializing OpsWorks Connection")
|
||||||
|
client.opsworksconn = opsworks.New(usEast1AwsConfig)
|
||||||
|
|
||||||
|
log.Println("[INFO] Initializing Directory Service connection")
|
||||||
|
client.dsconn = directoryservice.New(awsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
@ -221,6 +249,7 @@ func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
|
||||||
// User may be an IAM instance profile, so fail silently.
|
// User may be an IAM instance profile, so fail silently.
|
||||||
// If it is an IAM instance profile
|
// If it is an IAM instance profile
|
||||||
// validating account might be superfluous
|
// validating account might be superfluous
|
||||||
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("Failed getting account ID from IAM: %s", err)
|
return fmt.Errorf("Failed getting account ID from IAM: %s", err)
|
||||||
// return error if the account id is explicitly not authorised
|
// return error if the account id is explicitly not authorised
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeAwsStringList(in []interface{}) []*string {
|
||||||
|
ret := make([]*string, len(in), len(in))
|
||||||
|
for i := 0; i < len(in); i++ {
|
||||||
|
ret[i] = aws.String(in[i].(string))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAwsStringSet(in *schema.Set) []*string {
|
||||||
|
inList := in.List()
|
||||||
|
ret := make([]*string, len(inList), len(inList))
|
||||||
|
for i := 0; i < len(ret); i++ {
|
||||||
|
ret[i] = aws.String(inList[i].(string))
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrapAwsStringList(in []*string) []string {
|
||||||
|
ret := make([]string, len(in), len(in))
|
||||||
|
for i := 0; i < len(in); i++ {
|
||||||
|
if in[i] != nil {
|
||||||
|
ret[i] = *in[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -29,7 +29,7 @@ func expandNetworkAclEntries(configured []interface{}, entryType string) ([]*ec2
|
||||||
From: aws.Int64(int64(data["from_port"].(int))),
|
From: aws.Int64(int64(data["from_port"].(int))),
|
||||||
To: aws.Int64(int64(data["to_port"].(int))),
|
To: aws.Int64(int64(data["to_port"].(int))),
|
||||||
},
|
},
|
||||||
Egress: aws.Bool((entryType == "egress")),
|
Egress: aws.Bool(entryType == "egress"),
|
||||||
RuleAction: aws.String(data["action"].(string)),
|
RuleAction: aws.String(data["action"].(string)),
|
||||||
RuleNumber: aws.Int64(int64(data["rule_no"].(int))),
|
RuleNumber: aws.Int64(int64(data["rule_no"].(int))),
|
||||||
CidrBlock: aws.String(data["cidr_block"].(string)),
|
CidrBlock: aws.String(data["cidr_block"].(string)),
|
||||||
|
|
|
@ -0,0 +1,558 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OpsWorks has a single concept of "layer" which represents several different
|
||||||
|
// layer types. The differences between these are in some extra properties that
|
||||||
|
// get packed into an "Attributes" map, but in the OpsWorks UI these are presented
|
||||||
|
// as first-class options, and so Terraform prefers to expose them this way and
|
||||||
|
// hide the implementation detail that they are all packed into a single type
|
||||||
|
// in the underlying API.
|
||||||
|
//
|
||||||
|
// This file contains utilities that are shared between all of the concrete
|
||||||
|
// layer resource types, which have names matching aws_opsworks_*_layer .
|
||||||
|
|
||||||
|
type opsworksLayerTypeAttribute struct {
|
||||||
|
AttrName string
|
||||||
|
Type schema.ValueType
|
||||||
|
Default interface{}
|
||||||
|
Required bool
|
||||||
|
WriteOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type opsworksLayerType struct {
|
||||||
|
TypeName string
|
||||||
|
DefaultLayerName string
|
||||||
|
Attributes map[string]*opsworksLayerTypeAttribute
|
||||||
|
CustomShortName bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
opsworksTrueString = "1"
|
||||||
|
opsworksFalseString = "0"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) SchemaResource() *schema.Resource {
|
||||||
|
resourceSchema := map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"auto_assign_elastic_ips": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"auto_assign_public_ips": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_instance_profile_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_setup_recipes": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_configure_recipes": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_deploy_recipes": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_undeploy_recipes": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_shutdown_recipes": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_security_group_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"auto_healing": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"install_updates_on_boot": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance_shutdown_timeout": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 120,
|
||||||
|
},
|
||||||
|
|
||||||
|
"drain_elb_on_shutdown": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"system_packages": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"stack_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"use_ebs_optimized_instances": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ebs_volume": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
|
||||||
|
"iops": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
"mount_point": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"number_of_disks": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"raid_level": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
"size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "standard",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
return hashcode.String(m["mount_point"].(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if lt.CustomShortName {
|
||||||
|
resourceSchema["short_name"] = &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lt.DefaultLayerName != "" {
|
||||||
|
resourceSchema["name"] = &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: lt.DefaultLayerName,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resourceSchema["name"] = &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, def := range lt.Attributes {
|
||||||
|
resourceSchema[key] = &schema.Schema{
|
||||||
|
Type: def.Type,
|
||||||
|
Default: def.Default,
|
||||||
|
Required: def.Required,
|
||||||
|
Optional: !def.Required,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &schema.Resource{
|
||||||
|
Read: func(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
return lt.Read(d, client)
|
||||||
|
},
|
||||||
|
Create: func(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
return lt.Create(d, client)
|
||||||
|
},
|
||||||
|
Update: func(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
return lt.Update(d, client)
|
||||||
|
},
|
||||||
|
Delete: func(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
return lt.Delete(d, client)
|
||||||
|
},
|
||||||
|
|
||||||
|
Schema: resourceSchema,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) Read(d *schema.ResourceData, client *opsworks.OpsWorks) error {
|
||||||
|
|
||||||
|
req := &opsworks.DescribeLayersInput{
|
||||||
|
LayerIds: []*string{
|
||||||
|
aws.String(d.Id()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Reading OpsWorks layer: %s", d.Id())
|
||||||
|
|
||||||
|
resp, err := client.DescribeLayers(req)
|
||||||
|
if err != nil {
|
||||||
|
if awserr, ok := err.(awserr.Error); ok {
|
||||||
|
if awserr.Code() == "ResourceNotFoundException" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
layer := resp.Layers[0]
|
||||||
|
d.Set("id", layer.LayerId)
|
||||||
|
d.Set("auto_assign_elastic_ips", layer.AutoAssignElasticIps)
|
||||||
|
d.Set("auto_assign_public_ips", layer.AutoAssignPublicIps)
|
||||||
|
d.Set("custom_instance_profile_arn", layer.CustomInstanceProfileArn)
|
||||||
|
d.Set("custom_security_group_ids", unwrapAwsStringList(layer.CustomSecurityGroupIds))
|
||||||
|
d.Set("auto_healing", layer.EnableAutoHealing)
|
||||||
|
d.Set("install_updates_on_boot", layer.InstallUpdatesOnBoot)
|
||||||
|
d.Set("name", layer.Name)
|
||||||
|
d.Set("system_packages", unwrapAwsStringList(layer.Packages))
|
||||||
|
d.Set("stack_id", layer.StackId)
|
||||||
|
d.Set("use_ebs_optimized_instances", layer.UseEbsOptimizedInstances)
|
||||||
|
|
||||||
|
if lt.CustomShortName {
|
||||||
|
d.Set("short_name", layer.Shortname)
|
||||||
|
}
|
||||||
|
|
||||||
|
lt.SetAttributeMap(d, layer.Attributes)
|
||||||
|
lt.SetLifecycleEventConfiguration(d, layer.LifecycleEventConfiguration)
|
||||||
|
lt.SetCustomRecipes(d, layer.CustomRecipes)
|
||||||
|
lt.SetVolumeConfigurations(d, layer.VolumeConfigurations)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) Create(d *schema.ResourceData, client *opsworks.OpsWorks) error {
|
||||||
|
|
||||||
|
req := &opsworks.CreateLayerInput{
|
||||||
|
AutoAssignElasticIps: aws.Bool(d.Get("auto_assign_elastic_ips").(bool)),
|
||||||
|
AutoAssignPublicIps: aws.Bool(d.Get("auto_assign_public_ips").(bool)),
|
||||||
|
CustomInstanceProfileArn: aws.String(d.Get("custom_instance_profile_arn").(string)),
|
||||||
|
CustomRecipes: lt.CustomRecipes(d),
|
||||||
|
CustomSecurityGroupIds: makeAwsStringSet(d.Get("custom_security_group_ids").(*schema.Set)),
|
||||||
|
EnableAutoHealing: aws.Bool(d.Get("auto_healing").(bool)),
|
||||||
|
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
|
||||||
|
LifecycleEventConfiguration: lt.LifecycleEventConfiguration(d),
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
Packages: makeAwsStringSet(d.Get("system_packages").(*schema.Set)),
|
||||||
|
Type: aws.String(lt.TypeName),
|
||||||
|
StackId: aws.String(d.Get("stack_id").(string)),
|
||||||
|
UseEbsOptimizedInstances: aws.Bool(d.Get("use_ebs_optimized_instances").(bool)),
|
||||||
|
Attributes: lt.AttributeMap(d),
|
||||||
|
VolumeConfigurations: lt.VolumeConfigurations(d),
|
||||||
|
}
|
||||||
|
|
||||||
|
if lt.CustomShortName {
|
||||||
|
req.Shortname = aws.String(d.Get("short_name").(string))
|
||||||
|
} else {
|
||||||
|
req.Shortname = aws.String(lt.TypeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating OpsWorks layer: %s", d.Id())
|
||||||
|
|
||||||
|
resp, err := client.CreateLayer(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
layerId := *resp.LayerId
|
||||||
|
d.SetId(layerId)
|
||||||
|
d.Set("id", layerId)
|
||||||
|
|
||||||
|
return lt.Read(d, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) Update(d *schema.ResourceData, client *opsworks.OpsWorks) error {
|
||||||
|
|
||||||
|
req := &opsworks.UpdateLayerInput{
|
||||||
|
LayerId: aws.String(d.Id()),
|
||||||
|
AutoAssignElasticIps: aws.Bool(d.Get("auto_assign_elastic_ips").(bool)),
|
||||||
|
AutoAssignPublicIps: aws.Bool(d.Get("auto_assign_public_ips").(bool)),
|
||||||
|
CustomInstanceProfileArn: aws.String(d.Get("custom_instance_profile_arn").(string)),
|
||||||
|
CustomRecipes: lt.CustomRecipes(d),
|
||||||
|
CustomSecurityGroupIds: makeAwsStringSet(d.Get("custom_security_group_ids").(*schema.Set)),
|
||||||
|
EnableAutoHealing: aws.Bool(d.Get("auto_healing").(bool)),
|
||||||
|
InstallUpdatesOnBoot: aws.Bool(d.Get("install_updates_on_boot").(bool)),
|
||||||
|
LifecycleEventConfiguration: lt.LifecycleEventConfiguration(d),
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
Packages: makeAwsStringSet(d.Get("system_packages").(*schema.Set)),
|
||||||
|
UseEbsOptimizedInstances: aws.Bool(d.Get("use_ebs_optimized_instances").(bool)),
|
||||||
|
Attributes: lt.AttributeMap(d),
|
||||||
|
VolumeConfigurations: lt.VolumeConfigurations(d),
|
||||||
|
}
|
||||||
|
|
||||||
|
if lt.CustomShortName {
|
||||||
|
req.Shortname = aws.String(d.Get("short_name").(string))
|
||||||
|
} else {
|
||||||
|
req.Shortname = aws.String(lt.TypeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Updating OpsWorks layer: %s", d.Id())
|
||||||
|
|
||||||
|
_, err := client.UpdateLayer(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return lt.Read(d, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) Delete(d *schema.ResourceData, client *opsworks.OpsWorks) error {
|
||||||
|
req := &opsworks.DeleteLayerInput{
|
||||||
|
LayerId: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting OpsWorks layer: %s", d.Id())
|
||||||
|
|
||||||
|
_, err := client.DeleteLayer(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) AttributeMap(d *schema.ResourceData) map[string]*string {
|
||||||
|
attrs := map[string]*string{}
|
||||||
|
|
||||||
|
for key, def := range lt.Attributes {
|
||||||
|
value := d.Get(key)
|
||||||
|
switch def.Type {
|
||||||
|
case schema.TypeString:
|
||||||
|
strValue := value.(string)
|
||||||
|
attrs[def.AttrName] = &strValue
|
||||||
|
case schema.TypeInt:
|
||||||
|
intValue := value.(int)
|
||||||
|
strValue := strconv.Itoa(intValue)
|
||||||
|
attrs[def.AttrName] = &strValue
|
||||||
|
case schema.TypeBool:
|
||||||
|
boolValue := value.(bool)
|
||||||
|
if boolValue {
|
||||||
|
attrs[def.AttrName] = &opsworksTrueString
|
||||||
|
} else {
|
||||||
|
attrs[def.AttrName] = &opsworksFalseString
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// should never happen
|
||||||
|
panic(fmt.Errorf("Unsupported OpsWorks layer attribute type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) SetAttributeMap(d *schema.ResourceData, attrs map[string]*string) {
|
||||||
|
for key, def := range lt.Attributes {
|
||||||
|
// Ignore write-only attributes; we'll just keep what we already have stored.
|
||||||
|
// (The AWS API returns garbage placeholder values for these.)
|
||||||
|
if def.WriteOnly {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strPtr, ok := attrs[def.AttrName]; ok && strPtr != nil {
|
||||||
|
strValue := *strPtr
|
||||||
|
|
||||||
|
switch def.Type {
|
||||||
|
case schema.TypeString:
|
||||||
|
d.Set(key, strValue)
|
||||||
|
case schema.TypeInt:
|
||||||
|
intValue, err := strconv.Atoi(strValue)
|
||||||
|
if err == nil {
|
||||||
|
d.Set(key, intValue)
|
||||||
|
} else {
|
||||||
|
// Got garbage from the AWS API
|
||||||
|
d.Set(key, nil)
|
||||||
|
}
|
||||||
|
case schema.TypeBool:
|
||||||
|
boolValue := true
|
||||||
|
if strValue == opsworksFalseString {
|
||||||
|
boolValue = false
|
||||||
|
}
|
||||||
|
d.Set(key, boolValue)
|
||||||
|
default:
|
||||||
|
// should never happen
|
||||||
|
panic(fmt.Errorf("Unsupported OpsWorks layer attribute type"))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
} else {
|
||||||
|
d.Set(key, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) LifecycleEventConfiguration(d *schema.ResourceData) *opsworks.LifecycleEventConfiguration {
|
||||||
|
return &opsworks.LifecycleEventConfiguration{
|
||||||
|
Shutdown: &opsworks.ShutdownEventConfiguration{
|
||||||
|
DelayUntilElbConnectionsDrained: aws.Bool(d.Get("drain_elb_on_shutdown").(bool)),
|
||||||
|
ExecutionTimeout: aws.Int64(int64(d.Get("instance_shutdown_timeout").(int))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) SetLifecycleEventConfiguration(d *schema.ResourceData, v *opsworks.LifecycleEventConfiguration) {
|
||||||
|
if v == nil || v.Shutdown == nil {
|
||||||
|
d.Set("drain_elb_on_shutdown", nil)
|
||||||
|
d.Set("instance_shutdown_timeout", nil)
|
||||||
|
} else {
|
||||||
|
d.Set("drain_elb_on_shutdown", v.Shutdown.DelayUntilElbConnectionsDrained)
|
||||||
|
d.Set("instance_shutdown_timeout", v.Shutdown.ExecutionTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) CustomRecipes(d *schema.ResourceData) *opsworks.Recipes {
|
||||||
|
return &opsworks.Recipes{
|
||||||
|
Configure: makeAwsStringList(d.Get("custom_configure_recipes").([]interface{})),
|
||||||
|
Deploy: makeAwsStringList(d.Get("custom_deploy_recipes").([]interface{})),
|
||||||
|
Setup: makeAwsStringList(d.Get("custom_setup_recipes").([]interface{})),
|
||||||
|
Shutdown: makeAwsStringList(d.Get("custom_shutdown_recipes").([]interface{})),
|
||||||
|
Undeploy: makeAwsStringList(d.Get("custom_undeploy_recipes").([]interface{})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) SetCustomRecipes(d *schema.ResourceData, v *opsworks.Recipes) {
|
||||||
|
// Null out everything first, and then we'll consider what to put back.
|
||||||
|
d.Set("custom_configure_recipes", nil)
|
||||||
|
d.Set("custom_deploy_recipes", nil)
|
||||||
|
d.Set("custom_setup_recipes", nil)
|
||||||
|
d.Set("custom_shutdown_recipes", nil)
|
||||||
|
d.Set("custom_undeploy_recipes", nil)
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("custom_configure_recipes", unwrapAwsStringList(v.Configure))
|
||||||
|
d.Set("custom_deploy_recipes", unwrapAwsStringList(v.Deploy))
|
||||||
|
d.Set("custom_setup_recipes", unwrapAwsStringList(v.Setup))
|
||||||
|
d.Set("custom_shutdown_recipes", unwrapAwsStringList(v.Shutdown))
|
||||||
|
d.Set("custom_undeploy_recipes", unwrapAwsStringList(v.Undeploy))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) VolumeConfigurations(d *schema.ResourceData) []*opsworks.VolumeConfiguration {
|
||||||
|
configuredVolumes := d.Get("ebs_volume").(*schema.Set).List()
|
||||||
|
result := make([]*opsworks.VolumeConfiguration, len(configuredVolumes))
|
||||||
|
|
||||||
|
for i := 0; i < len(configuredVolumes); i++ {
|
||||||
|
volumeData := configuredVolumes[i].(map[string]interface{})
|
||||||
|
|
||||||
|
result[i] = &opsworks.VolumeConfiguration{
|
||||||
|
MountPoint: aws.String(volumeData["mount_point"].(string)),
|
||||||
|
NumberOfDisks: aws.Int64(int64(volumeData["number_of_disks"].(int))),
|
||||||
|
Size: aws.Int64(int64(volumeData["size"].(int))),
|
||||||
|
VolumeType: aws.String(volumeData["type"].(string)),
|
||||||
|
}
|
||||||
|
iops := int64(volumeData["iops"].(int))
|
||||||
|
if iops != 0 {
|
||||||
|
result[i].Iops = aws.Int64(iops)
|
||||||
|
}
|
||||||
|
|
||||||
|
raidLevelStr := volumeData["raid_level"].(string)
|
||||||
|
if raidLevelStr != "" {
|
||||||
|
raidLevel, err := strconv.Atoi(raidLevelStr)
|
||||||
|
if err == nil {
|
||||||
|
result[i].RaidLevel = aws.Int64(int64(raidLevel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lt *opsworksLayerType) SetVolumeConfigurations(d *schema.ResourceData, v []*opsworks.VolumeConfiguration) {
|
||||||
|
newValue := make([]*map[string]interface{}, len(v))
|
||||||
|
|
||||||
|
for i := 0; i < len(v); i++ {
|
||||||
|
config := v[i]
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
newValue[i] = &data
|
||||||
|
|
||||||
|
if config.Iops != nil {
|
||||||
|
data["iops"] = int(*config.Iops)
|
||||||
|
} else {
|
||||||
|
data["iops"] = 0
|
||||||
|
}
|
||||||
|
if config.MountPoint != nil {
|
||||||
|
data["mount_point"] = *config.MountPoint
|
||||||
|
}
|
||||||
|
if config.NumberOfDisks != nil {
|
||||||
|
data["number_of_disks"] = int(*config.NumberOfDisks)
|
||||||
|
}
|
||||||
|
if config.RaidLevel != nil {
|
||||||
|
data["raid_level"] = strconv.Itoa(int(*config.RaidLevel))
|
||||||
|
}
|
||||||
|
if config.Size != nil {
|
||||||
|
data["size"] = int(*config.Size)
|
||||||
|
}
|
||||||
|
if config.VolumeType != nil {
|
||||||
|
data["type"] = *config.VolumeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("ebs_volume", newValue)
|
||||||
|
}
|
|
@ -163,22 +163,28 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
|
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
|
||||||
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
|
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
|
||||||
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),
|
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),
|
||||||
|
"aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(),
|
||||||
|
"aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(),
|
||||||
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
|
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
|
||||||
"aws_customer_gateway": resourceAwsCustomerGateway(),
|
"aws_customer_gateway": resourceAwsCustomerGateway(),
|
||||||
"aws_db_instance": resourceAwsDbInstance(),
|
"aws_db_instance": resourceAwsDbInstance(),
|
||||||
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
||||||
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
||||||
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
||||||
|
"aws_directory_service_directory": resourceAwsDirectoryServiceDirectory(),
|
||||||
"aws_dynamodb_table": resourceAwsDynamoDbTable(),
|
"aws_dynamodb_table": resourceAwsDynamoDbTable(),
|
||||||
"aws_ebs_volume": resourceAwsEbsVolume(),
|
"aws_ebs_volume": resourceAwsEbsVolume(),
|
||||||
"aws_ecs_cluster": resourceAwsEcsCluster(),
|
"aws_ecs_cluster": resourceAwsEcsCluster(),
|
||||||
"aws_ecs_service": resourceAwsEcsService(),
|
"aws_ecs_service": resourceAwsEcsService(),
|
||||||
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
|
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
|
||||||
|
"aws_efs_file_system": resourceAwsEfsFileSystem(),
|
||||||
|
"aws_efs_mount_target": resourceAwsEfsMountTarget(),
|
||||||
"aws_eip": resourceAwsEip(),
|
"aws_eip": resourceAwsEip(),
|
||||||
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
|
||||||
"aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(),
|
"aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(),
|
||||||
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
|
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
|
||||||
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
|
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
|
||||||
|
"aws_elasticsearch_domain": resourceAwsElasticSearchDomain(),
|
||||||
"aws_elb": resourceAwsElb(),
|
"aws_elb": resourceAwsElb(),
|
||||||
"aws_flow_log": resourceAwsFlowLog(),
|
"aws_flow_log": resourceAwsFlowLog(),
|
||||||
"aws_iam_access_key": resourceAwsIamAccessKey(),
|
"aws_iam_access_key": resourceAwsIamAccessKey(),
|
||||||
|
@ -190,6 +196,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(),
|
"aws_iam_policy_attachment": resourceAwsIamPolicyAttachment(),
|
||||||
"aws_iam_role_policy": resourceAwsIamRolePolicy(),
|
"aws_iam_role_policy": resourceAwsIamRolePolicy(),
|
||||||
"aws_iam_role": resourceAwsIamRole(),
|
"aws_iam_role": resourceAwsIamRole(),
|
||||||
|
"aws_iam_saml_provider": resourceAwsIamSamlProvider(),
|
||||||
"aws_iam_server_certificate": resourceAwsIAMServerCertificate(),
|
"aws_iam_server_certificate": resourceAwsIAMServerCertificate(),
|
||||||
"aws_iam_user_policy": resourceAwsIamUserPolicy(),
|
"aws_iam_user_policy": resourceAwsIamUserPolicy(),
|
||||||
"aws_iam_user": resourceAwsIamUser(),
|
"aws_iam_user": resourceAwsIamUser(),
|
||||||
|
@ -203,7 +210,20 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
|
"aws_main_route_table_association": resourceAwsMainRouteTableAssociation(),
|
||||||
"aws_network_acl": resourceAwsNetworkAcl(),
|
"aws_network_acl": resourceAwsNetworkAcl(),
|
||||||
"aws_network_interface": resourceAwsNetworkInterface(),
|
"aws_network_interface": resourceAwsNetworkInterface(),
|
||||||
|
"aws_opsworks_stack": resourceAwsOpsworksStack(),
|
||||||
|
"aws_opsworks_java_app_layer": resourceAwsOpsworksJavaAppLayer(),
|
||||||
|
"aws_opsworks_haproxy_layer": resourceAwsOpsworksHaproxyLayer(),
|
||||||
|
"aws_opsworks_static_web_layer": resourceAwsOpsworksStaticWebLayer(),
|
||||||
|
"aws_opsworks_php_app_layer": resourceAwsOpsworksPhpAppLayer(),
|
||||||
|
"aws_opsworks_rails_app_layer": resourceAwsOpsworksRailsAppLayer(),
|
||||||
|
"aws_opsworks_nodejs_app_layer": resourceAwsOpsworksNodejsAppLayer(),
|
||||||
|
"aws_opsworks_memcached_layer": resourceAwsOpsworksMemcachedLayer(),
|
||||||
|
"aws_opsworks_mysql_layer": resourceAwsOpsworksMysqlLayer(),
|
||||||
|
"aws_opsworks_ganglia_layer": resourceAwsOpsworksGangliaLayer(),
|
||||||
|
"aws_opsworks_custom_layer": resourceAwsOpsworksCustomLayer(),
|
||||||
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
"aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(),
|
||||||
|
"aws_rds_cluster": resourceAwsRDSCluster(),
|
||||||
|
"aws_rds_cluster_instance": resourceAwsRDSClusterInstance(),
|
||||||
"aws_route53_delegation_set": resourceAwsRoute53DelegationSet(),
|
"aws_route53_delegation_set": resourceAwsRoute53DelegationSet(),
|
||||||
"aws_route53_record": resourceAwsRoute53Record(),
|
"aws_route53_record": resourceAwsRoute53Record(),
|
||||||
"aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(),
|
"aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(),
|
||||||
|
|
|
@ -130,7 +130,7 @@ func resourceAwsAmiRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
image := res.Images[0]
|
image := res.Images[0]
|
||||||
state := *(image.State)
|
state := *image.State
|
||||||
|
|
||||||
if state == "pending" {
|
if state == "pending" {
|
||||||
// This could happen if a user manually adds an image we didn't create
|
// This could happen if a user manually adds an image we didn't create
|
||||||
|
@ -142,7 +142,7 @@ func resourceAwsAmiRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state = *(image.State)
|
state = *image.State
|
||||||
}
|
}
|
||||||
|
|
||||||
if state == "deregistered" {
|
if state == "deregistered" {
|
||||||
|
@ -170,22 +170,22 @@ func resourceAwsAmiRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
for _, blockDev := range image.BlockDeviceMappings {
|
for _, blockDev := range image.BlockDeviceMappings {
|
||||||
if blockDev.Ebs != nil {
|
if blockDev.Ebs != nil {
|
||||||
ebsBlockDev := map[string]interface{}{
|
ebsBlockDev := map[string]interface{}{
|
||||||
"device_name": *(blockDev.DeviceName),
|
"device_name": *blockDev.DeviceName,
|
||||||
"delete_on_termination": *(blockDev.Ebs.DeleteOnTermination),
|
"delete_on_termination": *blockDev.Ebs.DeleteOnTermination,
|
||||||
"encrypted": *(blockDev.Ebs.Encrypted),
|
"encrypted": *blockDev.Ebs.Encrypted,
|
||||||
"iops": 0,
|
"iops": 0,
|
||||||
"snapshot_id": *(blockDev.Ebs.SnapshotId),
|
"snapshot_id": *blockDev.Ebs.SnapshotId,
|
||||||
"volume_size": int(*(blockDev.Ebs.VolumeSize)),
|
"volume_size": int(*blockDev.Ebs.VolumeSize),
|
||||||
"volume_type": *(blockDev.Ebs.VolumeType),
|
"volume_type": *blockDev.Ebs.VolumeType,
|
||||||
}
|
}
|
||||||
if blockDev.Ebs.Iops != nil {
|
if blockDev.Ebs.Iops != nil {
|
||||||
ebsBlockDev["iops"] = int(*(blockDev.Ebs.Iops))
|
ebsBlockDev["iops"] = int(*blockDev.Ebs.Iops)
|
||||||
}
|
}
|
||||||
ebsBlockDevs = append(ebsBlockDevs, ebsBlockDev)
|
ebsBlockDevs = append(ebsBlockDevs, ebsBlockDev)
|
||||||
} else {
|
} else {
|
||||||
ephemeralBlockDevs = append(ephemeralBlockDevs, map[string]interface{}{
|
ephemeralBlockDevs = append(ephemeralBlockDevs, map[string]interface{}{
|
||||||
"device_name": *(blockDev.DeviceName),
|
"device_name": *blockDev.DeviceName,
|
||||||
"virtual_name": *(blockDev.VirtualName),
|
"virtual_name": *blockDev.VirtualName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -301,7 +301,7 @@ func resourceAwsAmiWaitForAvailable(id string, client *ec2.EC2) (*ec2.Image, err
|
||||||
return nil, fmt.Errorf("new AMI vanished while pending")
|
return nil, fmt.Errorf("new AMI vanished while pending")
|
||||||
}
|
}
|
||||||
|
|
||||||
state := *(res.Images[0].State)
|
state := *res.Images[0].State
|
||||||
|
|
||||||
if state == "pending" {
|
if state == "pending" {
|
||||||
// Give it a few seconds before we poll again.
|
// Give it a few seconds before we poll again.
|
||||||
|
@ -316,7 +316,7 @@ func resourceAwsAmiWaitForAvailable(id string, client *ec2.EC2) (*ec2.Image, err
|
||||||
|
|
||||||
// If we're not pending or available then we're in one of the invalid/error
|
// If we're not pending or available then we're in one of the invalid/error
|
||||||
// states, so stop polling and bail out.
|
// states, so stop polling and bail out.
|
||||||
stateReason := *(res.Images[0].StateReason)
|
stateReason := *res.Images[0].StateReason
|
||||||
return nil, fmt.Errorf("new AMI became %s while pending: %s", state, stateReason)
|
return nil, fmt.Errorf("new AMI became %s while pending: %s", state, stateReason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
@ -15,8 +16,6 @@ func resourceAwsAppCookieStickinessPolicy() *schema.Resource {
|
||||||
// There is no concept of "updating" an App Stickiness policy in
|
// There is no concept of "updating" an App Stickiness policy in
|
||||||
// the AWS API.
|
// the AWS API.
|
||||||
Create: resourceAwsAppCookieStickinessPolicyCreate,
|
Create: resourceAwsAppCookieStickinessPolicyCreate,
|
||||||
Update: resourceAwsAppCookieStickinessPolicyCreate,
|
|
||||||
|
|
||||||
Read: resourceAwsAppCookieStickinessPolicyRead,
|
Read: resourceAwsAppCookieStickinessPolicyRead,
|
||||||
Delete: resourceAwsAppCookieStickinessPolicyDelete,
|
Delete: resourceAwsAppCookieStickinessPolicyDelete,
|
||||||
|
|
||||||
|
@ -25,6 +24,14 @@ func resourceAwsAppCookieStickinessPolicy() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
|
||||||
|
es = append(es, fmt.Errorf(
|
||||||
|
"only alphanumeric characters and hyphens allowed in %q", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"load_balancer": &schema.Schema{
|
"load_balancer": &schema.Schema{
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsAutoscalingLifecycleHook() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsAutoscalingLifecycleHookPut,
|
||||||
|
Read: resourceAwsAutoscalingLifecycleHookRead,
|
||||||
|
Update: resourceAwsAutoscalingLifecycleHookPut,
|
||||||
|
Delete: resourceAwsAutoscalingLifecycleHookDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"autoscaling_group_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"default_result": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"heartbeat_timeout": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"lifecycle_transition": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"notification_metadata": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"notification_target_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"role_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsAutoscalingLifecycleHookPut(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
autoscalingconn := meta.(*AWSClient).autoscalingconn
|
||||||
|
|
||||||
|
params := getAwsAutoscalingPutLifecycleHookInput(d)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] AutoScaling PutLifecyleHook: %#v", params)
|
||||||
|
_, err := autoscalingconn.PutLifecycleHook(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error putting lifecycle hook: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(d.Get("name").(string))
|
||||||
|
|
||||||
|
return resourceAwsAutoscalingLifecycleHookRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsAutoscalingLifecycleHookRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
p, err := getAwsAutoscalingLifecycleHook(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Read Lifecycle Hook: ASG: %s, SH: %s, Obj: %#v", d.Get("autoscaling_group_name"), d.Get("name"), p)
|
||||||
|
|
||||||
|
d.Set("default_result", p.DefaultResult)
|
||||||
|
d.Set("heartbeat_timeout", p.HeartbeatTimeout)
|
||||||
|
d.Set("lifecycle_transition", p.LifecycleTransition)
|
||||||
|
d.Set("notification_metadata", p.NotificationMetadata)
|
||||||
|
d.Set("notification_target_arn", p.NotificationTargetARN)
|
||||||
|
d.Set("name", p.LifecycleHookName)
|
||||||
|
d.Set("role_arn", p.RoleARN)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsAutoscalingLifecycleHookDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
autoscalingconn := meta.(*AWSClient).autoscalingconn
|
||||||
|
p, err := getAwsAutoscalingLifecycleHook(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
params := autoscaling.DeleteLifecycleHookInput{
|
||||||
|
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
|
||||||
|
LifecycleHookName: aws.String(d.Get("name").(string)),
|
||||||
|
}
|
||||||
|
if _, err := autoscalingconn.DeleteLifecycleHook(¶ms); err != nil {
|
||||||
|
return fmt.Errorf("Autoscaling Lifecycle Hook: %s ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAwsAutoscalingPutLifecycleHookInput(d *schema.ResourceData) autoscaling.PutLifecycleHookInput {
|
||||||
|
var params = autoscaling.PutLifecycleHookInput{
|
||||||
|
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
|
||||||
|
LifecycleHookName: aws.String(d.Get("name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("default_result"); ok {
|
||||||
|
params.DefaultResult = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("heartbeat_timeout"); ok {
|
||||||
|
params.HeartbeatTimeout = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("lifecycle_transition"); ok {
|
||||||
|
params.LifecycleTransition = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("notification_metadata"); ok {
|
||||||
|
params.NotificationMetadata = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("notification_target_arn"); ok {
|
||||||
|
params.NotificationTargetARN = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("role_arn"); ok {
|
||||||
|
params.RoleARN = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAwsAutoscalingLifecycleHook(d *schema.ResourceData, meta interface{}) (*autoscaling.LifecycleHook, error) {
|
||||||
|
autoscalingconn := meta.(*AWSClient).autoscalingconn
|
||||||
|
|
||||||
|
params := autoscaling.DescribeLifecycleHooksInput{
|
||||||
|
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
|
||||||
|
LifecycleHookNames: []*string{aws.String(d.Get("name").(string))},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] AutoScaling Lifecycle Hook Describe Params: %#v", params)
|
||||||
|
resp, err := autoscalingconn.DescribeLifecycleHooks(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error retrieving lifecycle hooks: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find lifecycle hooks
|
||||||
|
name := d.Get("name")
|
||||||
|
for idx, sp := range resp.LifecycleHooks {
|
||||||
|
if *sp.LifecycleHookName == name {
|
||||||
|
return resp.LifecycleHooks[idx], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lifecycle hook not found
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/autoscaling"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSAutoscalingLifecycleHook_basic(t *testing.T) {
|
||||||
|
var hook autoscaling.LifecycleHook
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSAutoscalingLifecycleHookDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSAutoscalingLifecycleHookConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckLifecycleHookExists("aws_autoscaling_lifecycle_hook.foobar", &hook),
|
||||||
|
resource.TestCheckResourceAttr("aws_autoscaling_lifecycle_hook.foobar", "autoscaling_group_name", "terraform-test-foobar5"),
|
||||||
|
resource.TestCheckResourceAttr("aws_autoscaling_lifecycle_hook.foobar", "default_result", "CONTINUE"),
|
||||||
|
resource.TestCheckResourceAttr("aws_autoscaling_lifecycle_hook.foobar", "heartbeat_timeout", "2000"),
|
||||||
|
resource.TestCheckResourceAttr("aws_autoscaling_lifecycle_hook.foobar", "lifecycle_transition", "autoscaling:EC2_INSTANCE_LAUNCHING"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckLifecycleHookExists(n string, hook *autoscaling.LifecycleHook) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
rs = rs
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
|
||||||
|
params := &autoscaling.DescribeLifecycleHooksInput{
|
||||||
|
AutoScalingGroupName: aws.String(rs.Primary.Attributes["autoscaling_group_name"]),
|
||||||
|
LifecycleHookNames: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
}
|
||||||
|
resp, err := conn.DescribeLifecycleHooks(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.LifecycleHooks) == 0 {
|
||||||
|
return fmt.Errorf("LifecycleHook not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSAutoscalingLifecycleHookDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_autoscaling_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
params := autoscaling.DescribeLifecycleHooksInput{
|
||||||
|
AutoScalingGroupName: aws.String(rs.Primary.Attributes["autoscaling_group_name"]),
|
||||||
|
LifecycleHookNames: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := conn.DescribeLifecycleHooks(¶ms)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if len(resp.LifecycleHooks) != 0 &&
|
||||||
|
*resp.LifecycleHooks[0].LifecycleHookName == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Lifecycle Hook Still Exists: %s", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAWSAutoscalingLifecycleHookConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_launch_configuration" "foobar" {
|
||||||
|
name = "terraform-test-foobar5"
|
||||||
|
image_id = "ami-21f78e11"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_sqs_queue" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
delay_seconds = 90
|
||||||
|
max_message_size = 2048
|
||||||
|
message_retention_seconds = 86400
|
||||||
|
receive_wait_time_seconds = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
assume_role_policy = <<EOF
|
||||||
|
{
|
||||||
|
"Version" : "2012-10-17",
|
||||||
|
"Statement": [ {
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {"AWS": "*"},
|
||||||
|
"Action": [ "sts:AssumeRole" ]
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
role = "${aws_iam_role.foobar.id}"
|
||||||
|
policy = <<EOF
|
||||||
|
{
|
||||||
|
"Version" : "2012-10-17",
|
||||||
|
"Statement": [ {
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"sqs:SendMessage",
|
||||||
|
"sqs:GetQueueUrl",
|
||||||
|
"sns:Publish"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"${aws_sqs_queue.foobar.arn}"
|
||||||
|
]
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
resource "aws_autoscaling_group" "foobar" {
|
||||||
|
availability_zones = ["us-west-2a"]
|
||||||
|
name = "terraform-test-foobar5"
|
||||||
|
max_size = 5
|
||||||
|
min_size = 2
|
||||||
|
health_check_grace_period = 300
|
||||||
|
health_check_type = "ELB"
|
||||||
|
force_delete = true
|
||||||
|
termination_policies = ["OldestInstance"]
|
||||||
|
launch_configuration = "${aws_launch_configuration.foobar.name}"
|
||||||
|
tag {
|
||||||
|
key = "Foo"
|
||||||
|
value = "foo-bar"
|
||||||
|
propagate_at_launch = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_autoscaling_lifecycle_hook" "foobar" {
|
||||||
|
name = "foobar"
|
||||||
|
autoscaling_group_name = "${aws_autoscaling_group.foobar.name}"
|
||||||
|
default_result = "CONTINUE"
|
||||||
|
heartbeat_timeout = 2000
|
||||||
|
lifecycle_transition = "autoscaling:EC2_INSTANCE_LAUNCHING"
|
||||||
|
notification_metadata = <<EOF
|
||||||
|
{
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
notification_target_arn = "${aws_sqs_queue.foobar.arn}"
|
||||||
|
role_arn = "${aws_iam_role.foobar.arn}"
|
||||||
|
}
|
||||||
|
`)
|
|
@ -0,0 +1,146 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsCloudWatchLogGroup() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsCloudWatchLogGroupCreate,
|
||||||
|
Read: resourceAwsCloudWatchLogGroupRead,
|
||||||
|
Update: resourceAwsCloudWatchLogGroupUpdate,
|
||||||
|
Delete: resourceAwsCloudWatchLogGroupDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"retention_in_days": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).cloudwatchlogsconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating CloudWatch Log Group: %s", d.Get("name").(string))
|
||||||
|
_, err := conn.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
|
||||||
|
LogGroupName: aws.String(d.Get("name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Creating CloudWatch Log Group failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(d.Get("name").(string))
|
||||||
|
|
||||||
|
log.Println("[INFO] CloudWatch Log Group created")
|
||||||
|
|
||||||
|
return resourceAwsCloudWatchLogGroupUpdate(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsCloudWatchLogGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).cloudwatchlogsconn
|
||||||
|
log.Printf("[DEBUG] Reading CloudWatch Log Group: %q", d.Get("name").(string))
|
||||||
|
lg, err := lookupCloudWatchLogGroup(conn, d.Get("name").(string), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Found Log Group: %#v", *lg)
|
||||||
|
|
||||||
|
d.Set("arn", *lg.Arn)
|
||||||
|
d.Set("name", *lg.LogGroupName)
|
||||||
|
|
||||||
|
if lg.RetentionInDays != nil {
|
||||||
|
d.Set("retention_in_days", *lg.RetentionInDays)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupCloudWatchLogGroup(conn *cloudwatchlogs.CloudWatchLogs,
|
||||||
|
name string, nextToken *string) (*cloudwatchlogs.LogGroup, error) {
|
||||||
|
input := &cloudwatchlogs.DescribeLogGroupsInput{
|
||||||
|
LogGroupNamePrefix: aws.String(name),
|
||||||
|
NextToken: nextToken,
|
||||||
|
}
|
||||||
|
resp, err := conn.DescribeLogGroups(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lg := range resp.LogGroups {
|
||||||
|
if *lg.LogGroupName == name {
|
||||||
|
return lg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.NextToken != nil {
|
||||||
|
return lookupCloudWatchLogGroup(conn, name, resp.NextToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("CloudWatch Log Group %q not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsCloudWatchLogGroupUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).cloudwatchlogsconn
|
||||||
|
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
log.Printf("[DEBUG] Updating CloudWatch Log Group: %q", name)
|
||||||
|
|
||||||
|
if d.HasChange("retention_in_days") {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("retention_in_days"); ok {
|
||||||
|
input := cloudwatchlogs.PutRetentionPolicyInput{
|
||||||
|
LogGroupName: aws.String(name),
|
||||||
|
RetentionInDays: aws.Int64(int64(v.(int))),
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Setting retention for CloudWatch Log Group: %q: %s", name, input)
|
||||||
|
_, err = conn.PutRetentionPolicy(&input)
|
||||||
|
} else {
|
||||||
|
log.Printf("[DEBUG] Deleting retention for CloudWatch Log Group: %q", name)
|
||||||
|
_, err = conn.DeleteRetentionPolicy(&cloudwatchlogs.DeleteRetentionPolicyInput{
|
||||||
|
LogGroupName: aws.String(name),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsCloudWatchLogGroupRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsCloudWatchLogGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).cloudwatchlogsconn
|
||||||
|
log.Printf("[INFO] Deleting CloudWatch Log Group: %s", d.Id())
|
||||||
|
_, err := conn.DeleteLogGroup(&cloudwatchlogs.DeleteLogGroupInput{
|
||||||
|
LogGroupName: aws.String(d.Get("name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error deleting CloudWatch Log Group: %s", err)
|
||||||
|
}
|
||||||
|
log.Println("[INFO] CloudWatch Log Group deleted")
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSCloudWatchLogGroup_basic(t *testing.T) {
|
||||||
|
var lg cloudwatchlogs.LogGroup
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSCloudWatchLogGroupConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "0"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSCloudWatchLogGroup_retentionPolicy(t *testing.T) {
|
||||||
|
var lg cloudwatchlogs.LogGroup
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSCloudWatchLogGroupConfig_withRetention,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "365"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSCloudWatchLogGroupConfigModified_withRetention,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "0"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSCloudWatchLogGroup_multiple(t *testing.T) {
|
||||||
|
var lg cloudwatchlogs.LogGroup
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSCloudWatchLogGroupConfig_multiple,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.alpha", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.alpha", "retention_in_days", "14"),
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.beta", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.beta", "retention_in_days", "0"),
|
||||||
|
testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.charlie", &lg),
|
||||||
|
resource.TestCheckResourceAttr("aws_cloudwatch_log_group.charlie", "retention_in_days", "3653"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckCloudWatchLogGroupExists(n string, lg *cloudwatchlogs.LogGroup) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
|
||||||
|
logGroup, err := lookupCloudWatchLogGroup(conn, rs.Primary.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*lg = *logGroup
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSCloudWatchLogGroupDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_cloudwatch_log_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := lookupCloudWatchLogGroup(conn, rs.Primary.ID, nil)
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("LogGroup Still Exists: %s", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAWSCloudWatchLogGroupConfig = `
|
||||||
|
resource "aws_cloudwatch_log_group" "foobar" {
|
||||||
|
name = "foo-bar"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAWSCloudWatchLogGroupConfig_withRetention = `
|
||||||
|
resource "aws_cloudwatch_log_group" "foobar" {
|
||||||
|
name = "foo-bang"
|
||||||
|
retention_in_days = 365
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAWSCloudWatchLogGroupConfigModified_withRetention = `
|
||||||
|
resource "aws_cloudwatch_log_group" "foobar" {
|
||||||
|
name = "foo-bang"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAWSCloudWatchLogGroupConfig_multiple = `
|
||||||
|
resource "aws_cloudwatch_log_group" "alpha" {
|
||||||
|
name = "foo-bar"
|
||||||
|
retention_in_days = 14
|
||||||
|
}
|
||||||
|
resource "aws_cloudwatch_log_group" "beta" {
|
||||||
|
name = "foo-bara"
|
||||||
|
}
|
||||||
|
resource "aws_cloudwatch_log_group" "charlie" {
|
||||||
|
name = "foo-baraa"
|
||||||
|
retention_in_days = 3653
|
||||||
|
}
|
||||||
|
`
|
|
@ -75,29 +75,10 @@ func resourceAwsDbInstance() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
"identifier": &schema.Schema{
|
"identifier": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
ValidateFunc: validateRdsId,
|
||||||
value := v.(string)
|
|
||||||
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
|
|
||||||
}
|
|
||||||
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"first character of %q must be a letter", k))
|
|
||||||
}
|
|
||||||
if regexp.MustCompile(`--`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"%q cannot contain two consecutive hyphens", k))
|
|
||||||
}
|
|
||||||
if regexp.MustCompile(`-$`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"%q cannot end with a hyphen", k))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"instance_class": &schema.Schema{
|
"instance_class": &schema.Schema{
|
||||||
|
@ -524,7 +505,6 @@ func resourceAwsDbInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
if v.DBName != nil && *v.DBName != "" {
|
if v.DBName != nil && *v.DBName != "" {
|
||||||
name = *v.DBName
|
name = *v.DBName
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name)
|
log.Printf("[DEBUG] Error building ARN for DB Instance, not setting Tags for DB %s", name)
|
||||||
} else {
|
} else {
|
||||||
resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{
|
resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -24,9 +25,10 @@ func resourceAwsDbParameterGroup() *schema.Resource {
|
||||||
Delete: resourceAwsDbParameterGroupDelete,
|
Delete: resourceAwsDbParameterGroupDelete,
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
ValidateFunc: validateDbParamGroupName,
|
||||||
},
|
},
|
||||||
"family": &schema.Schema{
|
"family": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -227,3 +229,29 @@ func resourceAwsDbParameterHash(v interface{}) int {
|
||||||
|
|
||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"first character of %q must be a letter", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`--`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot contain two consecutive hyphens", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`-$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot end with a hyphen", k))
|
||||||
|
}
|
||||||
|
if len(value) > 255 {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot be greater than 255 characters", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
@ -106,6 +108,46 @@ func TestAccAWSDBParameterGroupOnly(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceAWSDBParameterGroupName_validation(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Value string
|
||||||
|
ErrCount int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Value: "tEsting123",
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: "testing123!",
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: "1testing123",
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: "testing--123",
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: "testing123-",
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value: randomString(256),
|
||||||
|
ErrCount: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
_, errors := validateDbParamGroupName(tc.Value, "aws_db_parameter_group_name")
|
||||||
|
|
||||||
|
if len(errors) != tc.ErrCount {
|
||||||
|
t.Fatalf("Expected the DB Parameter Group Name to trigger a validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckAWSDBParameterGroupDestroy(s *terraform.State) error {
|
func testAccCheckAWSDBParameterGroupDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).rdsconn
|
conn := testAccProvider.Meta().(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
@ -193,6 +235,16 @@ func testAccCheckAWSDBParameterGroupExists(n string, v *rds.DBParameterGroup) re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randomString(strlen int) string {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
const chars = "abcdefghijklmnopqrstuvwxyz"
|
||||||
|
result := make([]byte, strlen)
|
||||||
|
for i := 0; i < strlen; i++ {
|
||||||
|
result[i] = chars[rand.Intn(len(chars))]
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
const testAccAWSDBParameterGroupConfig = `
|
const testAccAWSDBParameterGroupConfig = `
|
||||||
resource "aws_db_parameter_group" "bar" {
|
resource "aws_db_parameter_group" "bar" {
|
||||||
name = "parameter-group-test-terraform"
|
name = "parameter-group-test-terraform"
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/service/rds"
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/multierror"
|
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/directoryservice"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsDirectoryServiceDirectory() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsDirectoryServiceDirectoryCreate,
|
||||||
|
Read: resourceAwsDirectoryServiceDirectoryRead,
|
||||||
|
Update: resourceAwsDirectoryServiceDirectoryUpdate,
|
||||||
|
Delete: resourceAwsDirectoryServiceDirectoryDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"password": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"size": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"alias": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"short_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"vpc_settings": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Required: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"subnet_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
"vpc_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"enable_sso": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
"access_url": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"dns_ip_addresses": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsDirectoryServiceDirectoryCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
dsconn := meta.(*AWSClient).dsconn
|
||||||
|
|
||||||
|
input := directoryservice.CreateDirectoryInput{
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
Password: aws.String(d.Get("password").(string)),
|
||||||
|
Size: aws.String(d.Get("size").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("description"); ok {
|
||||||
|
input.Description = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("short_name"); ok {
|
||||||
|
input.ShortName = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("vpc_settings"); ok {
|
||||||
|
settings := v.([]interface{})
|
||||||
|
|
||||||
|
if len(settings) > 1 {
|
||||||
|
return fmt.Errorf("Only a single vpc_settings block is expected")
|
||||||
|
} else if len(settings) == 1 {
|
||||||
|
s := settings[0].(map[string]interface{})
|
||||||
|
var subnetIds []*string
|
||||||
|
for _, id := range s["subnet_ids"].(*schema.Set).List() {
|
||||||
|
subnetIds = append(subnetIds, aws.String(id.(string)))
|
||||||
|
}
|
||||||
|
|
||||||
|
vpcSettings := directoryservice.DirectoryVpcSettings{
|
||||||
|
SubnetIds: subnetIds,
|
||||||
|
VpcId: aws.String(s["vpc_id"].(string)),
|
||||||
|
}
|
||||||
|
input.VpcSettings = &vpcSettings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating Directory Service: %s", input)
|
||||||
|
out, err := dsconn.CreateDirectory(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Directory Service created: %s", out)
|
||||||
|
d.SetId(*out.DirectoryId)
|
||||||
|
|
||||||
|
// Wait for creation
|
||||||
|
log.Printf("[DEBUG] Waiting for DS (%q) to become available", d.Id())
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"Requested", "Creating", "Created"},
|
||||||
|
Target: "Active",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(d.Id())},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error during creation of DS: %q", err.Error())
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := resp.DirectoryDescriptions[0]
|
||||||
|
log.Printf("[DEBUG] Creation of DS %q is in following stage: %q.",
|
||||||
|
d.Id(), *ds.Stage)
|
||||||
|
return ds, *ds.Stage, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for Directory Service (%s) to become available: %#v",
|
||||||
|
d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("alias"); ok {
|
||||||
|
d.SetPartial("alias")
|
||||||
|
|
||||||
|
input := directoryservice.CreateAliasInput{
|
||||||
|
DirectoryId: aws.String(d.Id()),
|
||||||
|
Alias: aws.String(v.(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Assigning alias %q to DS directory %q",
|
||||||
|
v.(string), d.Id())
|
||||||
|
out, err := dsconn.CreateAlias(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Alias %q assigned to DS directory %q",
|
||||||
|
*out.Alias, *out.DirectoryId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsDirectoryServiceDirectoryUpdate(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsDirectoryServiceDirectoryUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
dsconn := meta.(*AWSClient).dsconn
|
||||||
|
|
||||||
|
if d.HasChange("enable_sso") {
|
||||||
|
d.SetPartial("enable_sso")
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("enable_sso"); ok && v.(bool) {
|
||||||
|
log.Printf("[DEBUG] Enabling SSO for DS directory %q", d.Id())
|
||||||
|
_, err = dsconn.EnableSso(&directoryservice.EnableSsoInput{
|
||||||
|
DirectoryId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Printf("[DEBUG] Disabling SSO for DS directory %q", d.Id())
|
||||||
|
_, err = dsconn.DisableSso(&directoryservice.DisableSsoInput{
|
||||||
|
DirectoryId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsDirectoryServiceDirectoryRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsDirectoryServiceDirectoryRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
dsconn := meta.(*AWSClient).dsconn
|
||||||
|
|
||||||
|
input := directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(d.Id())},
|
||||||
|
}
|
||||||
|
out, err := dsconn.DescribeDirectories(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := out.DirectoryDescriptions[0]
|
||||||
|
log.Printf("[DEBUG] Received DS directory: %s", *dir)
|
||||||
|
|
||||||
|
d.Set("access_url", *dir.AccessUrl)
|
||||||
|
d.Set("alias", *dir.Alias)
|
||||||
|
if dir.Description != nil {
|
||||||
|
d.Set("description", *dir.Description)
|
||||||
|
}
|
||||||
|
d.Set("dns_ip_addresses", schema.NewSet(schema.HashString, flattenStringList(dir.DnsIpAddrs)))
|
||||||
|
d.Set("name", *dir.Name)
|
||||||
|
if dir.ShortName != nil {
|
||||||
|
d.Set("short_name", *dir.ShortName)
|
||||||
|
}
|
||||||
|
d.Set("size", *dir.Size)
|
||||||
|
d.Set("type", *dir.Type)
|
||||||
|
d.Set("vpc_settings", flattenDSVpcSettings(dir.VpcSettings))
|
||||||
|
d.Set("enable_sso", *dir.SsoEnabled)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsDirectoryServiceDirectoryDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
dsconn := meta.(*AWSClient).dsconn
|
||||||
|
|
||||||
|
input := directoryservice.DeleteDirectoryInput{
|
||||||
|
DirectoryId: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
_, err := dsconn.DeleteDirectory(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for deletion
|
||||||
|
log.Printf("[DEBUG] Waiting for DS (%q) to be deleted", d.Id())
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"Deleting"},
|
||||||
|
Target: "",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(d.Id())},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.DirectoryDescriptions) == 0 {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := resp.DirectoryDescriptions[0]
|
||||||
|
log.Printf("[DEBUG] Deletion of DS %q is in following stage: %q.",
|
||||||
|
d.Id(), *ds.Stage)
|
||||||
|
return ds, *ds.Stage, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Error waiting for Directory Service (%s) to be deleted: %q",
|
||||||
|
d.Id(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,283 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/directoryservice"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSDirectoryServiceDirectory_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckDirectoryServiceDirectoryDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDirectoryServiceDirectoryConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckServiceDirectoryExists("aws_directory_service_directory.bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSDirectoryServiceDirectory_withAliasAndSso(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckDirectoryServiceDirectoryDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDirectoryServiceDirectoryConfig_withAlias,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckServiceDirectoryExists("aws_directory_service_directory.bar_a"),
|
||||||
|
testAccCheckServiceDirectoryAlias("aws_directory_service_directory.bar_a",
|
||||||
|
fmt.Sprintf("tf-d-%d", randomInteger)),
|
||||||
|
testAccCheckServiceDirectorySso("aws_directory_service_directory.bar_a", false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDirectoryServiceDirectoryConfig_withSso,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckServiceDirectoryExists("aws_directory_service_directory.bar_a"),
|
||||||
|
testAccCheckServiceDirectoryAlias("aws_directory_service_directory.bar_a",
|
||||||
|
fmt.Sprintf("tf-d-%d", randomInteger)),
|
||||||
|
testAccCheckServiceDirectorySso("aws_directory_service_directory.bar_a", true),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccDirectoryServiceDirectoryConfig_withSso_modified,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckServiceDirectoryExists("aws_directory_service_directory.bar_a"),
|
||||||
|
testAccCheckServiceDirectoryAlias("aws_directory_service_directory.bar_a",
|
||||||
|
fmt.Sprintf("tf-d-%d", randomInteger)),
|
||||||
|
testAccCheckServiceDirectorySso("aws_directory_service_directory.bar_a", false),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckDirectoryServiceDirectoryDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v",
|
||||||
|
s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckServiceDirectoryExists(name string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
dsconn := testAccProvider.Meta().(*AWSClient).dsconn
|
||||||
|
out, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.DirectoryDescriptions) < 1 {
|
||||||
|
return fmt.Errorf("No DS directory found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if *out.DirectoryDescriptions[0].DirectoryId != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("DS directory ID mismatch - existing: %q, state: %q",
|
||||||
|
*out.DirectoryDescriptions[0].DirectoryId, rs.Primary.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckServiceDirectoryAlias(name, alias string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
dsconn := testAccProvider.Meta().(*AWSClient).dsconn
|
||||||
|
out, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *out.DirectoryDescriptions[0].Alias != alias {
|
||||||
|
return fmt.Errorf("DS directory Alias mismatch - actual: %q, expected: %q",
|
||||||
|
*out.DirectoryDescriptions[0].Alias, alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckServiceDirectorySso(name string, ssoEnabled bool) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
dsconn := testAccProvider.Meta().(*AWSClient).dsconn
|
||||||
|
out, err := dsconn.DescribeDirectories(&directoryservice.DescribeDirectoriesInput{
|
||||||
|
DirectoryIds: []*string{aws.String(rs.Primary.ID)},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *out.DirectoryDescriptions[0].SsoEnabled != ssoEnabled {
|
||||||
|
return fmt.Errorf("DS directory SSO mismatch - actual: %t, expected: %t",
|
||||||
|
*out.DirectoryDescriptions[0].SsoEnabled, ssoEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccDirectoryServiceDirectoryConfig = `
|
||||||
|
resource "aws_directory_service_directory" "bar" {
|
||||||
|
name = "corp.notexample.com"
|
||||||
|
password = "SuperSecretPassw0rd"
|
||||||
|
size = "Small"
|
||||||
|
|
||||||
|
vpc_settings {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}", "${aws_subnet.bar.id}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2b"
|
||||||
|
cidr_block = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var randomInteger = genRandInt()
|
||||||
|
var testAccDirectoryServiceDirectoryConfig_withAlias = fmt.Sprintf(`
|
||||||
|
resource "aws_directory_service_directory" "bar_a" {
|
||||||
|
name = "corp.notexample.com"
|
||||||
|
password = "SuperSecretPassw0rd"
|
||||||
|
size = "Small"
|
||||||
|
alias = "tf-d-%d"
|
||||||
|
|
||||||
|
vpc_settings {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}", "${aws_subnet.bar.id}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2b"
|
||||||
|
cidr_block = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
`, randomInteger)
|
||||||
|
|
||||||
|
var testAccDirectoryServiceDirectoryConfig_withSso = fmt.Sprintf(`
|
||||||
|
resource "aws_directory_service_directory" "bar_a" {
|
||||||
|
name = "corp.notexample.com"
|
||||||
|
password = "SuperSecretPassw0rd"
|
||||||
|
size = "Small"
|
||||||
|
alias = "tf-d-%d"
|
||||||
|
enable_sso = true
|
||||||
|
|
||||||
|
vpc_settings {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}", "${aws_subnet.bar.id}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2b"
|
||||||
|
cidr_block = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
`, randomInteger)
|
||||||
|
|
||||||
|
var testAccDirectoryServiceDirectoryConfig_withSso_modified = fmt.Sprintf(`
|
||||||
|
resource "aws_directory_service_directory" "bar_a" {
|
||||||
|
name = "corp.notexample.com"
|
||||||
|
password = "SuperSecretPassw0rd"
|
||||||
|
size = "Small"
|
||||||
|
alias = "tf-d-%d"
|
||||||
|
enable_sso = false
|
||||||
|
|
||||||
|
vpc_settings {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}", "${aws_subnet.bar.id}"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "main" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.main.id}"
|
||||||
|
availability_zone = "us-west-2b"
|
||||||
|
cidr_block = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
`, randomInteger)
|
|
@ -384,7 +384,7 @@ func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
updates = append(updates, update)
|
updates = append(updates, update)
|
||||||
|
|
||||||
// Hash key is required, range key isn't
|
// Hash key is required, range key isn't
|
||||||
hashkey_type, err := getAttributeType(d, *(gsi.KeySchema[0].AttributeName))
|
hashkey_type, err := getAttributeType(d, *gsi.KeySchema[0].AttributeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -396,7 +396,7 @@ func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
// If there's a range key, there will be 2 elements in KeySchema
|
// If there's a range key, there will be 2 elements in KeySchema
|
||||||
if len(gsi.KeySchema) == 2 {
|
if len(gsi.KeySchema) == 2 {
|
||||||
rangekey_type, err := getAttributeType(d, *(gsi.KeySchema[1].AttributeName))
|
rangekey_type, err := getAttributeType(d, *gsi.KeySchema[1].AttributeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -480,8 +480,8 @@ func resourceAwsDynamoDbTableUpdate(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
capacityUpdated := false
|
capacityUpdated := false
|
||||||
|
|
||||||
if int64(gsiReadCapacity) != *(gsi.ProvisionedThroughput.ReadCapacityUnits) ||
|
if int64(gsiReadCapacity) != *gsi.ProvisionedThroughput.ReadCapacityUnits ||
|
||||||
int64(gsiWriteCapacity) != *(gsi.ProvisionedThroughput.WriteCapacityUnits) {
|
int64(gsiWriteCapacity) != *gsi.ProvisionedThroughput.WriteCapacityUnits {
|
||||||
capacityUpdated = true
|
capacityUpdated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,8 +544,8 @@ func resourceAwsDynamoDbTableRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
attributes := []interface{}{}
|
attributes := []interface{}{}
|
||||||
for _, attrdef := range table.AttributeDefinitions {
|
for _, attrdef := range table.AttributeDefinitions {
|
||||||
attribute := map[string]string{
|
attribute := map[string]string{
|
||||||
"name": *(attrdef.AttributeName),
|
"name": *attrdef.AttributeName,
|
||||||
"type": *(attrdef.AttributeType),
|
"type": *attrdef.AttributeType,
|
||||||
}
|
}
|
||||||
attributes = append(attributes, attribute)
|
attributes = append(attributes, attribute)
|
||||||
log.Printf("[DEBUG] Added Attribute: %s", attribute["name"])
|
log.Printf("[DEBUG] Added Attribute: %s", attribute["name"])
|
||||||
|
@ -556,9 +556,9 @@ func resourceAwsDynamoDbTableRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
gsiList := make([]map[string]interface{}, 0, len(table.GlobalSecondaryIndexes))
|
gsiList := make([]map[string]interface{}, 0, len(table.GlobalSecondaryIndexes))
|
||||||
for _, gsiObject := range table.GlobalSecondaryIndexes {
|
for _, gsiObject := range table.GlobalSecondaryIndexes {
|
||||||
gsi := map[string]interface{}{
|
gsi := map[string]interface{}{
|
||||||
"write_capacity": *(gsiObject.ProvisionedThroughput.WriteCapacityUnits),
|
"write_capacity": *gsiObject.ProvisionedThroughput.WriteCapacityUnits,
|
||||||
"read_capacity": *(gsiObject.ProvisionedThroughput.ReadCapacityUnits),
|
"read_capacity": *gsiObject.ProvisionedThroughput.ReadCapacityUnits,
|
||||||
"name": *(gsiObject.IndexName),
|
"name": *gsiObject.IndexName,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, attribute := range gsiObject.KeySchema {
|
for _, attribute := range gsiObject.KeySchema {
|
||||||
|
@ -571,7 +571,7 @@ func resourceAwsDynamoDbTableRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsi["projection_type"] = *(gsiObject.Projection.ProjectionType)
|
gsi["projection_type"] = *gsiObject.Projection.ProjectionType
|
||||||
gsi["non_key_attributes"] = gsiObject.Projection.NonKeyAttributes
|
gsi["non_key_attributes"] = gsiObject.Projection.NonKeyAttributes
|
||||||
|
|
||||||
gsiList = append(gsiList, gsi)
|
gsiList = append(gsiList, gsi)
|
||||||
|
@ -647,7 +647,7 @@ func createGSIFromData(data *map[string]interface{}) dynamodb.GlobalSecondaryInd
|
||||||
|
|
||||||
func getGlobalSecondaryIndex(indexName string, indexList []*dynamodb.GlobalSecondaryIndexDescription) (*dynamodb.GlobalSecondaryIndexDescription, error) {
|
func getGlobalSecondaryIndex(indexName string, indexList []*dynamodb.GlobalSecondaryIndexDescription) (*dynamodb.GlobalSecondaryIndexDescription, error) {
|
||||||
for _, gsi := range indexList {
|
for _, gsi := range indexList {
|
||||||
if *(gsi.IndexName) == indexName {
|
if *gsi.IndexName == indexName {
|
||||||
return gsi, nil
|
return gsi, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -726,7 +726,7 @@ func waitForTableToBeActive(tableName string, meta interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
activeState = *(result.Table.TableStatus) == "ACTIVE"
|
activeState = *result.Table.TableStatus == "ACTIVE"
|
||||||
|
|
||||||
// Wait for a few seconds
|
// Wait for a few seconds
|
||||||
if !activeState {
|
if !activeState {
|
||||||
|
|
|
@ -211,7 +211,7 @@ func dynamoDbAttributesToMap(attributes *[]*dynamodb.AttributeDefinition) map[st
|
||||||
attrmap := make(map[string]string)
|
attrmap := make(map[string]string)
|
||||||
|
|
||||||
for _, attrdef := range *attributes {
|
for _, attrdef := range *attributes {
|
||||||
attrmap[*(attrdef.AttributeName)] = *(attrdef.AttributeType)
|
attrmap[*attrdef.AttributeName] = *attrdef.AttributeType
|
||||||
}
|
}
|
||||||
|
|
||||||
return attrmap
|
return attrmap
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsEfsFileSystem() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsEfsFileSystemCreate,
|
||||||
|
Read: resourceAwsEfsFileSystemRead,
|
||||||
|
Update: resourceAwsEfsFileSystemUpdate,
|
||||||
|
Delete: resourceAwsEfsFileSystemDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"reference_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"tags": tagsSchema(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsFileSystemCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
referenceName := ""
|
||||||
|
if v, ok := d.GetOk("reference_name"); ok {
|
||||||
|
referenceName = v.(string) + "-"
|
||||||
|
}
|
||||||
|
token := referenceName + resource.UniqueId()
|
||||||
|
fs, err := conn.CreateFileSystem(&efs.CreateFileSystemInput{
|
||||||
|
CreationToken: aws.String(token),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating EFS file system: %s", *fs)
|
||||||
|
d.SetId(*fs.FileSystemId)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"creating"},
|
||||||
|
Target: "available",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "error", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.FileSystems) < 1 {
|
||||||
|
return nil, "not-found", fmt.Errorf("EFS file system %q not found", d.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := resp.FileSystems[0]
|
||||||
|
log.Printf("[DEBUG] current status of %q: %q", *fs.FileSystemId, *fs.LifeCycleState)
|
||||||
|
return fs, *fs.LifeCycleState, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 2 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for EFS file system (%q) to create: %q",
|
||||||
|
d.Id(), err.Error())
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] EFS file system created: %q", *fs.FileSystemId)
|
||||||
|
|
||||||
|
return resourceAwsEfsFileSystemUpdate(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsFileSystemUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
err := setTagsEFS(conn, d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsEfsFileSystemRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsFileSystemRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.FileSystems) < 1 {
|
||||||
|
return fmt.Errorf("EFS file system %q not found", d.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
tagsResp, err := conn.DescribeTags(&efs.DescribeTagsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("tags", tagsToMapEFS(tagsResp.Tags))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting EFS file system %s", d.Id())
|
||||||
|
_, err := conn.DeleteFileSystem(&efs.DeleteFileSystemInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"available", "deleting"},
|
||||||
|
Target: "",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
efsErr, ok := err.(awserr.Error)
|
||||||
|
if ok && efsErr.Code() == "FileSystemNotFound" {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
return nil, "error", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.FileSystems) < 1 {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := resp.FileSystems[0]
|
||||||
|
log.Printf("[DEBUG] current status of %q: %q",
|
||||||
|
*fs.FileSystemId, *fs.LifeCycleState)
|
||||||
|
return fs, *fs.LifeCycleState, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 2 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] EFS file system %q deleted.", d.Id())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSEFSFileSystem(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckEfsFileSystemDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEFSFileSystemConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckEfsFileSystem(
|
||||||
|
"aws_efs_file_system.foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEFSFileSystemConfigWithTags,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckEfsFileSystem(
|
||||||
|
"aws_efs_file_system.foo-with-tags",
|
||||||
|
),
|
||||||
|
testAccCheckEfsFileSystemTags(
|
||||||
|
"aws_efs_file_system.foo-with-tags",
|
||||||
|
map[string]string{
|
||||||
|
"Name": "foo-efs",
|
||||||
|
"Another": "tag",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckEfsFileSystemDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckEfsFileSystem(resourceID string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
fs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).efsconn
|
||||||
|
_, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
|
||||||
|
FileSystemId: aws.String(fs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckEfsFileSystemTags(resourceID string, expectedTags map[string]string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
fs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).efsconn
|
||||||
|
resp, err := conn.DescribeTags(&efs.DescribeTagsInput{
|
||||||
|
FileSystemId: aws.String(fs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expectedTags, tagsToMapEFS(resp.Tags)) {
|
||||||
|
return fmt.Errorf("Tags mismatch.\nExpected: %#v\nGiven: %#v",
|
||||||
|
expectedTags, resp.Tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccAWSEFSFileSystemConfig = `
|
||||||
|
resource "aws_efs_file_system" "foo" {
|
||||||
|
reference_name = "radeksimko"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccAWSEFSFileSystemConfigWithTags = `
|
||||||
|
resource "aws_efs_file_system" "foo-with-tags" {
|
||||||
|
reference_name = "yada_yada"
|
||||||
|
tags {
|
||||||
|
Name = "foo-efs"
|
||||||
|
Another = "tag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,223 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsEfsMountTarget() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsEfsMountTargetCreate,
|
||||||
|
Read: resourceAwsEfsMountTargetRead,
|
||||||
|
Update: resourceAwsEfsMountTargetUpdate,
|
||||||
|
Delete: resourceAwsEfsMountTargetDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"file_system_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ip_address": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"security_groups": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
Computed: true,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"subnet_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"network_interface_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsMountTargetCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
input := efs.CreateMountTargetInput{
|
||||||
|
FileSystemId: aws.String(d.Get("file_system_id").(string)),
|
||||||
|
SubnetId: aws.String(d.Get("subnet_id").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ip_address"); ok {
|
||||||
|
input.IpAddress = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("security_groups"); ok {
|
||||||
|
input.SecurityGroups = expandStringList(v.(*schema.Set).List())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating EFS mount target: %#v", input)
|
||||||
|
|
||||||
|
mt, err := conn.CreateMountTarget(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*mt.MountTargetId)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"creating"},
|
||||||
|
Target: "available",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "error", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.MountTargets) < 1 {
|
||||||
|
return nil, "error", fmt.Errorf("EFS mount target %q not found", d.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := resp.MountTargets[0]
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Current status of %q: %q", *mt.MountTargetId, *mt.LifeCycleState)
|
||||||
|
return mt, *mt.LifeCycleState, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 2 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for EFS mount target (%s) to create: %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] EFS mount target created: %s", *mt.MountTargetId)
|
||||||
|
|
||||||
|
return resourceAwsEfsMountTargetRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsMountTargetUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
if d.HasChange("security_groups") {
|
||||||
|
input := efs.ModifyMountTargetSecurityGroupsInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
SecurityGroups: expandStringList(d.Get("security_groups").(*schema.Set).List()),
|
||||||
|
}
|
||||||
|
_, err := conn.ModifyMountTargetSecurityGroups(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsEfsMountTargetRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsMountTargetRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.MountTargets) < 1 {
|
||||||
|
return fmt.Errorf("EFS mount target %q not found", d.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := resp.MountTargets[0]
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Found EFS mount target: %#v", mt)
|
||||||
|
|
||||||
|
d.SetId(*mt.MountTargetId)
|
||||||
|
d.Set("file_system_id", *mt.FileSystemId)
|
||||||
|
d.Set("ip_address", *mt.IpAddress)
|
||||||
|
d.Set("subnet_id", *mt.SubnetId)
|
||||||
|
d.Set("network_interface_id", *mt.NetworkInterfaceId)
|
||||||
|
|
||||||
|
sgResp, err := conn.DescribeMountTargetSecurityGroups(&efs.DescribeMountTargetSecurityGroupsInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("security_groups", schema.NewSet(schema.HashString, flattenStringList(sgResp.SecurityGroups)))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsEfsMountTargetDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).efsconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting EFS mount target %q", d.Id())
|
||||||
|
_, err := conn.DeleteMountTarget(&efs.DeleteMountTargetInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"available", "deleting", "deleted"},
|
||||||
|
Target: "",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{
|
||||||
|
MountTargetId: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
awsErr, ok := err.(awserr.Error)
|
||||||
|
if !ok {
|
||||||
|
return nil, "error", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if awsErr.Code() == "MountTargetNotFound" {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "error", awsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.MountTargets) < 1 {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := resp.MountTargets[0]
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Current status of %q: %q", *mt.MountTargetId, *mt.LifeCycleState)
|
||||||
|
return mt, *mt.LifeCycleState, nil
|
||||||
|
},
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 2 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for EFS mount target (%q) to delete: %q",
|
||||||
|
d.Id(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] EFS mount target %q deleted.", d.Id())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSEFSMountTarget(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckEfsMountTargetDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEFSMountTargetConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckEfsMountTarget(
|
||||||
|
"aws_efs_mount_target.alpha",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEFSMountTargetConfigModified,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckEfsMountTarget(
|
||||||
|
"aws_efs_mount_target.alpha",
|
||||||
|
),
|
||||||
|
testAccCheckEfsMountTarget(
|
||||||
|
"aws_efs_mount_target.beta",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckEfsMountTargetDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckEfsMountTarget(resourceID string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
fs, ok := s.RootModule().Resources[resourceID]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).efsconn
|
||||||
|
mt, err := conn.DescribeMountTargets(&efs.DescribeMountTargetsInput{
|
||||||
|
MountTargetId: aws.String(fs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *mt.MountTargets[0].MountTargetId != fs.Primary.ID {
|
||||||
|
return fmt.Errorf("Mount target ID mismatch: %q != %q",
|
||||||
|
*mt.MountTargets[0].MountTargetId, fs.Primary.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccAWSEFSMountTargetConfig = `
|
||||||
|
resource "aws_efs_file_system" "foo" {
|
||||||
|
reference_name = "radeksimko"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_efs_mount_target" "alpha" {
|
||||||
|
file_system_id = "${aws_efs_file_system.foo.id}"
|
||||||
|
subnet_id = "${aws_subnet.alpha.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "alpha" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccAWSEFSMountTargetConfigModified = `
|
||||||
|
resource "aws_efs_file_system" "foo" {
|
||||||
|
reference_name = "radeksimko"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_efs_mount_target" "alpha" {
|
||||||
|
file_system_id = "${aws_efs_file_system.foo.id}"
|
||||||
|
subnet_id = "${aws_subnet.alpha.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_efs_mount_target" "beta" {
|
||||||
|
file_system_id = "${aws_efs_file_system.foo.id}"
|
||||||
|
subnet_id = "${aws_subnet.beta.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "10.0.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "alpha" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
cidr_block = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "beta" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
availability_zone = "us-west-2b"
|
||||||
|
cidr_block = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
`
|
|
@ -30,13 +30,13 @@ func resourceAwsEip() *schema.Resource {
|
||||||
"instance": &schema.Schema{
|
"instance": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"network_interface": &schema.Schema{
|
"network_interface": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ConflictsWith: []string{"instance"},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"allocation_id": &schema.Schema{
|
"allocation_id": &schema.Schema{
|
||||||
|
@ -134,7 +134,7 @@ func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
// Verify AWS returned our EIP
|
// Verify AWS returned our EIP
|
||||||
if len(describeAddresses.Addresses) != 1 ||
|
if len(describeAddresses.Addresses) != 1 ||
|
||||||
(domain == "vpc" && *describeAddresses.Addresses[0].AllocationId != id) ||
|
domain == "vpc" && *describeAddresses.Addresses[0].AllocationId != id ||
|
||||||
*describeAddresses.Addresses[0].PublicIp != id {
|
*describeAddresses.Addresses[0].PublicIp != id {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
|
return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses)
|
||||||
|
|
|
@ -28,6 +28,16 @@ func resourceAwsElasticacheCluster() *schema.Resource {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
StateFunc: func(val interface{}) string {
|
||||||
|
// Elasticache normalizes cluster ids to lowercase,
|
||||||
|
// so we have to do this too or else we can end up
|
||||||
|
// with non-converging diffs.
|
||||||
|
return strings.ToLower(val.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"configuration_endpoint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
},
|
},
|
||||||
"engine": &schema.Schema{
|
"engine": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
|
@ -190,7 +200,11 @@ func resourceAwsElasticacheClusterCreate(d *schema.ResourceData, meta interface{
|
||||||
return fmt.Errorf("Error creating Elasticache: %s", err)
|
return fmt.Errorf("Error creating Elasticache: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetId(*resp.CacheCluster.CacheClusterId)
|
// Assign the cluster id as the resource ID
|
||||||
|
// Elasticache always retains the id in lower case, so we have to
|
||||||
|
// mimic that or else we won't be able to refresh a resource whose
|
||||||
|
// name contained uppercase characters.
|
||||||
|
d.SetId(strings.ToLower(*resp.CacheCluster.CacheClusterId))
|
||||||
|
|
||||||
pending := []string{"creating"}
|
pending := []string{"creating"}
|
||||||
stateConf := &resource.StateChangeConf{
|
stateConf := &resource.StateChangeConf{
|
||||||
|
@ -232,7 +246,9 @@ func resourceAwsElasticacheClusterRead(d *schema.ResourceData, meta interface{})
|
||||||
d.Set("engine_version", c.EngineVersion)
|
d.Set("engine_version", c.EngineVersion)
|
||||||
if c.ConfigurationEndpoint != nil {
|
if c.ConfigurationEndpoint != nil {
|
||||||
d.Set("port", c.ConfigurationEndpoint.Port)
|
d.Set("port", c.ConfigurationEndpoint.Port)
|
||||||
|
d.Set("configuration_endpoint", aws.String(fmt.Sprintf("%s:%d", *c.ConfigurationEndpoint.Address, *c.ConfigurationEndpoint.Port)))
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("subnet_group_name", c.CacheSubnetGroupName)
|
d.Set("subnet_group_name", c.CacheSubnetGroupName)
|
||||||
d.Set("security_group_names", c.CacheSecurityGroups)
|
d.Set("security_group_names", c.CacheSecurityGroups)
|
||||||
d.Set("security_group_ids", c.SecurityGroups)
|
d.Set("security_group_ids", c.SecurityGroups)
|
||||||
|
|
|
@ -163,7 +163,10 @@ resource "aws_security_group" "bar" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_elasticache_cluster" "bar" {
|
resource "aws_elasticache_cluster" "bar" {
|
||||||
cluster_id = "tf-test-%03d"
|
// Including uppercase letters in this name to ensure
|
||||||
|
// that we correctly handle the fact that the API
|
||||||
|
// normalizes names to lowercase.
|
||||||
|
cluster_id = "tf-TEST-%03d"
|
||||||
node_type = "cache.m1.small"
|
node_type = "cache.m1.small"
|
||||||
num_cache_nodes = 1
|
num_cache_nodes = 1
|
||||||
engine = "redis"
|
engine = "redis"
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsElasticSearchDomain() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsElasticSearchDomainCreate,
|
||||||
|
Read: resourceAwsElasticSearchDomainRead,
|
||||||
|
Update: resourceAwsElasticSearchDomainUpdate,
|
||||||
|
Delete: resourceAwsElasticSearchDomainDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"access_policies": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
StateFunc: normalizeJson,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"advanced_options": &schema.Schema{
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"domain_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9A-Za-z]+`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q must start with a letter or number", k))
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`^[0-9A-Za-z][0-9a-z-]+$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q can only contain lowercase characters, numbers and hyphens", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"domain_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"endpoint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"ebs_options": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"ebs_enabled": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"iops": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"volume_size": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"volume_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"cluster_config": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"dedicated_master_count": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"dedicated_master_enabled": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
"dedicated_master_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"instance_count": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 1,
|
||||||
|
},
|
||||||
|
"instance_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "m3.medium.elasticsearch",
|
||||||
|
},
|
||||||
|
"zone_awareness_enabled": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"snapshot_options": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"automated_snapshot_start_hour": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticSearchDomainCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).esconn
|
||||||
|
|
||||||
|
input := elasticsearch.CreateElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("access_policies"); ok {
|
||||||
|
input.AccessPolicies = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("advanced_options"); ok {
|
||||||
|
input.AdvancedOptions = stringMapToPointers(v.(map[string]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ebs_options"); ok {
|
||||||
|
options := v.([]interface{})
|
||||||
|
|
||||||
|
if len(options) > 1 {
|
||||||
|
return fmt.Errorf("Only a single ebs_options block is expected")
|
||||||
|
} else if len(options) == 1 {
|
||||||
|
if options[0] == nil {
|
||||||
|
return fmt.Errorf("At least one field is expected inside ebs_options")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := options[0].(map[string]interface{})
|
||||||
|
input.EBSOptions = expandESEBSOptions(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("cluster_config"); ok {
|
||||||
|
config := v.([]interface{})
|
||||||
|
|
||||||
|
if len(config) > 1 {
|
||||||
|
return fmt.Errorf("Only a single cluster_config block is expected")
|
||||||
|
} else if len(config) == 1 {
|
||||||
|
if config[0] == nil {
|
||||||
|
return fmt.Errorf("At least one field is expected inside cluster_config")
|
||||||
|
}
|
||||||
|
m := config[0].(map[string]interface{})
|
||||||
|
input.ElasticsearchClusterConfig = expandESClusterConfig(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("snapshot_options"); ok {
|
||||||
|
options := v.([]interface{})
|
||||||
|
|
||||||
|
if len(options) > 1 {
|
||||||
|
return fmt.Errorf("Only a single snapshot_options block is expected")
|
||||||
|
} else if len(options) == 1 {
|
||||||
|
if options[0] == nil {
|
||||||
|
return fmt.Errorf("At least one field is expected inside snapshot_options")
|
||||||
|
}
|
||||||
|
|
||||||
|
o := options[0].(map[string]interface{})
|
||||||
|
|
||||||
|
snapshotOptions := elasticsearch.SnapshotOptions{
|
||||||
|
AutomatedSnapshotStartHour: aws.Int64(int64(o["automated_snapshot_start_hour"].(int))),
|
||||||
|
}
|
||||||
|
|
||||||
|
input.SnapshotOptions = &snapshotOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating ElasticSearch domain: %s", input)
|
||||||
|
out, err := conn.CreateElasticsearchDomain(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*out.DomainStatus.ARN)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for ElasticSearch domain %q to be created", d.Id())
|
||||||
|
err = resource.Retry(15*time.Minute, func() error {
|
||||||
|
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*out.DomainStatus.Processing && out.DomainStatus.Endpoint != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%q: Timeout while waiting for the domain to be created", d.Id())
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] ElasticSearch domain %q created", d.Id())
|
||||||
|
|
||||||
|
return resourceAwsElasticSearchDomainRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticSearchDomainRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).esconn
|
||||||
|
|
||||||
|
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Received ElasticSearch domain: %s", out)
|
||||||
|
|
||||||
|
ds := out.DomainStatus
|
||||||
|
|
||||||
|
d.Set("access_policies", *ds.AccessPolicies)
|
||||||
|
err = d.Set("advanced_options", pointersMapToStringList(ds.AdvancedOptions))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Set("domain_id", *ds.DomainId)
|
||||||
|
d.Set("domain_name", *ds.DomainName)
|
||||||
|
if ds.Endpoint != nil {
|
||||||
|
d.Set("endpoint", *ds.Endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Set("ebs_options", flattenESEBSOptions(ds.EBSOptions))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.Set("cluster_config", flattenESClusterConfig(ds.ElasticsearchClusterConfig))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ds.SnapshotOptions != nil {
|
||||||
|
d.Set("snapshot_options", map[string]interface{}{
|
||||||
|
"automated_snapshot_start_hour": *ds.SnapshotOptions.AutomatedSnapshotStartHour,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("arn", *ds.ARN)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticSearchDomainUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).esconn
|
||||||
|
|
||||||
|
input := elasticsearch.UpdateElasticsearchDomainConfigInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("access_policies") {
|
||||||
|
input.AccessPolicies = aws.String(d.Get("access_policies").(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("advanced_options") {
|
||||||
|
input.AdvancedOptions = stringMapToPointers(d.Get("advanced_options").(map[string]interface{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("ebs_options") {
|
||||||
|
options := d.Get("ebs_options").([]interface{})
|
||||||
|
|
||||||
|
if len(options) > 1 {
|
||||||
|
return fmt.Errorf("Only a single ebs_options block is expected")
|
||||||
|
} else if len(options) == 1 {
|
||||||
|
s := options[0].(map[string]interface{})
|
||||||
|
input.EBSOptions = expandESEBSOptions(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("cluster_config") {
|
||||||
|
config := d.Get("cluster_config").([]interface{})
|
||||||
|
|
||||||
|
if len(config) > 1 {
|
||||||
|
return fmt.Errorf("Only a single cluster_config block is expected")
|
||||||
|
} else if len(config) == 1 {
|
||||||
|
m := config[0].(map[string]interface{})
|
||||||
|
input.ElasticsearchClusterConfig = expandESClusterConfig(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("snapshot_options") {
|
||||||
|
options := d.Get("snapshot_options").([]interface{})
|
||||||
|
|
||||||
|
if len(options) > 1 {
|
||||||
|
return fmt.Errorf("Only a single snapshot_options block is expected")
|
||||||
|
} else if len(options) == 1 {
|
||||||
|
o := options[0].(map[string]interface{})
|
||||||
|
|
||||||
|
snapshotOptions := elasticsearch.SnapshotOptions{
|
||||||
|
AutomatedSnapshotStartHour: aws.Int64(int64(o["automated_snapshot_start_hour"].(int))),
|
||||||
|
}
|
||||||
|
|
||||||
|
input.SnapshotOptions = &snapshotOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.UpdateElasticsearchDomainConfig(&input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resource.Retry(25*time.Minute, func() error {
|
||||||
|
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *out.DomainStatus.Processing == false {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%q: Timeout while waiting for changes to be processed", d.Id())
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsElasticSearchDomainRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticSearchDomainDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).esconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting ElasticSearch domain: %q", d.Get("domain_name").(string))
|
||||||
|
_, err := conn.DeleteElasticsearchDomain(&elasticsearch.DeleteElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for ElasticSearch domain %q to be deleted", d.Get("domain_name").(string))
|
||||||
|
err = resource.Retry(15*time.Minute, func() error {
|
||||||
|
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(d.Get("domain_name").(string)),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
awsErr, ok := err.(awserr.Error)
|
||||||
|
if !ok {
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
if awsErr.Code() == "ResourceNotFoundException" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource.RetryError{Err: awsErr}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !*out.DomainStatus.Processing {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%q: Timeout while waiting for the domain to be deleted", d.Id())
|
||||||
|
})
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSElasticSearchDomain_basic(t *testing.T) {
|
||||||
|
var domain elasticsearch.ElasticsearchDomainStatus
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckESDomainDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccESDomainConfig_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckESDomainExists("aws_elasticsearch_domain.example", &domain),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSElasticSearchDomain_complex(t *testing.T) {
|
||||||
|
var domain elasticsearch.ElasticsearchDomainStatus
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckESDomainDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccESDomainConfig_complex,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckESDomainExists("aws_elasticsearch_domain.example", &domain),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckESDomainExists(n string, domain *elasticsearch.ElasticsearchDomainStatus) 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 ES Domain ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).esconn
|
||||||
|
opts := &elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(rs.Primary.Attributes["domain_name"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := conn.DescribeElasticsearchDomain(opts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error describing domain: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
*domain = *resp.DomainStatus
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckESDomainDestroy(s *terraform.State) error {
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_elasticsearch_domain" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).esconn
|
||||||
|
opts := &elasticsearch.DescribeElasticsearchDomainInput{
|
||||||
|
DomainName: aws.String(rs.Primary.Attributes["domain_name"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.DescribeElasticsearchDomain(opts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error describing ES domains: %q", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccESDomainConfig_basic = `
|
||||||
|
resource "aws_elasticsearch_domain" "example" {
|
||||||
|
domain_name = "tf-test-1"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccESDomainConfig_complex = `
|
||||||
|
resource "aws_elasticsearch_domain" "example" {
|
||||||
|
domain_name = "tf-test-2"
|
||||||
|
|
||||||
|
advanced_options {
|
||||||
|
"indices.fielddata.cache.size" = 80
|
||||||
|
}
|
||||||
|
|
||||||
|
ebs_options {
|
||||||
|
ebs_enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster_config {
|
||||||
|
instance_count = 2
|
||||||
|
zone_awareness_enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot_options {
|
||||||
|
automated_snapshot_start_hour = 23
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -24,31 +24,11 @@ func resourceAwsElb() *schema.Resource {
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": &schema.Schema{
|
"name": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
Computed: true,
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
ValidateFunc: validateElbName,
|
||||||
value := v.(string)
|
|
||||||
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"only alphanumeric characters and hyphens allowed in %q: %q",
|
|
||||||
k, value))
|
|
||||||
}
|
|
||||||
if len(value) > 32 {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"%q cannot be longer than 32 characters: %q", k, value))
|
|
||||||
}
|
|
||||||
if regexp.MustCompile(`^-`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"%q cannot begin with a hyphen: %q", k, value))
|
|
||||||
}
|
|
||||||
if regexp.MustCompile(`-$`).MatchString(value) {
|
|
||||||
errors = append(errors, fmt.Errorf(
|
|
||||||
"%q cannot end with a hyphen: %q", k, value))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"internal": &schema.Schema{
|
"internal": &schema.Schema{
|
||||||
|
@ -591,3 +571,26 @@ func isLoadBalancerNotFound(err error) bool {
|
||||||
elberr, ok := err.(awserr.Error)
|
elberr, ok := err.(awserr.Error)
|
||||||
return ok && elberr.Code() == "LoadBalancerNotFound"
|
return ok && elberr.Code() == "LoadBalancerNotFound"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateElbName(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"only alphanumeric characters and hyphens allowed in %q: %q",
|
||||||
|
k, value))
|
||||||
|
}
|
||||||
|
if len(value) > 32 {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot be longer than 32 characters: %q", k, value))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`^-`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot begin with a hyphen: %q", k, value))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`-$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot end with a hyphen: %q", k, value))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -431,12 +431,48 @@ func TestResourceAwsElbListenerHash(t *testing.T) {
|
||||||
for tn, tc := range cases {
|
for tn, tc := range cases {
|
||||||
leftHash := resourceAwsElbListenerHash(tc.Left)
|
leftHash := resourceAwsElbListenerHash(tc.Left)
|
||||||
rightHash := resourceAwsElbListenerHash(tc.Right)
|
rightHash := resourceAwsElbListenerHash(tc.Right)
|
||||||
if (leftHash == rightHash) != tc.Match {
|
if leftHash == rightHash != tc.Match {
|
||||||
t.Fatalf("%s: expected match: %t, but did not get it", tn, tc.Match)
|
t.Fatalf("%s: expected match: %t, but did not get it", tn, tc.Match)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceAWSELB_validateElbNameCannotBeginWithHyphen(t *testing.T) {
|
||||||
|
var elbName = "-Testing123"
|
||||||
|
_, errors := validateElbName(elbName, "SampleKey")
|
||||||
|
|
||||||
|
if len(errors) != 1 {
|
||||||
|
t.Fatalf("Expected the ELB Name to trigger a validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceAWSELB_validateElbNameCannotBeLongerThen32Characters(t *testing.T) {
|
||||||
|
var elbName = "Testing123dddddddddddddddddddvvvv"
|
||||||
|
_, errors := validateElbName(elbName, "SampleKey")
|
||||||
|
|
||||||
|
if len(errors) != 1 {
|
||||||
|
t.Fatalf("Expected the ELB Name to trigger a validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceAWSELB_validateElbNameCannotHaveSpecialCharacters(t *testing.T) {
|
||||||
|
var elbName = "Testing123%%"
|
||||||
|
_, errors := validateElbName(elbName, "SampleKey")
|
||||||
|
|
||||||
|
if len(errors) != 1 {
|
||||||
|
t.Fatalf("Expected the ELB Name to trigger a validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceAWSELB_validateElbNameCannotEndWithHyphen(t *testing.T) {
|
||||||
|
var elbName = "Testing123-"
|
||||||
|
_, errors := validateElbName(elbName, "SampleKey")
|
||||||
|
|
||||||
|
if len(errors) != 1 {
|
||||||
|
t.Fatalf("Expected the ELB Name to trigger a validation error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccCheckAWSELBDestroy(s *terraform.State) error {
|
func testAccCheckAWSELBDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).elbconn
|
conn := testAccProvider.Meta().(*AWSClient).elbconn
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ func testAccCheckAWSPolicyAttachmentAttributes(users []string, roles []string, g
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if uc != 0 || rc != 0 || gc != 0 {
|
if uc != 0 || rc != 0 || gc != 0 {
|
||||||
return fmt.Errorf("Error: Number of attached users, roles, or groups was incorrect:\n expected %d users and found %d\nexpected %d roles and found %d\nexpected %d groups and found %d", len(users), (len(users) - uc), len(roles), (len(roles) - rc), len(groups), (len(groups) - gc))
|
return fmt.Errorf("Error: Number of attached users, roles, or groups was incorrect:\n expected %d users and found %d\nexpected %d roles and found %d\nexpected %d groups and found %d", len(users), len(users)-uc, len(roles), len(roles)-rc, len(groups), len(groups)-gc)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsIamSamlProvider() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsIamSamlProviderCreate,
|
||||||
|
Read: resourceAwsIamSamlProviderRead,
|
||||||
|
Update: resourceAwsIamSamlProviderUpdate,
|
||||||
|
Delete: resourceAwsIamSamlProviderDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"valid_until": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"saml_metadata_document": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamSamlProviderCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
input := &iam.CreateSAMLProviderInput{
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
SAMLMetadataDocument: aws.String(d.Get("saml_metadata_document").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := iamconn.CreateSAMLProvider(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*out.SAMLProviderArn)
|
||||||
|
|
||||||
|
return resourceAwsIamSamlProviderRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamSamlProviderRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
input := &iam.GetSAMLProviderInput{
|
||||||
|
SAMLProviderArn: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
out, err := iamconn.GetSAMLProvider(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validUntil := out.ValidUntil.Format(time.RFC1123)
|
||||||
|
d.Set("valid_until", validUntil)
|
||||||
|
d.Set("saml_metadata_document", *out.SAMLMetadataDocument)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamSamlProviderUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
input := &iam.UpdateSAMLProviderInput{
|
||||||
|
SAMLProviderArn: aws.String(d.Id()),
|
||||||
|
SAMLMetadataDocument: aws.String(d.Get("saml_metadata_document").(string)),
|
||||||
|
}
|
||||||
|
_, err := iamconn.UpdateSAMLProvider(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsIamSamlProviderRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsIamSamlProviderDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
iamconn := meta.(*AWSClient).iamconn
|
||||||
|
|
||||||
|
input := &iam.DeleteSAMLProviderInput{
|
||||||
|
SAMLProviderArn: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
_, err := iamconn.DeleteSAMLProvider(input)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSIAMSamlProvider_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckIAMSamlProviderDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccIAMSamlProviderConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckIAMSamlProvider("aws_iam_saml_provider.salesforce"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccIAMSamlProviderConfigUpdate,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckIAMSamlProvider("aws_iam_saml_provider.salesforce"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckIAMSamlProviderDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckIAMSamlProvider(id string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[id]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not Found: %s", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
iamconn := testAccProvider.Meta().(*AWSClient).iamconn
|
||||||
|
_, err := iamconn.GetSAMLProvider(&iam.GetSAMLProviderInput{
|
||||||
|
SAMLProviderArn: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccIAMSamlProviderConfig = `
|
||||||
|
resource "aws_iam_saml_provider" "salesforce" {
|
||||||
|
name = "tf-salesforce-test"
|
||||||
|
saml_metadata_document = "${file("./test-fixtures/saml-metadata.xml")}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const testAccIAMSamlProviderConfigUpdate = `
|
||||||
|
resource "aws_iam_saml_provider" "salesforce" {
|
||||||
|
name = "tf-salesforce-test"
|
||||||
|
saml_metadata_document = "${file("./test-fixtures/saml-metadata-modified.xml")}"
|
||||||
|
}
|
||||||
|
`
|
|
@ -414,11 +414,6 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set our attributes
|
|
||||||
if err := resourceAwsInstanceRead(d, meta); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update if we need to
|
// Update if we need to
|
||||||
return resourceAwsInstanceUpdate(d, meta)
|
return resourceAwsInstanceUpdate(d, meta)
|
||||||
}
|
}
|
||||||
|
@ -548,16 +543,23 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SourceDestCheck can only be set on VPC instances
|
// SourceDestCheck can only be set on VPC instances
|
||||||
if d.Get("subnet_id").(string) != "" {
|
// AWS will return an error of InvalidParameterCombination if we attempt
|
||||||
log.Printf("[INFO] Modifying instance %s", d.Id())
|
// to modify the source_dest_check of an instance in EC2 Classic
|
||||||
_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
log.Printf("[INFO] Modifying instance %s", d.Id())
|
||||||
InstanceId: aws.String(d.Id()),
|
_, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{
|
||||||
SourceDestCheck: &ec2.AttributeBooleanValue{
|
InstanceId: aws.String(d.Id()),
|
||||||
Value: aws.Bool(d.Get("source_dest_check").(bool)),
|
SourceDestCheck: &ec2.AttributeBooleanValue{
|
||||||
},
|
Value: aws.Bool(d.Get("source_dest_check").(bool)),
|
||||||
})
|
},
|
||||||
if err != nil {
|
})
|
||||||
return err
|
if err != nil {
|
||||||
|
if ec2err, ok := err.(awserr.Error); ok {
|
||||||
|
// Toloerate InvalidParameterCombination error in Classic, otherwise
|
||||||
|
// return the error
|
||||||
|
if "InvalidParameterCombination" != ec2err.Code() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s", ec2err.Message())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,7 +695,7 @@ func readBlockDevicesFromInstance(instance *ec2.Instance, conn *ec2.EC2) (map[st
|
||||||
instanceBlockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping)
|
instanceBlockDevices := make(map[string]*ec2.InstanceBlockDeviceMapping)
|
||||||
for _, bd := range instance.BlockDeviceMappings {
|
for _, bd := range instance.BlockDeviceMappings {
|
||||||
if bd.Ebs != nil {
|
if bd.Ebs != nil {
|
||||||
instanceBlockDevices[*(bd.Ebs.VolumeId)] = bd
|
instanceBlockDevices[*bd.Ebs.VolumeId] = bd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,9 +755,9 @@ func readBlockDevicesFromInstance(instance *ec2.Instance, conn *ec2.EC2) (map[st
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockDeviceIsRoot(bd *ec2.InstanceBlockDeviceMapping, instance *ec2.Instance) bool {
|
func blockDeviceIsRoot(bd *ec2.InstanceBlockDeviceMapping, instance *ec2.Instance) bool {
|
||||||
return (bd.DeviceName != nil &&
|
return bd.DeviceName != nil &&
|
||||||
instance.RootDeviceName != nil &&
|
instance.RootDeviceName != nil &&
|
||||||
*bd.DeviceName == *instance.RootDeviceName)
|
*bd.DeviceName == *instance.RootDeviceName
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
|
func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
|
||||||
|
|
|
@ -190,6 +190,9 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
|
||||||
|
|
||||||
testCheck := func(enabled bool) resource.TestCheckFunc {
|
testCheck := func(enabled bool) resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
|
if v.SourceDestCheck == nil {
|
||||||
|
return fmt.Errorf("bad source_dest_check: got nil")
|
||||||
|
}
|
||||||
if *v.SourceDestCheck != enabled {
|
if *v.SourceDestCheck != enabled {
|
||||||
return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
|
return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@ func resourceAwsLBCookieStickinessPolicy() *schema.Resource {
|
||||||
// There is no concept of "updating" an LB Stickiness policy in
|
// There is no concept of "updating" an LB Stickiness policy in
|
||||||
// the AWS API.
|
// the AWS API.
|
||||||
Create: resourceAwsLBCookieStickinessPolicyCreate,
|
Create: resourceAwsLBCookieStickinessPolicyCreate,
|
||||||
Update: resourceAwsLBCookieStickinessPolicyCreate,
|
|
||||||
|
|
||||||
Read: resourceAwsLBCookieStickinessPolicyRead,
|
Read: resourceAwsLBCookieStickinessPolicyRead,
|
||||||
Delete: resourceAwsLBCookieStickinessPolicyDelete,
|
Delete: resourceAwsLBCookieStickinessPolicyDelete,
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksCustomLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "custom",
|
||||||
|
CustomShortName: true,
|
||||||
|
|
||||||
|
// The "custom" layer type has no additional attributes
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These tests assume the existence of predefined Opsworks IAM roles named `aws-opsworks-ec2-role`
|
||||||
|
// and `aws-opsworks-service-role`.
|
||||||
|
|
||||||
|
func TestAccAwsOpsworksCustomLayer(t *testing.T) {
|
||||||
|
opsiam := testAccAwsOpsworksStackIam{}
|
||||||
|
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAwsOpsworksCustomLayerDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAwsOpsworksCustomLayerConfigCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "auto_assign_elastic_ips", "false",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "auto_healing", "true",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "true",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "300",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAwsOpsworksCustomLayerConfigUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "false",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "120",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "3",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "3",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "system_packages.4101929740", "subversion",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.type", "io1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.number_of_disks", "4",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.mount_point", "/var",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.size", "100",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.raid_level", "1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.iops", "3000",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAwsOpsworksCustomLayerDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAwsOpsworksCustomLayerSecurityGroups = `
|
||||||
|
resource "aws_security_group" "tf-ops-acc-layer1" {
|
||||||
|
name = "tf-ops-acc-layer1"
|
||||||
|
ingress {
|
||||||
|
from_port = 8
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "aws_security_group" "tf-ops-acc-layer2" {
|
||||||
|
name = "tf-ops-acc-layer2"
|
||||||
|
ingress {
|
||||||
|
from_port = 8
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAwsOpsworksCustomLayerConfigCreate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
|
||||||
|
resource "aws_opsworks_custom_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
name = "tf-ops-acc-custom-layer"
|
||||||
|
short_name = "tf-ops-acc-custom-layer"
|
||||||
|
auto_assign_public_ips = true
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-layer1.id}",
|
||||||
|
"${aws_security_group.tf-ops-acc-layer2.id}",
|
||||||
|
]
|
||||||
|
drain_elb_on_shutdown = true
|
||||||
|
instance_shutdown_timeout = 300
|
||||||
|
system_packages = [
|
||||||
|
"git",
|
||||||
|
"golang",
|
||||||
|
]
|
||||||
|
ebs_volume {
|
||||||
|
type = "gp2"
|
||||||
|
number_of_disks = 2
|
||||||
|
mount_point = "/home"
|
||||||
|
size = 100
|
||||||
|
raid_level = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAwsOpsworksCustomLayerConfigUpdate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
|
||||||
|
resource "aws_security_group" "tf-ops-acc-layer3" {
|
||||||
|
name = "tf-ops-acc-layer3"
|
||||||
|
ingress {
|
||||||
|
from_port = 8
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resource "aws_opsworks_custom_layer" "tf-acc" {
|
||||||
|
stack_id = "${aws_opsworks_stack.tf-acc.id}"
|
||||||
|
name = "tf-ops-acc-custom-layer"
|
||||||
|
short_name = "tf-ops-acc-custom-layer"
|
||||||
|
auto_assign_public_ips = true
|
||||||
|
custom_security_group_ids = [
|
||||||
|
"${aws_security_group.tf-ops-acc-layer1.id}",
|
||||||
|
"${aws_security_group.tf-ops-acc-layer2.id}",
|
||||||
|
"${aws_security_group.tf-ops-acc-layer3.id}",
|
||||||
|
]
|
||||||
|
drain_elb_on_shutdown = false
|
||||||
|
instance_shutdown_timeout = 120
|
||||||
|
system_packages = [
|
||||||
|
"git",
|
||||||
|
"golang",
|
||||||
|
"subversion",
|
||||||
|
]
|
||||||
|
ebs_volume {
|
||||||
|
type = "gp2"
|
||||||
|
number_of_disks = 2
|
||||||
|
mount_point = "/home"
|
||||||
|
size = 100
|
||||||
|
raid_level = 0
|
||||||
|
}
|
||||||
|
ebs_volume {
|
||||||
|
type = "io1"
|
||||||
|
number_of_disks = 4
|
||||||
|
mount_point = "/var"
|
||||||
|
size = 100
|
||||||
|
raid_level = 1
|
||||||
|
iops = 3000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,33 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksGangliaLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "monitoring-master",
|
||||||
|
DefaultLayerName: "Ganglia",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"url": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "GangliaUrl",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "/ganglia",
|
||||||
|
},
|
||||||
|
"username": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "GangliaUser",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "opsworks",
|
||||||
|
},
|
||||||
|
"password": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "GangliaPassword",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
WriteOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksHaproxyLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "lb",
|
||||||
|
DefaultLayerName: "HAProxy",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"stats_enabled": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "EnableHaproxyStats",
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
"stats_url": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "HaproxyStatsUrl",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "/haproxy?stats",
|
||||||
|
},
|
||||||
|
"stats_user": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "HaproxyStatsUser",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "opsworks",
|
||||||
|
},
|
||||||
|
"stats_password": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "HaproxyStatsPassword",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
WriteOnly: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"healthcheck_url": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "HaproxyHealthCheckUrl",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "/",
|
||||||
|
},
|
||||||
|
"healthcheck_method": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "HaproxyHealthCheckMethod",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "OPTIONS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksJavaAppLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "java-app",
|
||||||
|
DefaultLayerName: "Java App Server",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"jvm_type": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "Jvm",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "openjdk",
|
||||||
|
},
|
||||||
|
"jvm_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "JvmVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "7",
|
||||||
|
},
|
||||||
|
"jvm_options": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "JvmOptions",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "",
|
||||||
|
},
|
||||||
|
"app_server": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "JavaAppServer",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "tomcat",
|
||||||
|
},
|
||||||
|
"app_server_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "JavaAppServerVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksMemcachedLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "memcached",
|
||||||
|
DefaultLayerName: "Memcached",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"allocated_memory": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "MemcachedMemory",
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Default: 512,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksMysqlLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "db-master",
|
||||||
|
DefaultLayerName: "MySQL",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"root_password": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "MysqlRootPassword",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
WriteOnly: true,
|
||||||
|
},
|
||||||
|
"root_password_on_all_instances": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "MysqlRootPasswordUbiquitous",
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksNodejsAppLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "nodejs-app",
|
||||||
|
DefaultLayerName: "Node.js App Server",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"nodejs_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "NodejsVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "0.10.38",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksPhpAppLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "php-app",
|
||||||
|
DefaultLayerName: "PHP App Server",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksRailsAppLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "rails-app",
|
||||||
|
DefaultLayerName: "Rails App Server",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{
|
||||||
|
"ruby_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "RubyVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "2.0.0",
|
||||||
|
},
|
||||||
|
"app_server": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "RailsStack",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "apache_passenger",
|
||||||
|
},
|
||||||
|
"passenger_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "PassengerVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "4.0.46",
|
||||||
|
},
|
||||||
|
"rubygems_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "RubygemsVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "2.2.2",
|
||||||
|
},
|
||||||
|
"manage_bundler": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "ManageBundler",
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
"bundler_version": &opsworksLayerTypeAttribute{
|
||||||
|
AttrName: "BundlerVersion",
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Default: "1.5.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,456 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStack() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsOpsworksStackCreate,
|
||||||
|
Read: resourceAwsOpsworksStackRead,
|
||||||
|
Update: resourceAwsOpsworksStackUpdate,
|
||||||
|
Delete: resourceAwsOpsworksStackDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"region": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"service_role_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_instance_profile_arn": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"color": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"configuration_manager_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "Chef",
|
||||||
|
},
|
||||||
|
|
||||||
|
"configuration_manager_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "11.4",
|
||||||
|
},
|
||||||
|
|
||||||
|
"manage_berkshelf": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"berkshelf_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "3.2.0",
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_cookbooks_source": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"url": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"username": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"password": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"revision": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"ssh_key": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"custom_json": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_availability_zone": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_os": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "Ubuntu 12.04 LTS",
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_root_device_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "instance-store",
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_ssh_key_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_subnet_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"hostname_theme": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "Layer_Dependent",
|
||||||
|
},
|
||||||
|
|
||||||
|
"use_custom_cookbooks": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
"use_opsworks_security_groups": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"vpc_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackValidate(d *schema.ResourceData) error {
|
||||||
|
cookbooksSourceCount := d.Get("custom_cookbooks_source.#").(int)
|
||||||
|
if cookbooksSourceCount > 1 {
|
||||||
|
return fmt.Errorf("Only one custom_cookbooks_source is permitted")
|
||||||
|
}
|
||||||
|
|
||||||
|
vpcId := d.Get("vpc_id").(string)
|
||||||
|
if vpcId != "" {
|
||||||
|
if d.Get("default_subnet_id").(string) == "" {
|
||||||
|
return fmt.Errorf("default_subnet_id must be set if vpc_id is set")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if d.Get("default_availability_zone").(string) == "" {
|
||||||
|
return fmt.Errorf("either vpc_id or default_availability_zone must be set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackCustomCookbooksSource(d *schema.ResourceData) *opsworks.Source {
|
||||||
|
count := d.Get("custom_cookbooks_source.#").(int)
|
||||||
|
if count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &opsworks.Source{
|
||||||
|
Type: aws.String(d.Get("custom_cookbooks_source.0.type").(string)),
|
||||||
|
Url: aws.String(d.Get("custom_cookbooks_source.0.url").(string)),
|
||||||
|
Username: aws.String(d.Get("custom_cookbooks_source.0.username").(string)),
|
||||||
|
Password: aws.String(d.Get("custom_cookbooks_source.0.password").(string)),
|
||||||
|
Revision: aws.String(d.Get("custom_cookbooks_source.0.revision").(string)),
|
||||||
|
SshKey: aws.String(d.Get("custom_cookbooks_source.0.ssh_key").(string)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksSetStackCustomCookbooksSource(d *schema.ResourceData, v *opsworks.Source) {
|
||||||
|
nv := make([]interface{}, 0, 1)
|
||||||
|
if v != nil {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
if v.Type != nil {
|
||||||
|
m["type"] = *v.Type
|
||||||
|
}
|
||||||
|
if v.Url != nil {
|
||||||
|
m["url"] = *v.Url
|
||||||
|
}
|
||||||
|
if v.Username != nil {
|
||||||
|
m["username"] = *v.Username
|
||||||
|
}
|
||||||
|
if v.Password != nil {
|
||||||
|
m["password"] = *v.Password
|
||||||
|
}
|
||||||
|
if v.Revision != nil {
|
||||||
|
m["revision"] = *v.Revision
|
||||||
|
}
|
||||||
|
if v.SshKey != nil {
|
||||||
|
m["ssh_key"] = *v.SshKey
|
||||||
|
}
|
||||||
|
nv = append(nv, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := d.Set("custom_cookbooks_source", nv)
|
||||||
|
if err != nil {
|
||||||
|
// should never happen
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
req := &opsworks.DescribeStacksInput{
|
||||||
|
StackIds: []*string{
|
||||||
|
aws.String(d.Id()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Reading OpsWorks stack: %s", d.Id())
|
||||||
|
|
||||||
|
resp, err := client.DescribeStacks(req)
|
||||||
|
if err != nil {
|
||||||
|
if awserr, ok := err.(awserr.Error); ok {
|
||||||
|
if awserr.Code() == "ResourceNotFoundException" {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stack := resp.Stacks[0]
|
||||||
|
d.Set("name", stack.Name)
|
||||||
|
d.Set("region", stack.Region)
|
||||||
|
d.Set("default_instance_profile_arn", stack.DefaultInstanceProfileArn)
|
||||||
|
d.Set("service_role_arn", stack.ServiceRoleArn)
|
||||||
|
d.Set("default_availability_zone", stack.DefaultAvailabilityZone)
|
||||||
|
d.Set("default_os", stack.DefaultOs)
|
||||||
|
d.Set("default_root_device_type", stack.DefaultRootDeviceType)
|
||||||
|
d.Set("default_ssh_key_name", stack.DefaultSshKeyName)
|
||||||
|
d.Set("default_subnet_id", stack.DefaultSubnetId)
|
||||||
|
d.Set("hostname_theme", stack.HostnameTheme)
|
||||||
|
d.Set("use_custom_cookbooks", stack.UseCustomCookbooks)
|
||||||
|
d.Set("use_opsworks_security_groups", stack.UseOpsworksSecurityGroups)
|
||||||
|
d.Set("vpc_id", stack.VpcId)
|
||||||
|
if color, ok := stack.Attributes["Color"]; ok {
|
||||||
|
d.Set("color", color)
|
||||||
|
}
|
||||||
|
if stack.ConfigurationManager != nil {
|
||||||
|
d.Set("configuration_manager_name", stack.ConfigurationManager.Name)
|
||||||
|
d.Set("configuration_manager_version", stack.ConfigurationManager.Version)
|
||||||
|
}
|
||||||
|
if stack.ChefConfiguration != nil {
|
||||||
|
d.Set("berkshelf_version", stack.ChefConfiguration.BerkshelfVersion)
|
||||||
|
d.Set("manage_berkshelf", stack.ChefConfiguration.ManageBerkshelf)
|
||||||
|
}
|
||||||
|
resourceAwsOpsworksSetStackCustomCookbooksSource(d, stack.CustomCookbooksSource)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
err := resourceAwsOpsworksStackValidate(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &opsworks.CreateStackInput{
|
||||||
|
DefaultInstanceProfileArn: aws.String(d.Get("default_instance_profile_arn").(string)),
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
Region: aws.String(d.Get("region").(string)),
|
||||||
|
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
|
||||||
|
}
|
||||||
|
inVpc := false
|
||||||
|
if vpcId, ok := d.GetOk("vpc_id"); ok {
|
||||||
|
req.VpcId = aws.String(vpcId.(string))
|
||||||
|
inVpc = true
|
||||||
|
}
|
||||||
|
if defaultSubnetId, ok := d.GetOk("default_subnet_id"); ok {
|
||||||
|
req.DefaultSubnetId = aws.String(defaultSubnetId.(string))
|
||||||
|
}
|
||||||
|
if defaultAvailabilityZone, ok := d.GetOk("default_availability_zone"); ok {
|
||||||
|
req.DefaultAvailabilityZone = aws.String(defaultAvailabilityZone.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating OpsWorks stack: %s", *req.Name)
|
||||||
|
|
||||||
|
var resp *opsworks.CreateStackOutput
|
||||||
|
err = resource.Retry(20*time.Minute, func() error {
|
||||||
|
var cerr error
|
||||||
|
resp, cerr = client.CreateStack(req)
|
||||||
|
if cerr != nil {
|
||||||
|
if opserr, ok := cerr.(awserr.Error); ok {
|
||||||
|
// If Terraform is also managing the service IAM role,
|
||||||
|
// it may have just been created and not yet be
|
||||||
|
// propagated.
|
||||||
|
// AWS doesn't provide a machine-readable code for this
|
||||||
|
// specific error, so we're forced to do fragile message
|
||||||
|
// matching.
|
||||||
|
// The full error we're looking for looks something like
|
||||||
|
// the following:
|
||||||
|
// Service Role Arn: [...] is not yet propagated, please try again in a couple of minutes
|
||||||
|
if opserr.Code() == "ValidationException" && strings.Contains(opserr.Message(), "not yet propagated") {
|
||||||
|
log.Printf("[INFO] Waiting for service IAM role to propagate")
|
||||||
|
return cerr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resource.RetryError{Err: cerr}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stackId := *resp.StackId
|
||||||
|
d.SetId(stackId)
|
||||||
|
d.Set("id", stackId)
|
||||||
|
|
||||||
|
if inVpc {
|
||||||
|
// For VPC-based stacks, OpsWorks asynchronously creates some default
|
||||||
|
// security groups which must exist before layers can be created.
|
||||||
|
// Unfortunately it doesn't tell us what the ids of these are, so
|
||||||
|
// we can't actually check for them. Instead, we just wait a nominal
|
||||||
|
// amount of time for their creation to complete.
|
||||||
|
log.Print("[INFO] Waiting for OpsWorks built-in security groups to be created")
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsOpsworksStackUpdate(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
err := resourceAwsOpsworksStackValidate(d)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &opsworks.UpdateStackInput{
|
||||||
|
CustomJson: aws.String(d.Get("custom_json").(string)),
|
||||||
|
DefaultInstanceProfileArn: aws.String(d.Get("default_instance_profile_arn").(string)),
|
||||||
|
DefaultRootDeviceType: aws.String(d.Get("default_root_device_type").(string)),
|
||||||
|
DefaultSshKeyName: aws.String(d.Get("default_ssh_key_name").(string)),
|
||||||
|
Name: aws.String(d.Get("name").(string)),
|
||||||
|
ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)),
|
||||||
|
StackId: aws.String(d.Id()),
|
||||||
|
UseCustomCookbooks: aws.Bool(d.Get("use_custom_cookbooks").(bool)),
|
||||||
|
UseOpsworksSecurityGroups: aws.Bool(d.Get("use_opsworks_security_groups").(bool)),
|
||||||
|
Attributes: make(map[string]*string),
|
||||||
|
CustomCookbooksSource: resourceAwsOpsworksStackCustomCookbooksSource(d),
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("default_os"); ok {
|
||||||
|
req.DefaultOs = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("default_subnet_id"); ok {
|
||||||
|
req.DefaultSubnetId = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("default_availability_zone"); ok {
|
||||||
|
req.DefaultAvailabilityZone = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("hostname_theme"); ok {
|
||||||
|
req.HostnameTheme = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
if v, ok := d.GetOk("color"); ok {
|
||||||
|
req.Attributes["Color"] = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
req.ChefConfiguration = &opsworks.ChefConfiguration{
|
||||||
|
BerkshelfVersion: aws.String(d.Get("berkshelf_version").(string)),
|
||||||
|
ManageBerkshelf: aws.Bool(d.Get("manage_berkshelf").(bool)),
|
||||||
|
}
|
||||||
|
req.ConfigurationManager = &opsworks.StackConfigurationManager{
|
||||||
|
Name: aws.String(d.Get("configuration_manager_name").(string)),
|
||||||
|
Version: aws.String(d.Get("configuration_manager_version").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Updating OpsWorks stack: %s", d.Id())
|
||||||
|
|
||||||
|
_, err = client.UpdateStack(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsOpsworksStackRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStackDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
client := meta.(*AWSClient).opsworksconn
|
||||||
|
|
||||||
|
req := &opsworks.DeleteStackInput{
|
||||||
|
StackId: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Deleting OpsWorks stack: %s", d.Id())
|
||||||
|
|
||||||
|
_, err := client.DeleteStack(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a stack in a VPC, OpsWorks has created some default security groups
|
||||||
|
// in the VPC, which it will now delete.
|
||||||
|
// Unfortunately, the security groups are deleted asynchronously and there
|
||||||
|
// is no robust way for us to determine when it is done. The VPC itself
|
||||||
|
// isn't deletable until the security groups are cleaned up, so this could
|
||||||
|
// make 'terraform destroy' fail if the VPC is also managed and we don't
|
||||||
|
// wait for the security groups to be deleted.
|
||||||
|
// There is no robust way to check for this, so we'll just wait a
|
||||||
|
// nominal amount of time.
|
||||||
|
if _, ok := d.GetOk("vpc_id"); ok {
|
||||||
|
log.Print("[INFO] Waiting for Opsworks built-in security groups to be deleted")
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,353 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/iam"
|
||||||
|
"github.com/aws/aws-sdk-go/service/opsworks"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These tests assume the existence of predefined Opsworks IAM roles named `aws-opsworks-ec2-role`
|
||||||
|
// and `aws-opsworks-service-role`.
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
//// Tests for the No-VPC case
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
var testAccAwsOpsworksStackConfigNoVpcCreate = `
|
||||||
|
resource "aws_opsworks_stack" "tf-acc" {
|
||||||
|
name = "tf-opsworks-acc"
|
||||||
|
region = "us-west-2"
|
||||||
|
service_role_arn = "%s"
|
||||||
|
default_instance_profile_arn = "%s"
|
||||||
|
default_availability_zone = "us-west-2a"
|
||||||
|
default_os = "Amazon Linux 2014.09"
|
||||||
|
default_root_device_type = "ebs"
|
||||||
|
custom_json = "{\"key\": \"value\"}"
|
||||||
|
configuration_manager_version = "11.10"
|
||||||
|
use_opsworks_security_groups = false
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var testAccAWSOpsworksStackConfigNoVpcUpdate = `
|
||||||
|
resource "aws_opsworks_stack" "tf-acc" {
|
||||||
|
name = "tf-opsworks-acc"
|
||||||
|
region = "us-west-2"
|
||||||
|
service_role_arn = "%s"
|
||||||
|
default_instance_profile_arn = "%s"
|
||||||
|
default_availability_zone = "us-west-2a"
|
||||||
|
default_os = "Amazon Linux 2014.09"
|
||||||
|
default_root_device_type = "ebs"
|
||||||
|
custom_json = "{\"key\": \"value\"}"
|
||||||
|
configuration_manager_version = "11.10"
|
||||||
|
use_opsworks_security_groups = false
|
||||||
|
use_custom_cookbooks = true
|
||||||
|
manage_berkshelf = true
|
||||||
|
custom_cookbooks_source {
|
||||||
|
type = "git"
|
||||||
|
revision = "master"
|
||||||
|
url = "https://github.com/awslabs/opsworks-example-cookbooks.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestAccAwsOpsworksStackNoVpc(t *testing.T) {
|
||||||
|
opsiam := testAccAwsOpsworksStackIam{}
|
||||||
|
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAwsOpsworksStackConfigNoVpcCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: testAccAwsOpsworksStackCheckResourceAttrsCreate,
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAWSOpsworksStackConfigNoVpcUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: testAccAwsOpsworksStackCheckResourceAttrsUpdate,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//// Tests for the VPC case
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
var testAccAwsOpsworksStackConfigVpcCreate = `
|
||||||
|
resource "aws_vpc" "tf-acc" {
|
||||||
|
cidr_block = "10.3.5.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "tf-acc" {
|
||||||
|
vpc_id = "${aws_vpc.tf-acc.id}"
|
||||||
|
cidr_block = "${aws_vpc.tf-acc.cidr_block}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
resource "aws_opsworks_stack" "tf-acc" {
|
||||||
|
name = "tf-opsworks-acc"
|
||||||
|
region = "us-west-2"
|
||||||
|
vpc_id = "${aws_vpc.tf-acc.id}"
|
||||||
|
default_subnet_id = "${aws_subnet.tf-acc.id}"
|
||||||
|
service_role_arn = "%s"
|
||||||
|
default_instance_profile_arn = "%s"
|
||||||
|
default_os = "Amazon Linux 2014.09"
|
||||||
|
default_root_device_type = "ebs"
|
||||||
|
custom_json = "{\"key\": \"value\"}"
|
||||||
|
configuration_manager_version = "11.10"
|
||||||
|
use_opsworks_security_groups = false
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var testAccAWSOpsworksStackConfigVpcUpdate = `
|
||||||
|
resource "aws_vpc" "tf-acc" {
|
||||||
|
cidr_block = "10.3.5.0/24"
|
||||||
|
}
|
||||||
|
resource "aws_subnet" "tf-acc" {
|
||||||
|
vpc_id = "${aws_vpc.tf-acc.id}"
|
||||||
|
cidr_block = "${aws_vpc.tf-acc.cidr_block}"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
}
|
||||||
|
resource "aws_opsworks_stack" "tf-acc" {
|
||||||
|
name = "tf-opsworks-acc"
|
||||||
|
region = "us-west-2"
|
||||||
|
vpc_id = "${aws_vpc.tf-acc.id}"
|
||||||
|
default_subnet_id = "${aws_subnet.tf-acc.id}"
|
||||||
|
service_role_arn = "%s"
|
||||||
|
default_instance_profile_arn = "%s"
|
||||||
|
default_os = "Amazon Linux 2014.09"
|
||||||
|
default_root_device_type = "ebs"
|
||||||
|
custom_json = "{\"key\": \"value\"}"
|
||||||
|
configuration_manager_version = "11.10"
|
||||||
|
use_opsworks_security_groups = false
|
||||||
|
use_custom_cookbooks = true
|
||||||
|
manage_berkshelf = true
|
||||||
|
custom_cookbooks_source {
|
||||||
|
type = "git"
|
||||||
|
revision = "master"
|
||||||
|
url = "https://github.com/awslabs/opsworks-example-cookbooks.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestAccAwsOpsworksStackVpc(t *testing.T) {
|
||||||
|
opsiam := testAccAwsOpsworksStackIam{}
|
||||||
|
testAccAwsOpsworksStackPopulateIam(t, &opsiam)
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAwsOpsworksStackDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAwsOpsworksStackConfigVpcCreate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: testAccAwsOpsworksStackCheckResourceAttrsCreate,
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: fmt.Sprintf(testAccAWSOpsworksStackConfigVpcUpdate, opsiam.ServiceRoleArn, opsiam.InstanceProfileArn),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccAwsOpsworksStackCheckResourceAttrsUpdate,
|
||||||
|
testAccAwsOpsworksCheckVpc,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
//// Checkers and Utilities
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
var testAccAwsOpsworksStackCheckResourceAttrsCreate = resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"name",
|
||||||
|
"tf-opsworks-acc",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_availability_zone",
|
||||||
|
"us-west-2a",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_os",
|
||||||
|
"Amazon Linux 2014.09",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_root_device_type",
|
||||||
|
"ebs",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"custom_json",
|
||||||
|
`{"key": "value"}`,
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"configuration_manager_version",
|
||||||
|
"11.10",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"use_opsworks_security_groups",
|
||||||
|
"false",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
var testAccAwsOpsworksStackCheckResourceAttrsUpdate = resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"name",
|
||||||
|
"tf-opsworks-acc",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_availability_zone",
|
||||||
|
"us-west-2a",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_os",
|
||||||
|
"Amazon Linux 2014.09",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"default_root_device_type",
|
||||||
|
"ebs",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"custom_json",
|
||||||
|
`{"key": "value"}`,
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"configuration_manager_version",
|
||||||
|
"11.10",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"use_opsworks_security_groups",
|
||||||
|
"false",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"use_custom_cookbooks",
|
||||||
|
"true",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"manage_berkshelf",
|
||||||
|
"true",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"custom_cookbooks_source.0.type",
|
||||||
|
"git",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"custom_cookbooks_source.0.revision",
|
||||||
|
"master",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_opsworks_stack.tf-acc",
|
||||||
|
"custom_cookbooks_source.0.url",
|
||||||
|
"https://github.com/awslabs/opsworks-example-cookbooks.git",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
func testAccAwsOpsworksCheckVpc(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources["aws_opsworks_stack.tf-acc"]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", "aws_opsworks_stack.tf-acc")
|
||||||
|
}
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := rs.Primary
|
||||||
|
|
||||||
|
opsworksconn := testAccProvider.Meta().(*AWSClient).opsworksconn
|
||||||
|
describeOpts := &opsworks.DescribeStacksInput{
|
||||||
|
StackIds: []*string{aws.String(p.ID)},
|
||||||
|
}
|
||||||
|
resp, err := opsworksconn.DescribeStacks(describeOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.Stacks) == 0 {
|
||||||
|
return fmt.Errorf("No stack %s not found", p.ID)
|
||||||
|
}
|
||||||
|
if p.Attributes["vpc_id"] != *resp.Stacks[0].VpcId {
|
||||||
|
return fmt.Errorf("VPCID Got %s, expected %s", *resp.Stacks[0].VpcId, p.Attributes["vpc_id"])
|
||||||
|
}
|
||||||
|
if p.Attributes["default_subnet_id"] != *resp.Stacks[0].DefaultSubnetId {
|
||||||
|
return fmt.Errorf("VPCID Got %s, expected %s", *resp.Stacks[0].DefaultSubnetId, p.Attributes["default_subnet_id"])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAwsOpsworksStackDestroy(s *terraform.State) error {
|
||||||
|
if len(s.RootModule().Resources) > 0 {
|
||||||
|
return fmt.Errorf("Expected all resources to be gone, but found: %#v", s.RootModule().Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holds the two IAM object ARNs used in stack objects we'll create.
|
||||||
|
type testAccAwsOpsworksStackIam struct {
|
||||||
|
ServiceRoleArn string
|
||||||
|
InstanceProfileArn string
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccAwsOpsworksStackPopulateIam(t *testing.T, opsiam *testAccAwsOpsworksStackIam) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccInstanceConfig_pre, // noop
|
||||||
|
Check: testAccCheckAwsOpsworksEnsureIam(t, opsiam),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAwsOpsworksEnsureIam(t *testing.T, opsiam *testAccAwsOpsworksStackIam) func(*terraform.State) error {
|
||||||
|
return func(_ *terraform.State) error {
|
||||||
|
iamconn := testAccProvider.Meta().(*AWSClient).iamconn
|
||||||
|
|
||||||
|
serviceRoleOpts := &iam.GetRoleInput{
|
||||||
|
RoleName: aws.String("aws-opsworks-service-role"),
|
||||||
|
}
|
||||||
|
respServiceRole, err := iamconn.GetRole(serviceRoleOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceProfileOpts := &iam.GetInstanceProfileInput{
|
||||||
|
InstanceProfileName: aws.String("aws-opsworks-ec2-role"),
|
||||||
|
}
|
||||||
|
respInstanceProfile, err := iamconn.GetInstanceProfile(instanceProfileOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opsiam.ServiceRoleArn = *respServiceRole.Role.Arn
|
||||||
|
opsiam.InstanceProfileArn = *respInstanceProfile.InstanceProfile.Arn
|
||||||
|
|
||||||
|
t.Logf("[DEBUG] ServiceRoleARN for OpsWorks: %s", opsiam.ServiceRoleArn)
|
||||||
|
t.Logf("[DEBUG] Instance Profile ARN for OpsWorks: %s", opsiam.InstanceProfileArn)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsOpsworksStaticWebLayer() *schema.Resource {
|
||||||
|
layerType := &opsworksLayerType{
|
||||||
|
TypeName: "web",
|
||||||
|
DefaultLayerName: "Static Web Server",
|
||||||
|
|
||||||
|
Attributes: map[string]*opsworksLayerTypeAttribute{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType.SchemaResource()
|
||||||
|
}
|
|
@ -0,0 +1,347 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsRDSCluster() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsRDSClusterCreate,
|
||||||
|
Read: resourceAwsRDSClusterRead,
|
||||||
|
Update: resourceAwsRDSClusterUpdate,
|
||||||
|
Delete: resourceAwsRDSClusterDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
|
||||||
|
"availability_zones": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"cluster_identifier": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateRdsId,
|
||||||
|
},
|
||||||
|
|
||||||
|
"cluster_members": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
|
||||||
|
"database_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"db_subnet_group_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"endpoint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"engine": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"final_snapshot_identifier": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
|
||||||
|
es = append(es, fmt.Errorf(
|
||||||
|
"only alphanumeric characters and hyphens allowed in %q", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`--`).MatchString(value) {
|
||||||
|
es = append(es, fmt.Errorf("%q cannot contain two consecutive hyphens", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`-$`).MatchString(value) {
|
||||||
|
es = append(es, fmt.Errorf("%q cannot end in a hyphen", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"master_username": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"master_password": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"port": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// apply_immediately is used to determine when the update modifications
|
||||||
|
// take place.
|
||||||
|
// See http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.DBInstance.Modifying.html
|
||||||
|
"apply_immediately": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"vpc_security_group_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: schema.HashString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
createOpts := &rds.CreateDBClusterInput{
|
||||||
|
DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
|
||||||
|
Engine: aws.String("aurora"),
|
||||||
|
MasterUserPassword: aws.String(d.Get("master_password").(string)),
|
||||||
|
MasterUsername: aws.String(d.Get("master_username").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := d.Get("database_name"); v.(string) != "" {
|
||||||
|
createOpts.DatabaseName = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := d.GetOk("port"); ok {
|
||||||
|
createOpts.Port = aws.Int64(int64(attr.(int)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := d.GetOk("db_subnet_group_name"); ok {
|
||||||
|
createOpts.DBSubnetGroupName = aws.String(attr.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
|
||||||
|
createOpts.VpcSecurityGroupIds = expandStringList(attr.List())
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr := d.Get("availability_zones").(*schema.Set); attr.Len() > 0 {
|
||||||
|
createOpts.AvailabilityZones = expandStringList(attr.List())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] RDS Cluster create options: %s", createOpts)
|
||||||
|
resp, err := conn.CreateDBCluster(createOpts)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Error creating RDS Cluster: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG]: Cluster create response: %s", resp)
|
||||||
|
d.SetId(*resp.DBCluster.DBClusterIdentifier)
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"creating", "backing-up", "modifying"},
|
||||||
|
Target: "available",
|
||||||
|
Refresh: resourceAwsRDSClusterStateRefreshFunc(d, meta),
|
||||||
|
Timeout: 5 * time.Minute,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait, catching any errors
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[WARN] Error waiting for RDS Cluster state to be \"available\": %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsRDSClusterRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
|
||||||
|
DBClusterIdentifier: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if "DBClusterNotFoundFault" == awsErr.Code() {
|
||||||
|
d.SetId("")
|
||||||
|
log.Printf("[DEBUG] RDS Cluster (%s) not found", d.Id())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Error describing RDS Cluster (%s)", d.Id())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbc *rds.DBCluster
|
||||||
|
for _, c := range resp.DBClusters {
|
||||||
|
if *c.DBClusterIdentifier == d.Id() {
|
||||||
|
dbc = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbc == nil {
|
||||||
|
log.Printf("[WARN] RDS Cluster (%s) not found", d.Id())
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Set("availability_zones", aws.StringValueSlice(dbc.AvailabilityZones)); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error saving AvailabilityZones to state for RDS Cluster (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
d.Set("database_name", dbc.DatabaseName)
|
||||||
|
d.Set("db_subnet_group_name", dbc.DBSubnetGroup)
|
||||||
|
d.Set("endpoint", dbc.Endpoint)
|
||||||
|
d.Set("engine", dbc.Engine)
|
||||||
|
d.Set("master_username", dbc.MasterUsername)
|
||||||
|
d.Set("port", dbc.Port)
|
||||||
|
|
||||||
|
var vpcg []string
|
||||||
|
for _, g := range dbc.VpcSecurityGroups {
|
||||||
|
vpcg = append(vpcg, *g.VpcSecurityGroupId)
|
||||||
|
}
|
||||||
|
if err := d.Set("vpc_security_group_ids", vpcg); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error saving VPC Security Group IDs to state for RDS Cluster (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cm []string
|
||||||
|
for _, m := range dbc.DBClusterMembers {
|
||||||
|
cm = append(cm, *m.DBInstanceIdentifier)
|
||||||
|
}
|
||||||
|
if err := d.Set("cluster_members", cm); err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error saving RDS Cluster Members to state for RDS Cluster (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
req := &rds.ModifyDBClusterInput{
|
||||||
|
ApplyImmediately: aws.Bool(d.Get("apply_immediately").(bool)),
|
||||||
|
DBClusterIdentifier: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("master_password") {
|
||||||
|
req.MasterUserPassword = aws.String(d.Get("master_password").(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.HasChange("vpc_security_group_ids") {
|
||||||
|
if attr := d.Get("vpc_security_group_ids").(*schema.Set); attr.Len() > 0 {
|
||||||
|
req.VpcSecurityGroupIds = expandStringList(attr.List())
|
||||||
|
} else {
|
||||||
|
req.VpcSecurityGroupIds = []*string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.ModifyDBCluster(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[WARN] Error modifying RDS Cluster (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsRDSClusterRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
log.Printf("[DEBUG] Destroying RDS Cluster (%s)", d.Id())
|
||||||
|
|
||||||
|
deleteOpts := rds.DeleteDBClusterInput{
|
||||||
|
DBClusterIdentifier: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
finalSnapshot := d.Get("final_snapshot_identifier").(string)
|
||||||
|
if finalSnapshot == "" {
|
||||||
|
deleteOpts.SkipFinalSnapshot = aws.Bool(true)
|
||||||
|
} else {
|
||||||
|
deleteOpts.FinalDBSnapshotIdentifier = aws.String(finalSnapshot)
|
||||||
|
deleteOpts.SkipFinalSnapshot = aws.Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] RDS Cluster delete options: %s", deleteOpts)
|
||||||
|
_, err := conn.DeleteDBCluster(&deleteOpts)
|
||||||
|
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"deleting", "backing-up", "modifying"},
|
||||||
|
Target: "destroyed",
|
||||||
|
Refresh: resourceAwsRDSClusterStateRefreshFunc(d, meta),
|
||||||
|
Timeout: 5 * time.Minute,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait, catching any errors
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[WARN] Error deleting RDS Cluster (%s): %s", d.Id(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterStateRefreshFunc(
|
||||||
|
d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
|
||||||
|
DBClusterIdentifier: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if "DBClusterNotFoundFault" == awsErr.Code() {
|
||||||
|
return 42, "destroyed", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("[WARN] Error on retrieving DB Cluster (%s) when waiting: %s", d.Id(), err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var dbc *rds.DBCluster
|
||||||
|
|
||||||
|
for _, c := range resp.DBClusters {
|
||||||
|
if *c.DBClusterIdentifier == d.Id() {
|
||||||
|
dbc = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbc == nil {
|
||||||
|
return 42, "destroyed", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbc.Status != nil {
|
||||||
|
log.Printf("[DEBUG] DB Cluster status (%s): %s", d.Id(), *dbc.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbc, *dbc.Status, nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterInstance() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsRDSClusterInstanceCreate,
|
||||||
|
Read: resourceAwsRDSClusterInstanceRead,
|
||||||
|
Update: resourceAwsRDSClusterInstanceUpdate,
|
||||||
|
Delete: resourceAwsRDSClusterInstanceDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"identifier": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateRdsId,
|
||||||
|
},
|
||||||
|
|
||||||
|
"db_subnet_group_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"writer": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"cluster_identifier": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"endpoint": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"port": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"publicly_accessible": &schema.Schema{
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
Default: false,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance_class": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"tags": tagsSchema(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
tags := tagsFromMapRDS(d.Get("tags").(map[string]interface{}))
|
||||||
|
|
||||||
|
createOpts := &rds.CreateDBInstanceInput{
|
||||||
|
DBInstanceClass: aws.String(d.Get("instance_class").(string)),
|
||||||
|
DBClusterIdentifier: aws.String(d.Get("cluster_identifier").(string)),
|
||||||
|
Engine: aws.String("aurora"),
|
||||||
|
PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)),
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := d.Get("identifier").(string); v != "" {
|
||||||
|
createOpts.DBInstanceIdentifier = aws.String(v)
|
||||||
|
} else {
|
||||||
|
createOpts.DBInstanceIdentifier = aws.String(resource.UniqueId())
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := d.GetOk("db_subnet_group_name"); ok {
|
||||||
|
createOpts.DBSubnetGroupName = aws.String(attr.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating RDS DB Instance opts: %s", createOpts)
|
||||||
|
resp, err := conn.CreateDBInstance(createOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(*resp.DBInstance.DBInstanceIdentifier)
|
||||||
|
|
||||||
|
// reuse db_instance refresh func
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"creating", "backing-up", "modifying"},
|
||||||
|
Target: "available",
|
||||||
|
Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta),
|
||||||
|
Timeout: 40 * time.Minute,
|
||||||
|
MinTimeout: 10 * time.Second,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait, catching any errors
|
||||||
|
_, err = stateConf.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsRDSClusterInstanceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
db, err := resourceAwsDbInstanceRetrieve(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARN] Error on retrieving RDS Cluster Instance (%s): %s", d.Id(), err)
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retreive DB Cluster information, to determine if this Instance is a writer
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
|
||||||
|
DBClusterIdentifier: db.DBClusterIdentifier,
|
||||||
|
})
|
||||||
|
|
||||||
|
var dbc *rds.DBCluster
|
||||||
|
for _, c := range resp.DBClusters {
|
||||||
|
if *c.DBClusterIdentifier == *db.DBClusterIdentifier {
|
||||||
|
dbc = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dbc == nil {
|
||||||
|
return fmt.Errorf("[WARN] Error finding RDS Cluster (%s) for Cluster Instance (%s): %s",
|
||||||
|
*db.DBClusterIdentifier, *db.DBInstanceIdentifier, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range dbc.DBClusterMembers {
|
||||||
|
if *db.DBInstanceIdentifier == *m.DBInstanceIdentifier {
|
||||||
|
if *m.IsClusterWriter == true {
|
||||||
|
d.Set("writer", true)
|
||||||
|
} else {
|
||||||
|
d.Set("writer", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if db.Endpoint != nil {
|
||||||
|
d.Set("endpoint", db.Endpoint.Address)
|
||||||
|
d.Set("port", db.Endpoint.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("publicly_accessible", db.PubliclyAccessible)
|
||||||
|
|
||||||
|
// Fetch and save tags
|
||||||
|
arn, err := buildRDSARN(d, meta)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG] Error building ARN for RDS Cluster Instance (%s), not setting Tags", *db.DBInstanceIdentifier)
|
||||||
|
} else {
|
||||||
|
if err := saveTagsRDS(conn, d, arn); err != nil {
|
||||||
|
log.Printf("[WARN] Failed to save tags for RDS Cluster Instance (%s): %s", *db.DBClusterIdentifier, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
if arn, err := buildRDSARN(d, meta); err == nil {
|
||||||
|
if err := setTagsRDS(conn, d, arn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceAwsRDSClusterInstanceRead(d, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsRDSClusterInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).rdsconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] RDS Cluster Instance destroy: %v", d.Id())
|
||||||
|
|
||||||
|
opts := rds.DeleteDBInstanceInput{DBInstanceIdentifier: aws.String(d.Id())}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] RDS Cluster Instance destroy configuration: %s", opts)
|
||||||
|
if _, err := conn.DeleteDBInstance(&opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-uses db_instance refresh func
|
||||||
|
log.Println("[INFO] Waiting for RDS Cluster Instance to be destroyed")
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"modifying", "deleting"},
|
||||||
|
Target: "",
|
||||||
|
Refresh: resourceAwsDbInstanceStateRefreshFunc(d, meta),
|
||||||
|
Timeout: 40 * time.Minute,
|
||||||
|
MinTimeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSRDSClusterInstance_basic(t *testing.T) {
|
||||||
|
var v rds.DBInstance
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSClusterDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSClusterInstanceConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSClusterInstanceExists("aws_rds_cluster_instance.cluster_instances", &v),
|
||||||
|
testAccCheckAWSDBClusterInstanceAttributes(&v),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSClusterInstanceDestroy(s *terraform.State) error {
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_rds_cluster" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the Group
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).rdsconn
|
||||||
|
var err error
|
||||||
|
resp, err := conn.DescribeDBInstances(
|
||||||
|
&rds.DescribeDBInstancesInput{
|
||||||
|
DBInstanceIdentifier: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if len(resp.DBInstances) != 0 &&
|
||||||
|
*resp.DBInstances[0].DBInstanceIdentifier == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("DB Cluster Instance %s still exists", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nil if the Cluster Instance is already destroyed
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if awsErr.Code() == "DBInstanceNotFound" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSDBClusterInstanceAttributes(v *rds.DBInstance) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
|
||||||
|
if *v.Engine != "aurora" {
|
||||||
|
return fmt.Errorf("bad engine, expected \"aurora\": %#v", *v.Engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(*v.DBClusterIdentifier, "tf-aurora-cluster") {
|
||||||
|
return fmt.Errorf("Bad Cluster Identifier prefix:\nexpected: %s\ngot: %s", "tf-aurora-cluster", *v.DBClusterIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSClusterInstanceExists(n string, v *rds.DBInstance) 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().(*AWSClient).rdsconn
|
||||||
|
resp, err := conn.DescribeDBInstances(&rds.DescribeDBInstancesInput{
|
||||||
|
DBInstanceIdentifier: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range resp.DBInstances {
|
||||||
|
if *d.DBInstanceIdentifier == rs.Primary.ID {
|
||||||
|
*v = *d
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("DB Cluster (%s) not found", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some random to the name, to avoid collision
|
||||||
|
var testAccAWSClusterInstanceConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_rds_cluster" "default" {
|
||||||
|
cluster_identifier = "tf-aurora-cluster-test-%d"
|
||||||
|
availability_zones = ["us-west-2a","us-west-2b","us-west-2c"]
|
||||||
|
database_name = "mydb"
|
||||||
|
master_username = "foo"
|
||||||
|
master_password = "mustbeeightcharaters"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_rds_cluster_instance" "cluster_instances" {
|
||||||
|
identifier = "aurora-cluster-test-instance"
|
||||||
|
cluster_identifier = "${aws_rds_cluster.default.id}"
|
||||||
|
instance_class = "db.r3.large"
|
||||||
|
}
|
||||||
|
|
||||||
|
`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
|
|
@ -0,0 +1,108 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSRDSCluster_basic(t *testing.T) {
|
||||||
|
var v rds.DBCluster
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSClusterDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSClusterConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSClusterExists("aws_rds_cluster.default", &v),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSClusterDestroy(s *terraform.State) error {
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_rds_cluster" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find the Group
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).rdsconn
|
||||||
|
var err error
|
||||||
|
resp, err := conn.DescribeDBClusters(
|
||||||
|
&rds.DescribeDBClustersInput{
|
||||||
|
DBClusterIdentifier: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if len(resp.DBClusters) != 0 &&
|
||||||
|
*resp.DBClusters[0].DBClusterIdentifier == rs.Primary.ID {
|
||||||
|
return fmt.Errorf("DB Cluster %s still exists", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nil if the cluster is already destroyed
|
||||||
|
if awsErr, ok := err.(awserr.Error); ok {
|
||||||
|
if awsErr.Code() == "DBClusterNotFound" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSClusterExists(n string, v *rds.DBCluster) 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().(*AWSClient).rdsconn
|
||||||
|
resp, err := conn.DescribeDBClusters(&rds.DescribeDBClustersInput{
|
||||||
|
DBClusterIdentifier: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range resp.DBClusters {
|
||||||
|
if *c.DBClusterIdentifier == rs.Primary.ID {
|
||||||
|
*v = *c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("DB Cluster (%s) not found", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some random to the name, to avoid collision
|
||||||
|
var testAccAWSClusterConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_rds_cluster" "default" {
|
||||||
|
cluster_identifier = "tf-aurora-cluster-%d"
|
||||||
|
availability_zones = ["us-west-2a","us-west-2b","us-west-2c"]
|
||||||
|
database_name = "mydb"
|
||||||
|
master_username = "foo"
|
||||||
|
master_password = "mustbeeightcharaters"
|
||||||
|
}`, rand.New(rand.NewSource(time.Now().UnixNano())).Int())
|
|
@ -2,6 +2,7 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
@ -212,12 +213,16 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: re-enable this test.
|
|
||||||
// VPC Peering connections are prefixed with pcx
|
// VPC Peering connections are prefixed with pcx
|
||||||
// Right now there is no VPC Peering resource
|
// Right now there is no VPC Peering resource
|
||||||
func _TestAccAWSRouteTable_vpcPeering(t *testing.T) {
|
func TestAccAWSRouteTable_vpcPeering(t *testing.T) {
|
||||||
var v ec2.RouteTable
|
var v ec2.RouteTable
|
||||||
|
|
||||||
|
acctId := os.Getenv("TF_ACC_ID")
|
||||||
|
if acctId == "" && os.Getenv(resource.TestEnvVar) != "" {
|
||||||
|
t.Fatal("Error: Test TestAccAWSRouteTable_vpcPeering requires an Account ID in TF_ACC_ID ")
|
||||||
|
}
|
||||||
|
|
||||||
testCheck := func(*terraform.State) error {
|
testCheck := func(*terraform.State) error {
|
||||||
if len(v.Routes) != 2 {
|
if len(v.Routes) != 2 {
|
||||||
return fmt.Errorf("bad routes: %#v", v.Routes)
|
return fmt.Errorf("bad routes: %#v", v.Routes)
|
||||||
|
@ -243,7 +248,7 @@ func _TestAccAWSRouteTable_vpcPeering(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckRouteTableDestroy,
|
CheckDestroy: testAccCheckRouteTableDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccRouteTableVpcPeeringConfig,
|
Config: testAccRouteTableVpcPeeringConfig(acctId),
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckRouteTableExists(
|
testAccCheckRouteTableExists(
|
||||||
"aws_route_table.foo", &v),
|
"aws_route_table.foo", &v),
|
||||||
|
@ -395,11 +400,10 @@ resource "aws_route_table" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// TODO: re-enable this test.
|
|
||||||
// VPC Peering connections are prefixed with pcx
|
// VPC Peering connections are prefixed with pcx
|
||||||
// Right now there is no VPC Peering resource
|
// This test requires an ENV var, TF_ACC_ID, with a valid AWS Account ID
|
||||||
const testAccRouteTableVpcPeeringConfig = `
|
func testAccRouteTableVpcPeeringConfig(acc string) string {
|
||||||
resource "aws_vpc" "foo" {
|
cfg := `resource "aws_vpc" "foo" {
|
||||||
cidr_block = "10.1.0.0/16"
|
cidr_block = "10.1.0.0/16"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,15 +411,34 @@ resource "aws_internet_gateway" "foo" {
|
||||||
vpc_id = "${aws_vpc.foo.id}"
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc" "bar" {
|
||||||
|
cidr_block = "10.3.0.0/16"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_internet_gateway" "bar" {
|
||||||
|
vpc_id = "${aws_vpc.bar.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_vpc_peering_connection" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
peer_vpc_id = "${aws_vpc.bar.id}"
|
||||||
|
peer_owner_id = "%s"
|
||||||
|
tags {
|
||||||
|
foo = "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resource "aws_route_table" "foo" {
|
resource "aws_route_table" "foo" {
|
||||||
vpc_id = "${aws_vpc.foo.id}"
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
|
||||||
route {
|
route {
|
||||||
cidr_block = "10.2.0.0/16"
|
cidr_block = "10.2.0.0/16"
|
||||||
vpc_peering_connection_id = "pcx-12345"
|
vpc_peering_connection_id = "${aws_vpc_peering_connection.foo.id}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
return fmt.Sprintf(cfg, acc)
|
||||||
|
}
|
||||||
|
|
||||||
const testAccRouteTableVgwRoutePropagationConfig = `
|
const testAccRouteTableVgwRoutePropagationConfig = `
|
||||||
resource "aws_vpc" "foo" {
|
resource "aws_vpc" "foo" {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package aws
|
package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -63,9 +65,17 @@ func resourceAwsS3BucketObject() *schema.Resource {
|
||||||
},
|
},
|
||||||
|
|
||||||
"source": &schema.Schema{
|
"source": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
ConflictsWith: []string{"content"},
|
||||||
|
},
|
||||||
|
|
||||||
|
"content": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ConflictsWith: []string{"source"},
|
||||||
},
|
},
|
||||||
|
|
||||||
"etag": &schema.Schema{
|
"etag": &schema.Schema{
|
||||||
|
@ -81,17 +91,27 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
bucket := d.Get("bucket").(string)
|
bucket := d.Get("bucket").(string)
|
||||||
key := d.Get("key").(string)
|
key := d.Get("key").(string)
|
||||||
|
var body io.ReadSeeker
|
||||||
|
|
||||||
source := d.Get("source").(string)
|
if v, ok := d.GetOk("source"); ok {
|
||||||
file, err := os.Open(source)
|
source := v.(string)
|
||||||
|
file, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error opening S3 bucket object source (%s): %s", source, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
body = file
|
||||||
return fmt.Errorf("Error opening S3 bucket object source (%s): %s", source, err)
|
} else if v, ok := d.GetOk("content"); ok {
|
||||||
|
content := v.(string)
|
||||||
|
body = bytes.NewReader([]byte(content))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return fmt.Errorf("Must specify \"source\" or \"content\" field")
|
||||||
}
|
}
|
||||||
putInput := &s3.PutObjectInput{
|
putInput := &s3.PutObjectInput{
|
||||||
Bucket: aws.String(bucket),
|
Bucket: aws.String(bucket),
|
||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
Body: file,
|
Body: body,
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := d.GetOk("cache_control"); ok {
|
if v, ok := d.GetOk("cache_control"); ok {
|
||||||
|
@ -115,7 +135,6 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := s3conn.PutObject(putInput)
|
resp, err := s3conn.PutObject(putInput)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error putting object in S3 bucket (%s): %s", bucket, err)
|
return fmt.Errorf("Error putting object in S3 bucket (%s): %s", bucket, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
var tf, err = ioutil.TempFile("", "tf")
|
var tf, err = ioutil.TempFile("", "tf")
|
||||||
|
|
||||||
func TestAccAWSS3BucketObject_basic(t *testing.T) {
|
func TestAccAWSS3BucketObject_source(t *testing.T) {
|
||||||
// first write some data to the tempfile just so it's not 0 bytes.
|
// first write some data to the tempfile just so it's not 0 bytes.
|
||||||
ioutil.WriteFile(tf.Name(), []byte("{anything will do }"), 0644)
|
ioutil.WriteFile(tf.Name(), []byte("{anything will do }"), 0644)
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
|
@ -29,7 +29,26 @@ func TestAccAWSS3BucketObject_basic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
|
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAWSS3BucketObjectConfig,
|
Config: testAccAWSS3BucketObjectConfigSource,
|
||||||
|
Check: testAccCheckAWSS3BucketObjectExists("aws_s3_bucket_object.object"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccAWSS3BucketObject_content(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testAccPreCheck(t)
|
||||||
|
},
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSS3BucketObjectConfigContent,
|
||||||
Check: testAccCheckAWSS3BucketObjectExists("aws_s3_bucket_object.object"),
|
Check: testAccCheckAWSS3BucketObjectExists("aws_s3_bucket_object.object"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -111,11 +130,10 @@ func testAccCheckAWSS3BucketObjectExists(n string) resource.TestCheckFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
var randomBucket = randInt
|
var randomBucket = randInt
|
||||||
var testAccAWSS3BucketObjectConfig = fmt.Sprintf(`
|
var testAccAWSS3BucketObjectConfigSource = fmt.Sprintf(`
|
||||||
resource "aws_s3_bucket" "object_bucket" {
|
resource "aws_s3_bucket" "object_bucket" {
|
||||||
bucket = "tf-object-test-bucket-%d"
|
bucket = "tf-object-test-bucket-%d"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_s3_bucket_object" "object" {
|
resource "aws_s3_bucket_object" "object" {
|
||||||
bucket = "${aws_s3_bucket.object_bucket.bucket}"
|
bucket = "${aws_s3_bucket.object_bucket.bucket}"
|
||||||
key = "test-key"
|
key = "test-key"
|
||||||
|
@ -135,6 +153,16 @@ resource "aws_s3_bucket_object" "object" {
|
||||||
source = "%s"
|
source = "%s"
|
||||||
content_language = "en"
|
content_language = "en"
|
||||||
content_type = "binary/octet-stream"
|
content_type = "binary/octet-stream"
|
||||||
|
|
||||||
}
|
}
|
||||||
`, randomBucket, tf.Name())
|
`, randomBucket, tf.Name())
|
||||||
|
|
||||||
|
var testAccAWSS3BucketObjectConfigContent = fmt.Sprintf(`
|
||||||
|
resource "aws_s3_bucket" "object_bucket" {
|
||||||
|
bucket = "tf-object-test-bucket-%d"
|
||||||
|
}
|
||||||
|
resource "aws_s3_bucket_object" "object" {
|
||||||
|
bucket = "${aws_s3_bucket.object_bucket.bucket}"
|
||||||
|
key = "test-key"
|
||||||
|
content = "some_bucket_content"
|
||||||
|
}
|
||||||
|
`, randomBucket)
|
||||||
|
|
|
@ -17,8 +17,6 @@ func resourceAwsVpnConnectionRoute() *schema.Resource {
|
||||||
// You can't update a route. You can just delete one and make
|
// You can't update a route. You can just delete one and make
|
||||||
// a new one.
|
// a new one.
|
||||||
Create: resourceAwsVpnConnectionRouteCreate,
|
Create: resourceAwsVpnConnectionRouteCreate,
|
||||||
Update: resourceAwsVpnConnectionRouteCreate,
|
|
||||||
|
|
||||||
Read: resourceAwsVpnConnectionRouteRead,
|
Read: resourceAwsVpnConnectionRouteRead,
|
||||||
Delete: resourceAwsVpnConnectionRouteDelete,
|
Delete: resourceAwsVpnConnectionRouteDelete,
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,16 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/directoryservice"
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
"github.com/aws/aws-sdk-go/service/ecs"
|
"github.com/aws/aws-sdk-go/service/ecs"
|
||||||
"github.com/aws/aws-sdk-go/service/elasticache"
|
"github.com/aws/aws-sdk-go/service/elasticache"
|
||||||
|
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
|
||||||
"github.com/aws/aws-sdk-go/service/elb"
|
"github.com/aws/aws-sdk-go/service/elb"
|
||||||
"github.com/aws/aws-sdk-go/service/rds"
|
"github.com/aws/aws-sdk-go/service/rds"
|
||||||
"github.com/aws/aws-sdk-go/service/route53"
|
"github.com/aws/aws-sdk-go/service/route53"
|
||||||
|
@ -368,7 +371,7 @@ func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]in
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes the result of flatmap.Expand for an array of strings
|
// Takes the result of flatmap.Expand for an array of strings
|
||||||
// and returns a []string
|
// and returns a []*string
|
||||||
func expandStringList(configured []interface{}) []*string {
|
func expandStringList(configured []interface{}) []*string {
|
||||||
vs := make([]*string, 0, len(configured))
|
vs := make([]*string, 0, len(configured))
|
||||||
for _, v := range configured {
|
for _, v := range configured {
|
||||||
|
@ -377,6 +380,17 @@ func expandStringList(configured []interface{}) []*string {
|
||||||
return vs
|
return vs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Takes list of pointers to strings. Expand to an array
|
||||||
|
// of raw strings and returns a []interface{}
|
||||||
|
// to keep compatibility w/ schema.NewSetschema.NewSet
|
||||||
|
func flattenStringList(list []*string) []interface{} {
|
||||||
|
vs := make([]interface{}, 0, len(list))
|
||||||
|
for _, v := range list {
|
||||||
|
vs = append(vs, *v)
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
//Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0"
|
//Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0"
|
||||||
func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string {
|
func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string {
|
||||||
ips := make([]string, 0, len(dtos))
|
ips := make([]string, 0, len(dtos))
|
||||||
|
@ -446,3 +460,144 @@ func expandResourceRecords(recs []interface{}, typeStr string) []*route53.Resour
|
||||||
}
|
}
|
||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
|
||||||
|
value := v.(string)
|
||||||
|
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
|
||||||
|
}
|
||||||
|
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"first character of %q must be a letter", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`--`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot contain two consecutive hyphens", k))
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`-$`).MatchString(value) {
|
||||||
|
errors = append(errors, fmt.Errorf(
|
||||||
|
"%q cannot end with a hyphen", k))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig {
|
||||||
|
config := elasticsearch.ElasticsearchClusterConfig{}
|
||||||
|
|
||||||
|
if v, ok := m["dedicated_master_enabled"]; ok {
|
||||||
|
isEnabled := v.(bool)
|
||||||
|
config.DedicatedMasterEnabled = aws.Bool(isEnabled)
|
||||||
|
|
||||||
|
if isEnabled {
|
||||||
|
if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 {
|
||||||
|
config.DedicatedMasterCount = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" {
|
||||||
|
config.DedicatedMasterType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := m["instance_count"]; ok {
|
||||||
|
config.InstanceCount = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
if v, ok := m["instance_type"]; ok {
|
||||||
|
config.InstanceType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := m["zone_awareness_enabled"]; ok {
|
||||||
|
config.ZoneAwarenessEnabled = aws.Bool(v.(bool))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} {
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
|
||||||
|
if c.DedicatedMasterCount != nil {
|
||||||
|
m["dedicated_master_count"] = *c.DedicatedMasterCount
|
||||||
|
}
|
||||||
|
if c.DedicatedMasterEnabled != nil {
|
||||||
|
m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled
|
||||||
|
}
|
||||||
|
if c.DedicatedMasterType != nil {
|
||||||
|
m["dedicated_master_type"] = *c.DedicatedMasterType
|
||||||
|
}
|
||||||
|
if c.InstanceCount != nil {
|
||||||
|
m["instance_count"] = *c.InstanceCount
|
||||||
|
}
|
||||||
|
if c.InstanceType != nil {
|
||||||
|
m["instance_type"] = *c.InstanceType
|
||||||
|
}
|
||||||
|
if c.ZoneAwarenessEnabled != nil {
|
||||||
|
m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return []map[string]interface{}{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} {
|
||||||
|
m := map[string]interface{}{}
|
||||||
|
|
||||||
|
if o.EBSEnabled != nil {
|
||||||
|
m["ebs_enabled"] = *o.EBSEnabled
|
||||||
|
}
|
||||||
|
if o.Iops != nil {
|
||||||
|
m["iops"] = *o.Iops
|
||||||
|
}
|
||||||
|
if o.VolumeSize != nil {
|
||||||
|
m["volume_size"] = *o.VolumeSize
|
||||||
|
}
|
||||||
|
if o.VolumeType != nil {
|
||||||
|
m["volume_type"] = *o.VolumeType
|
||||||
|
}
|
||||||
|
|
||||||
|
return []map[string]interface{}{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions {
|
||||||
|
options := elasticsearch.EBSOptions{}
|
||||||
|
|
||||||
|
if v, ok := m["ebs_enabled"]; ok {
|
||||||
|
options.EBSEnabled = aws.Bool(v.(bool))
|
||||||
|
}
|
||||||
|
if v, ok := m["iops"]; ok && v.(int) > 0 {
|
||||||
|
options.Iops = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
if v, ok := m["volume_size"]; ok && v.(int) > 0 {
|
||||||
|
options.VolumeSize = aws.Int64(int64(v.(int)))
|
||||||
|
}
|
||||||
|
if v, ok := m["volume_type"]; ok && v.(string) != "" {
|
||||||
|
options.VolumeType = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &options
|
||||||
|
}
|
||||||
|
|
||||||
|
func pointersMapToStringList(pointers map[string]*string) map[string]interface{} {
|
||||||
|
list := make(map[string]interface{}, len(pointers))
|
||||||
|
for i, v := range pointers {
|
||||||
|
list[i] = *v
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringMapToPointers(m map[string]interface{}) map[string]*string {
|
||||||
|
list := make(map[string]*string, len(m))
|
||||||
|
for i, v := range m {
|
||||||
|
list[i] = aws.String(v.(string))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func flattenDSVpcSettings(
|
||||||
|
s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} {
|
||||||
|
settings := make(map[string]interface{}, 0)
|
||||||
|
|
||||||
|
settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds))
|
||||||
|
settings["vpc_id"] = *s.VpcId
|
||||||
|
|
||||||
|
return []map[string]interface{}{settings}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setTags is a helper to set the tags for a resource. It expects the
|
||||||
|
// tags field to be named "tags"
|
||||||
|
func setTagsEFS(conn *efs.EFS, d *schema.ResourceData) error {
|
||||||
|
if d.HasChange("tags") {
|
||||||
|
oraw, nraw := d.GetChange("tags")
|
||||||
|
o := oraw.(map[string]interface{})
|
||||||
|
n := nraw.(map[string]interface{})
|
||||||
|
create, remove := diffTagsEFS(tagsFromMapEFS(o), tagsFromMapEFS(n))
|
||||||
|
|
||||||
|
// Set tags
|
||||||
|
if len(remove) > 0 {
|
||||||
|
log.Printf("[DEBUG] Removing tags: %#v", remove)
|
||||||
|
k := make([]*string, 0, len(remove))
|
||||||
|
for _, t := range remove {
|
||||||
|
k = append(k, t.Key)
|
||||||
|
}
|
||||||
|
_, err := conn.DeleteTags(&efs.DeleteTagsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
TagKeys: k,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(create) > 0 {
|
||||||
|
log.Printf("[DEBUG] Creating tags: %#v", create)
|
||||||
|
_, err := conn.CreateTags(&efs.CreateTagsInput{
|
||||||
|
FileSystemId: aws.String(d.Id()),
|
||||||
|
Tags: create,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffTags takes our tags locally and the ones remotely and returns
|
||||||
|
// the set of tags that must be created, and the set of tags that must
|
||||||
|
// be destroyed.
|
||||||
|
func diffTagsEFS(oldTags, newTags []*efs.Tag) ([]*efs.Tag, []*efs.Tag) {
|
||||||
|
// First, we're creating everything we have
|
||||||
|
create := make(map[string]interface{})
|
||||||
|
for _, t := range newTags {
|
||||||
|
create[*t.Key] = *t.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the list of what to remove
|
||||||
|
var remove []*efs.Tag
|
||||||
|
for _, t := range oldTags {
|
||||||
|
old, ok := create[*t.Key]
|
||||||
|
if !ok || old != *t.Value {
|
||||||
|
// Delete it!
|
||||||
|
remove = append(remove, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagsFromMapEFS(create), remove
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagsFromMap returns the tags for the given map of data.
|
||||||
|
func tagsFromMapEFS(m map[string]interface{}) []*efs.Tag {
|
||||||
|
var result []*efs.Tag
|
||||||
|
for k, v := range m {
|
||||||
|
result = append(result, &efs.Tag{
|
||||||
|
Key: aws.String(k),
|
||||||
|
Value: aws.String(v.(string)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// tagsToMap turns the list of tags into a map.
|
||||||
|
func tagsToMapEFS(ts []*efs.Tag) map[string]string {
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, t := range ts {
|
||||||
|
result[*t.Key] = *t.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/efs"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiffEFSTags(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Old, New map[string]interface{}
|
||||||
|
Create, Remove map[string]string
|
||||||
|
}{
|
||||||
|
// Basic add/remove
|
||||||
|
{
|
||||||
|
Old: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
New: map[string]interface{}{
|
||||||
|
"bar": "baz",
|
||||||
|
},
|
||||||
|
Create: map[string]string{
|
||||||
|
"bar": "baz",
|
||||||
|
},
|
||||||
|
Remove: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Modify
|
||||||
|
{
|
||||||
|
Old: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
New: map[string]interface{}{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
Create: map[string]string{
|
||||||
|
"foo": "baz",
|
||||||
|
},
|
||||||
|
Remove: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
c, r := diffTagsEFS(tagsFromMapEFS(tc.Old), tagsFromMapEFS(tc.New))
|
||||||
|
cm := tagsToMapEFS(c)
|
||||||
|
rm := tagsToMapEFS(r)
|
||||||
|
if !reflect.DeepEqual(cm, tc.Create) {
|
||||||
|
t.Fatalf("%d: bad create: %#v", i, cm)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(rm, tc.Remove) {
|
||||||
|
t.Fatalf("%d: bad remove: %#v", i, rm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testAccCheckTags can be used to check the tags on a resource.
|
||||||
|
func testAccCheckEFSTags(
|
||||||
|
ts *[]*efs.Tag, key string, value string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
m := tagsToMapEFS(*ts)
|
||||||
|
v, ok := m[key]
|
||||||
|
if value != "" && !ok {
|
||||||
|
return fmt.Errorf("Missing tag: %s", key)
|
||||||
|
} else if value == "" && ok {
|
||||||
|
return fmt.Errorf("Extra tag: %s", key)
|
||||||
|
}
|
||||||
|
if value == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v != value {
|
||||||
|
return fmt.Errorf("%s: bad value: %s", key, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package aws
|
package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
@ -19,7 +20,7 @@ func setTagsRDS(conn *rds.RDS, d *schema.ResourceData, arn string) error {
|
||||||
|
|
||||||
// Set tags
|
// Set tags
|
||||||
if len(remove) > 0 {
|
if len(remove) > 0 {
|
||||||
log.Printf("[DEBUG] Removing tags: %#v", remove)
|
log.Printf("[DEBUG] Removing tags: %s", remove)
|
||||||
k := make([]*string, len(remove), len(remove))
|
k := make([]*string, len(remove), len(remove))
|
||||||
for i, t := range remove {
|
for i, t := range remove {
|
||||||
k[i] = t.Key
|
k[i] = t.Key
|
||||||
|
@ -34,7 +35,7 @@ func setTagsRDS(conn *rds.RDS, d *schema.ResourceData, arn string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(create) > 0 {
|
if len(create) > 0 {
|
||||||
log.Printf("[DEBUG] Creating tags: %#v", create)
|
log.Printf("[DEBUG] Creating tags: %s", create)
|
||||||
_, err := conn.AddTagsToResource(&rds.AddTagsToResourceInput{
|
_, err := conn.AddTagsToResource(&rds.AddTagsToResourceInput{
|
||||||
ResourceName: aws.String(arn),
|
ResourceName: aws.String(arn),
|
||||||
Tags: create,
|
Tags: create,
|
||||||
|
@ -93,3 +94,20 @@ func tagsToMapRDS(ts []*rds.Tag) map[string]string {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func saveTagsRDS(conn *rds.RDS, d *schema.ResourceData, arn string) error {
|
||||||
|
resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{
|
||||||
|
ResourceName: aws.String(arn),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[DEBUG] Error retreiving tags for ARN: %s", arn)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dt []*rds.Tag
|
||||||
|
if len(resp.TagList) > 0 {
|
||||||
|
dt = resp.TagList
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.Set("tags", tagsToMapRDS(dt))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://terraform2-dev-ed.my.salesforce.com" validUntil="2025-09-02T18:27:19.710Z">
|
||||||
|
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||||
|
<md:KeyDescriptor use="signing">
|
||||||
|
<ds:KeyInfo>
|
||||||
|
<ds:X509Data>
|
||||||
|
<ds:X509Certificate>MIIErDCCA5SgAwIBAgIOAU+PT8RBAAAAAHxJXEcwDQYJKoZIhvcNAQELBQAwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0EwHhcNMTUwOTAyMTgyNjUzWhcNMTcwOTAyMTIwMDAwWjCBkDEoMCYGA1UEAwwfU2VsZlNpZ25lZENlcnRfMDJTZXAyMDE1XzE4MjY1MzEYMBYGA1UECwwPMDBEMjQwMDAwMDBwQW9BMRcwFQYDVQQKDA5TYWxlc2ZvcmNlLmNvbTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzELMAkGA1UECAwCQ0ExDDAKBgNVBAYTA1VTQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJp/wTRr9n1IWJpkRTjNpep47OKJrD2E6rGbJ18TG2RxtIz+zCn2JwH2aP3TULh0r0hhcg/pecv51RRcG7O19DBBaTQ5+KuoICQyKZy07/yDXSiZontTwkEYs06ssTwTHUcRXbcwTKv16L7omt0MjIhTTGfvtLOYiPwyvKvzAHg4eNuAcli0duVM78UIBORtdmy9C9ZcMh8yRJo5aPBq85wsE3JXU58ytyZzCHTBLH+2xFQrjYnUSEW+FOEEpI7o33MVdFBvWWg1R17HkWzcve4C30lqOHqvxBzyESZ/N1mMlmSt8gPFyB+mUXY99StJDJpnytbY8DwSzMQUo/sOVB0CAwEAAaOCAQAwgf0wHQYDVR0OBBYEFByu1EQqRQS0bYQBKS9K5qwKi+6IMA8GA1UdEwEB/wQFMAMBAf8wgcoGA1UdIwSBwjCBv4AUHK7URCpFBLRthAEpL0rmrAqL7oihgZakgZMwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0GCDgFPj0/EQQAAAAB8SVxHMA0GCSqGSIb3DQEBCwUAA4IBAQA9O5o1tC71qJnkq+ABPo4A1aFKZVT/07GcBX4/wetcbYySL4Q2nR9pMgfPYYS1j+P2E3viPsQwPIWDUBwFkNsjjX5DSGEkLAioVGKRwJshRSCSynMcsVZbQkfBUiZXqhM0wzvoa/ALvGD+aSSb1m+x7lEpDYNwQKWaUW2VYcHWv9wjujMyy7dlj8E/jqM71mw7ThNl6k4+3RQ802dMa14txm8pkF0vZgfpV3tkqhBqtjBAicVCaveqr3r3iGqjvyilBgdY+0NR8szqzm7CD/Bkb22+/IgM/mXQuL9KHD/WADlSGmYKmG3SSahmcZxznYCnzcRNN9LVuXlz5cbljmBj</ds:X509Certificate>
|
||||||
|
</ds:X509Data>
|
||||||
|
</ds:KeyInfo>
|
||||||
|
</md:KeyDescriptor>
|
||||||
|
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
||||||
|
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://terraform2-dev-ed.my.salesforce.com/idp/endpoint/HttpPost"/>
|
||||||
|
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://terraform2-dev-ed.my.salesforce.com/idp/endpoint/HttpRedirect"/>
|
||||||
|
</md:IDPSSODescriptor>
|
||||||
|
</md:EntityDescriptor>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://terraform-dev-ed.my.salesforce.com" validUntil="2025-09-02T18:27:19.710Z">
|
||||||
|
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||||
|
<md:KeyDescriptor use="signing">
|
||||||
|
<ds:KeyInfo>
|
||||||
|
<ds:X509Data>
|
||||||
|
<ds:X509Certificate>MIIErDCCA5SgAwIBAgIOAU+PT8RBAAAAAHxJXEcwDQYJKoZIhvcNAQELBQAwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0EwHhcNMTUwOTAyMTgyNjUzWhcNMTcwOTAyMTIwMDAwWjCBkDEoMCYGA1UEAwwfU2VsZlNpZ25lZENlcnRfMDJTZXAyMDE1XzE4MjY1MzEYMBYGA1UECwwPMDBEMjQwMDAwMDBwQW9BMRcwFQYDVQQKDA5TYWxlc2ZvcmNlLmNvbTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzELMAkGA1UECAwCQ0ExDDAKBgNVBAYTA1VTQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJp/wTRr9n1IWJpkRTjNpep47OKJrD2E6rGbJ18TG2RxtIz+zCn2JwH2aP3TULh0r0hhcg/pecv51RRcG7O19DBBaTQ5+KuoICQyKZy07/yDXSiZontTwkEYs06ssTwTHUcRXbcwTKv16L7omt0MjIhTTGfvtLOYiPwyvKvzAHg4eNuAcli0duVM78UIBORtdmy9C9ZcMh8yRJo5aPBq85wsE3JXU58ytyZzCHTBLH+2xFQrjYnUSEW+FOEEpI7o33MVdFBvWWg1R17HkWzcve4C30lqOHqvxBzyESZ/N1mMlmSt8gPFyB+mUXY99StJDJpnytbY8DwSzMQUo/sOVB0CAwEAAaOCAQAwgf0wHQYDVR0OBBYEFByu1EQqRQS0bYQBKS9K5qwKi+6IMA8GA1UdEwEB/wQFMAMBAf8wgcoGA1UdIwSBwjCBv4AUHK7URCpFBLRthAEpL0rmrAqL7oihgZakgZMwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0GCDgFPj0/EQQAAAAB8SVxHMA0GCSqGSIb3DQEBCwUAA4IBAQA9O5o1tC71qJnkq+ABPo4A1aFKZVT/07GcBX4/wetcbYySL4Q2nR9pMgfPYYS1j+P2E3viPsQwPIWDUBwFkNsjjX5DSGEkLAioVGKRwJshRSCSynMcsVZbQkfBUiZXqhM0wzvoa/ALvGD+aSSb1m+x7lEpDYNwQKWaUW2VYcHWv9wjujMyy7dlj8E/jqM71mw7ThNl6k4+3RQ802dMa14txm8pkF0vZgfpV3tkqhBqtjBAicVCaveqr3r3iGqjvyilBgdY+0NR8szqzm7CD/Bkb22+/IgM/mXQuL9KHD/WADlSGmYKmG3SSahmcZxznYCnzcRNN9LVuXlz5cbljmBj</ds:X509Certificate>
|
||||||
|
</ds:X509Data>
|
||||||
|
</ds:KeyInfo>
|
||||||
|
</md:KeyDescriptor>
|
||||||
|
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
||||||
|
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://terraform-dev-ed.my.salesforce.com/idp/endpoint/HttpPost"/>
|
||||||
|
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://terraform-dev-ed.my.salesforce.com/idp/endpoint/HttpRedirect"/>
|
||||||
|
</md:IDPSSODescriptor>
|
||||||
|
</md:EntityDescriptor>
|
|
@ -297,15 +297,15 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error configuring %s for Windows: %s", name, err)
|
return fmt.Errorf("Error configuring %s for Windows: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if domain_name, ok := d.GetOk("domain_name"); ok {
|
if domain_name, ok := d.GetOk("domain_name"); ok {
|
||||||
err = vmutils.ConfigureWindowsToJoinDomain(
|
err = vmutils.ConfigureWindowsToJoinDomain(
|
||||||
&role,
|
&role,
|
||||||
d.Get("domain_username").(string),
|
d.Get("domain_username").(string),
|
||||||
d.Get("domain_password").(string),
|
d.Get("domain_password").(string),
|
||||||
domain_name.(string),
|
domain_name.(string),
|
||||||
d.Get("domain_ou").(string),
|
d.Get("domain_ou").(string),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error configuring %s for WindowsToJoinDomain: %s", name, err)
|
return fmt.Errorf("Error configuring %s for WindowsToJoinDomain: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ func resourceAzureStorageBlob() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceAzureStorageBlobCreate,
|
Create: resourceAzureStorageBlobCreate,
|
||||||
Read: resourceAzureStorageBlobRead,
|
Read: resourceAzureStorageBlobRead,
|
||||||
Update: resourceAzureStorageBlobUpdate,
|
|
||||||
Exists: resourceAzureStorageBlobExists,
|
Exists: resourceAzureStorageBlobExists,
|
||||||
Delete: resourceAzureStorageBlobDelete,
|
Delete: resourceAzureStorageBlobDelete,
|
||||||
|
|
||||||
|
@ -122,17 +121,6 @@ func resourceAzureStorageBlobRead(d *schema.ResourceData, meta interface{}) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceAzureStorageBlobUpdate does all the necessary API calls to
|
|
||||||
// update a blob on Azure.
|
|
||||||
func resourceAzureStorageBlobUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
// NOTE: although empty as most parameters have ForceNew set; this is
|
|
||||||
// still required in case of changes to the storage_service_key
|
|
||||||
|
|
||||||
// run the ExistsFunc beforehand to ensure the resource's existence nonetheless:
|
|
||||||
_, err := resourceAzureStorageBlobExists(d, meta)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourceAzureStorageBlobExists does all the necessary API calls to
|
// resourceAzureStorageBlobExists does all the necessary API calls to
|
||||||
// check for the existence of the blob on Azure.
|
// check for the existence of the blob on Azure.
|
||||||
func resourceAzureStorageBlobExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
func resourceAzureStorageBlobExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||||
|
|
|
@ -32,18 +32,18 @@ func testSetValueOnResourceData(t *testing.T) {
|
||||||
d := schema.ResourceData{}
|
d := schema.ResourceData{}
|
||||||
d.Set("id", "name")
|
d.Set("id", "name")
|
||||||
|
|
||||||
setValueOrUUID(&d, "id", "name", "54711781-274e-41b2-83c0-17194d0108f7")
|
setValueOrID(&d, "id", "name", "54711781-274e-41b2-83c0-17194d0108f7")
|
||||||
|
|
||||||
if d.Get("id").(string) != "name" {
|
if d.Get("id").(string) != "name" {
|
||||||
t.Fatal("err: 'id' does not match 'name'")
|
t.Fatal("err: 'id' does not match 'name'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetUUIDOnResourceData(t *testing.T) {
|
func testSetIDOnResourceData(t *testing.T) {
|
||||||
d := schema.ResourceData{}
|
d := schema.ResourceData{}
|
||||||
d.Set("id", "54711781-274e-41b2-83c0-17194d0108f7")
|
d.Set("id", "54711781-274e-41b2-83c0-17194d0108f7")
|
||||||
|
|
||||||
setValueOrUUID(&d, "id", "name", "54711781-274e-41b2-83c0-17194d0108f7")
|
setValueOrID(&d, "id", "name", "54711781-274e-41b2-83c0-17194d0108f7")
|
||||||
|
|
||||||
if d.Get("id").(string) != "54711781-274e-41b2-83c0-17194d0108f7" {
|
if d.Get("id").(string) != "54711781-274e-41b2-83c0-17194d0108f7" {
|
||||||
t.Fatal("err: 'id' doest not match '54711781-274e-41b2-83c0-17194d0108f7'")
|
t.Fatal("err: 'id' doest not match '54711781-274e-41b2-83c0-17194d0108f7'")
|
||||||
|
|
|
@ -80,12 +80,12 @@ func resourceCloudStackDiskCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
// Create a new parameter struct
|
// Create a new parameter struct
|
||||||
p := cs.Volume.NewCreateVolumeParams(name)
|
p := cs.Volume.NewCreateVolumeParams(name)
|
||||||
|
|
||||||
// Retrieve the disk_offering UUID
|
// Retrieve the disk_offering ID
|
||||||
diskofferingid, e := retrieveUUID(cs, "disk_offering", d.Get("disk_offering").(string))
|
diskofferingid, e := retrieveID(cs, "disk_offering", d.Get("disk_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
// Set the disk_offering UUID
|
// Set the disk_offering ID
|
||||||
p.SetDiskofferingid(diskofferingid)
|
p.SetDiskofferingid(diskofferingid)
|
||||||
|
|
||||||
if d.Get("size").(int) != 0 {
|
if d.Get("size").(int) != 0 {
|
||||||
|
@ -95,8 +95,8 @@ func resourceCloudStackDiskCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
// If there is a project supplied, we retrieve and set the project id
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
if project, ok := d.GetOk("project"); ok {
|
if project, ok := d.GetOk("project"); ok {
|
||||||
// Retrieve the project UUID
|
// Retrieve the project ID
|
||||||
projectid, e := retrieveUUID(cs, "project", project.(string))
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -104,8 +104,8 @@ func resourceCloudStackDiskCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
p.SetProjectid(projectid)
|
p.SetProjectid(projectid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the zone UUID
|
// Retrieve the zone ID
|
||||||
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
|
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ func resourceCloudStackDiskCreate(d *schema.ResourceData, meta interface{}) erro
|
||||||
return fmt.Errorf("Error creating the new disk %s: %s", name, err)
|
return fmt.Errorf("Error creating the new disk %s: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the volume UUID and partials
|
// Set the volume ID and partials
|
||||||
d.SetId(r.Id)
|
d.SetId(r.Id)
|
||||||
d.SetPartial("name")
|
d.SetPartial("name")
|
||||||
d.SetPartial("device")
|
d.SetPartial("device")
|
||||||
|
@ -160,9 +160,9 @@ func resourceCloudStackDiskRead(d *schema.ResourceData, meta interface{}) error
|
||||||
d.Set("attach", v.Attached != "") // If attached this will contain a timestamp when attached
|
d.Set("attach", v.Attached != "") // If attached this will contain a timestamp when attached
|
||||||
d.Set("size", int(v.Size/(1024*1024*1024))) // Needed to get GB's again
|
d.Set("size", int(v.Size/(1024*1024*1024))) // Needed to get GB's again
|
||||||
|
|
||||||
setValueOrUUID(d, "disk_offering", v.Diskofferingname, v.Diskofferingid)
|
setValueOrID(d, "disk_offering", v.Diskofferingname, v.Diskofferingid)
|
||||||
setValueOrUUID(d, "project", v.Project, v.Projectid)
|
setValueOrID(d, "project", v.Project, v.Projectid)
|
||||||
setValueOrUUID(d, "zone", v.Zonename, v.Zoneid)
|
setValueOrID(d, "zone", v.Zonename, v.Zoneid)
|
||||||
|
|
||||||
if v.Attached != "" {
|
if v.Attached != "" {
|
||||||
// Get the virtual machine details
|
// Get the virtual machine details
|
||||||
|
@ -184,7 +184,7 @@ func resourceCloudStackDiskRead(d *schema.ResourceData, meta interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("device", retrieveDeviceName(v.Deviceid, c.Name))
|
d.Set("device", retrieveDeviceName(v.Deviceid, c.Name))
|
||||||
setValueOrUUID(d, "virtual_machine", v.Vmname, v.Virtualmachineid)
|
setValueOrID(d, "virtual_machine", v.Vmname, v.Virtualmachineid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -205,13 +205,13 @@ func resourceCloudStackDiskUpdate(d *schema.ResourceData, meta interface{}) erro
|
||||||
// Create a new parameter struct
|
// Create a new parameter struct
|
||||||
p := cs.Volume.NewResizeVolumeParams(d.Id())
|
p := cs.Volume.NewResizeVolumeParams(d.Id())
|
||||||
|
|
||||||
// Retrieve the disk_offering UUID
|
// Retrieve the disk_offering ID
|
||||||
diskofferingid, e := retrieveUUID(cs, "disk_offering", d.Get("disk_offering").(string))
|
diskofferingid, e := retrieveID(cs, "disk_offering", d.Get("disk_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the disk_offering UUID
|
// Set the disk_offering ID
|
||||||
p.SetDiskofferingid(diskofferingid)
|
p.SetDiskofferingid(diskofferingid)
|
||||||
|
|
||||||
if d.Get("size").(int) != 0 {
|
if d.Get("size").(int) != 0 {
|
||||||
|
@ -228,7 +228,7 @@ func resourceCloudStackDiskUpdate(d *schema.ResourceData, meta interface{}) erro
|
||||||
return fmt.Errorf("Error changing disk offering/size for disk %s: %s", name, err)
|
return fmt.Errorf("Error changing disk offering/size for disk %s: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the volume UUID and set partials
|
// Update the volume ID and set partials
|
||||||
d.SetId(r.Id)
|
d.SetId(r.Id)
|
||||||
d.SetPartial("disk_offering")
|
d.SetPartial("disk_offering")
|
||||||
d.SetPartial("size")
|
d.SetPartial("size")
|
||||||
|
@ -278,7 +278,7 @@ func resourceCloudStackDiskDelete(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
// Delete the voluem
|
// Delete the voluem
|
||||||
if _, err := cs.Volume.DeleteVolume(p); err != nil {
|
if _, err := cs.Volume.DeleteVolume(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
@ -299,8 +299,8 @@ func resourceCloudStackDiskAttach(d *schema.ResourceData, meta interface{}) erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -341,13 +341,13 @@ func resourceCloudStackDiskDetach(d *schema.ResourceData, meta interface{}) erro
|
||||||
// Create a new parameter struct
|
// Create a new parameter struct
|
||||||
p := cs.Volume.NewDetachVolumeParams()
|
p := cs.Volume.NewDetachVolumeParams()
|
||||||
|
|
||||||
// Set the volume UUID
|
// Set the volume ID
|
||||||
p.SetId(d.Id())
|
p.SetId(d.Id())
|
||||||
|
|
||||||
// Detach the currently attached volume
|
// Detach the currently attached volume
|
||||||
if _, err := cs.Volume.DetachVolume(p); err != nil {
|
if _, err := cs.Volume.DetachVolume(p); err != nil {
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,8 @@ func resourceCloudStackEgressFirewallCreate(d *schema.ResourceData, meta interfa
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the network UUID
|
// Retrieve the network ID
|
||||||
networkid, e := retrieveUUID(cs, "network", d.Get("network").(string))
|
networkid, e := retrieveID(cs, "network", d.Get("network").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface
|
||||||
|
|
||||||
// Get the rule
|
// Get the rule
|
||||||
r, count, err := cs.Firewall.GetEgressFirewallRuleByID(id.(string))
|
r, count, err := cs.Firewall.GetEgressFirewallRuleByID(id.(string))
|
||||||
// If the count == 0, there is no object found for this UUID
|
// If the count == 0, there is no object found for this ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
delete(uuids, "icmp")
|
delete(uuids, "icmp")
|
||||||
|
@ -415,7 +415,7 @@ func resourceCloudStackEgressFirewallDeleteRule(
|
||||||
// Delete the rule
|
// Delete the rule
|
||||||
if _, err := cs.Firewall.DeleteEgressFirewallRule(p); err != nil {
|
if _, err := cs.Firewall.DeleteEgressFirewallRule(p); err != nil {
|
||||||
|
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", id.(string))) {
|
"or entity does not exist", id.(string))) {
|
||||||
|
|
|
@ -123,13 +123,13 @@ func testAccCheckCloudStackEgressFirewallRulesExist(n string) resource.TestCheck
|
||||||
return fmt.Errorf("No firewall ID is set")
|
return fmt.Errorf("No firewall ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||||
_, count, err := cs.Firewall.GetEgressFirewallRuleByID(uuid)
|
_, count, err := cs.Firewall.GetEgressFirewallRuleByID(id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -156,12 +156,12 @@ func testAccCheckCloudStackEgressFirewallDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No instance ID is set")
|
return fmt.Errorf("No instance ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err := cs.Firewall.GetEgressFirewallRuleByID(uuid)
|
_, _, err := cs.Firewall.GetEgressFirewallRuleByID(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Egress rule %s still exists", rs.Primary.ID)
|
return fmt.Errorf("Egress rule %s still exists", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,8 @@ func resourceCloudStackFirewallCreate(d *schema.ResourceData, meta interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the ipaddress UUID
|
// Retrieve the ipaddress ID
|
||||||
ipaddressid, e := retrieveUUID(cs, "ipaddress", d.Get("ipaddress").(string))
|
ipaddressid, e := retrieveID(cs, "ipaddress", d.Get("ipaddress").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ func resourceCloudStackFirewallRead(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
// Get the rule
|
// Get the rule
|
||||||
r, count, err := cs.Firewall.GetFirewallRuleByID(id.(string))
|
r, count, err := cs.Firewall.GetFirewallRuleByID(id.(string))
|
||||||
// If the count == 0, there is no object found for this UUID
|
// If the count == 0, there is no object found for this ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
delete(uuids, "icmp")
|
delete(uuids, "icmp")
|
||||||
|
@ -415,7 +415,7 @@ func resourceCloudStackFirewallDeleteRule(
|
||||||
// Delete the rule
|
// Delete the rule
|
||||||
if _, err := cs.Firewall.DeleteFirewallRule(p); err != nil {
|
if _, err := cs.Firewall.DeleteFirewallRule(p); err != nil {
|
||||||
|
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", id.(string))) {
|
"or entity does not exist", id.(string))) {
|
||||||
|
|
|
@ -110,13 +110,13 @@ func testAccCheckCloudStackFirewallRulesExist(n string) resource.TestCheckFunc {
|
||||||
return fmt.Errorf("No firewall ID is set")
|
return fmt.Errorf("No firewall ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||||
_, count, err := cs.Firewall.GetFirewallRuleByID(uuid)
|
_, count, err := cs.Firewall.GetFirewallRuleByID(id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -143,12 +143,12 @@ func testAccCheckCloudStackFirewallDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No instance ID is set")
|
return fmt.Errorf("No instance ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err := cs.Firewall.GetFirewallRuleByID(uuid)
|
_, _, err := cs.Firewall.GetFirewallRuleByID(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Firewall rule %s still exists", rs.Primary.ID)
|
return fmt.Errorf("Firewall rule %s still exists", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,14 +100,14 @@ func resourceCloudStackInstance() *schema.Resource {
|
||||||
func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the service_offering UUID
|
// Retrieve the service_offering ID
|
||||||
serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string))
|
serviceofferingid, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the zone UUID
|
// Retrieve the zone ID
|
||||||
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
|
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,8 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the template UUID
|
// Retrieve the template ID
|
||||||
templateid, e := retrieveTemplateUUID(cs, zone.Id, d.Get("template").(string))
|
templateid, e := retrieveTemplateID(cs, zone.Id, d.Get("template").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -139,8 +139,8 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
if zone.Networktype == "Advanced" {
|
if zone.Networktype == "Advanced" {
|
||||||
// Retrieve the network UUID
|
// Retrieve the network ID
|
||||||
networkid, e := retrieveUUID(cs, "network", d.Get("network").(string))
|
networkid, e := retrieveID(cs, "network", d.Get("network").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -155,8 +155,8 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
// If there is a project supplied, we retrieve and set the project id
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
if project, ok := d.GetOk("project"); ok {
|
if project, ok := d.GetOk("project"); ok {
|
||||||
// Retrieve the project UUID
|
// Retrieve the project ID
|
||||||
projectid, e := retrieveUUID(cs, "project", project.(string))
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -229,11 +229,11 @@ func resourceCloudStackInstanceRead(d *schema.ResourceData, meta interface{}) er
|
||||||
d.Set("ipaddress", vm.Nic[0].Ipaddress)
|
d.Set("ipaddress", vm.Nic[0].Ipaddress)
|
||||||
//NB cloudstack sometimes sends back the wrong keypair name, so dont update it
|
//NB cloudstack sometimes sends back the wrong keypair name, so dont update it
|
||||||
|
|
||||||
setValueOrUUID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid)
|
setValueOrID(d, "network", vm.Nic[0].Networkname, vm.Nic[0].Networkid)
|
||||||
setValueOrUUID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid)
|
setValueOrID(d, "service_offering", vm.Serviceofferingname, vm.Serviceofferingid)
|
||||||
setValueOrUUID(d, "template", vm.Templatename, vm.Templateid)
|
setValueOrID(d, "template", vm.Templatename, vm.Templateid)
|
||||||
setValueOrUUID(d, "project", vm.Project, vm.Projectid)
|
setValueOrID(d, "project", vm.Project, vm.Projectid)
|
||||||
setValueOrUUID(d, "zone", vm.Zonename, vm.Zoneid)
|
setValueOrID(d, "zone", vm.Zonename, vm.Zoneid)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -278,8 +278,8 @@ func resourceCloudStackInstanceUpdate(d *schema.ResourceData, meta interface{})
|
||||||
if d.HasChange("service_offering") {
|
if d.HasChange("service_offering") {
|
||||||
log.Printf("[DEBUG] Service offering changed for %s, starting update", name)
|
log.Printf("[DEBUG] Service offering changed for %s, starting update", name)
|
||||||
|
|
||||||
// Retrieve the service_offering UUID
|
// Retrieve the service_offering ID
|
||||||
serviceofferingid, e := retrieveUUID(cs, "service_offering", d.Get("service_offering").(string))
|
serviceofferingid, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ func resourceCloudStackInstanceDelete(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
log.Printf("[INFO] Destroying instance: %s", d.Get("name").(string))
|
log.Printf("[INFO] Destroying instance: %s", d.Get("name").(string))
|
||||||
if _, err := cs.VirtualMachine.DestroyVirtualMachine(p); err != nil {
|
if _, err := cs.VirtualMachine.DestroyVirtualMachine(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -53,8 +53,8 @@ func resourceCloudStackIPAddressCreate(d *schema.ResourceData, meta interface{})
|
||||||
p := cs.Address.NewAssociateIpAddressParams()
|
p := cs.Address.NewAssociateIpAddressParams()
|
||||||
|
|
||||||
if network, ok := d.GetOk("network"); ok {
|
if network, ok := d.GetOk("network"); ok {
|
||||||
// Retrieve the network UUID
|
// Retrieve the network ID
|
||||||
networkid, e := retrieveUUID(cs, "network", network.(string))
|
networkid, e := retrieveID(cs, "network", network.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ func resourceCloudStackIPAddressCreate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
if vpc, ok := d.GetOk("vpc"); ok {
|
if vpc, ok := d.GetOk("vpc"); ok {
|
||||||
// Retrieve the vpc UUID
|
// Retrieve the vpc ID
|
||||||
vpcid, e := retrieveUUID(cs, "vpc", vpc.(string))
|
vpcid, e := retrieveID(cs, "vpc", vpc.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -76,8 +76,8 @@ func resourceCloudStackIPAddressCreate(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
// If there is a project supplied, we retrieve and set the project id
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
if project, ok := d.GetOk("project"); ok {
|
if project, ok := d.GetOk("project"); ok {
|
||||||
// Retrieve the project UUID
|
// Retrieve the project ID
|
||||||
projectid, e := retrieveUUID(cs, "project", project.(string))
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "network", n.Name, f.Associatednetworkid)
|
setValueOrID(d, "network", n.Name, f.Associatednetworkid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := d.GetOk("vpc"); ok {
|
if _, ok := d.GetOk("vpc"); ok {
|
||||||
|
@ -132,10 +132,10 @@ func resourceCloudStackIPAddressRead(d *schema.ResourceData, meta interface{}) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "vpc", v.Name, f.Vpcid)
|
setValueOrID(d, "vpc", v.Name, f.Vpcid)
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "project", f.Project, f.Projectid)
|
setValueOrID(d, "project", f.Project, f.Projectid)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ func resourceCloudStackIPAddressDelete(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
// Disassociate the IP address
|
// Disassociate the IP address
|
||||||
if _, err := cs.Address.DisassociateIpAddress(p); err != nil {
|
if _, err := cs.Address.DisassociateIpAddress(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
@ -165,7 +165,7 @@ func verifyIPAddressParams(d *schema.ResourceData) error {
|
||||||
_, network := d.GetOk("network")
|
_, network := d.GetOk("network")
|
||||||
_, vpc := d.GetOk("vpc")
|
_, vpc := d.GetOk("vpc")
|
||||||
|
|
||||||
if (network && vpc) || (!network && !vpc) {
|
if network && vpc || !network && !vpc {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"You must supply a value for either (so not both) the 'network' or 'vpc' parameter")
|
"You must supply a value for either (so not both) the 'network' or 'vpc' parameter")
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,9 +89,9 @@ func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta inter
|
||||||
p.SetDescription(d.Get("name").(string))
|
p.SetDescription(d.Get("name").(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the network and the UUID
|
// Retrieve the network and the ID
|
||||||
if network, ok := d.GetOk("network"); ok {
|
if network, ok := d.GetOk("network"); ok {
|
||||||
networkid, e := retrieveUUID(cs, "network", network.(string))
|
networkid, e := retrieveID(cs, "network", network.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -100,8 +100,8 @@ func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta inter
|
||||||
p.SetNetworkid(networkid)
|
p.SetNetworkid(networkid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the ipaddress UUID
|
// Retrieve the ipaddress ID
|
||||||
ipaddressid, e := retrieveUUID(cs, "ipaddress", d.Get("ipaddress").(string))
|
ipaddressid, e := retrieveID(cs, "ipaddress", d.Get("ipaddress").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func resourceCloudStackLoadBalancerRuleCreate(d *schema.ResourceData, meta inter
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the load balancer rule UUID and set partials
|
// Set the load balancer rule ID and set partials
|
||||||
d.SetId(r.Id)
|
d.SetId(r.Id)
|
||||||
d.SetPartial("name")
|
d.SetPartial("name")
|
||||||
d.SetPartial("description")
|
d.SetPartial("description")
|
||||||
|
@ -163,7 +163,7 @@ func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interfa
|
||||||
d.Set("public_port", lb.Publicport)
|
d.Set("public_port", lb.Publicport)
|
||||||
d.Set("private_port", lb.Privateport)
|
d.Set("private_port", lb.Privateport)
|
||||||
|
|
||||||
setValueOrUUID(d, "ipaddress", lb.Publicip, lb.Publicipid)
|
setValueOrID(d, "ipaddress", lb.Publicip, lb.Publicipid)
|
||||||
|
|
||||||
// Only set network if user specified it to avoid spurious diffs
|
// Only set network if user specified it to avoid spurious diffs
|
||||||
if _, ok := d.GetOk("network"); ok {
|
if _, ok := d.GetOk("network"); ok {
|
||||||
|
@ -171,7 +171,7 @@ func resourceCloudStackLoadBalancerRuleRead(d *schema.ResourceData, meta interfa
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
setValueOrUUID(d, "network", network.Name, lb.Networkid)
|
setValueOrID(d, "network", network.Name, lb.Networkid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -229,7 +229,7 @@ func resourceCloudStackLoadBalancerRuleDelete(d *schema.ResourceData, meta inter
|
||||||
|
|
||||||
log.Printf("[INFO] Deleting load balancer rule: %s", d.Get("name").(string))
|
log.Printf("[INFO] Deleting load balancer rule: %s", d.Get("name").(string))
|
||||||
if _, err := cs.LoadBalancer.DeleteLoadBalancerRule(p); err != nil {
|
if _, err := cs.LoadBalancer.DeleteLoadBalancerRule(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if !strings.Contains(err.Error(), fmt.Sprintf(
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -223,12 +223,12 @@ func testAccCheckCloudStackLoadBalancerRuleDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No Loadbalancer rule ID is set")
|
return fmt.Errorf("No Loadbalancer rule ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, "uuid") {
|
if !strings.Contains(k, "uuid") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err := cs.LoadBalancer.GetLoadBalancerRuleByID(uuid)
|
_, _, err := cs.LoadBalancer.GetLoadBalancerRuleByID(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Loadbalancer rule %s still exists", rs.Primary.ID)
|
return fmt.Errorf("Loadbalancer rule %s still exists", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,14 +72,14 @@ func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) e
|
||||||
|
|
||||||
name := d.Get("name").(string)
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
// Retrieve the network_offering UUID
|
// Retrieve the network_offering ID
|
||||||
networkofferingid, e := retrieveUUID(cs, "network_offering", d.Get("network_offering").(string))
|
networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the zone UUID
|
// Retrieve the zone ID
|
||||||
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
|
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -108,27 +108,27 @@ func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) e
|
||||||
// Check is this network needs to be created in a VPC
|
// Check is this network needs to be created in a VPC
|
||||||
vpc := d.Get("vpc").(string)
|
vpc := d.Get("vpc").(string)
|
||||||
if vpc != "" {
|
if vpc != "" {
|
||||||
// Retrieve the vpc UUID
|
// Retrieve the vpc ID
|
||||||
vpcid, e := retrieveUUID(cs, "vpc", vpc)
|
vpcid, e := retrieveID(cs, "vpc", vpc)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the vpc UUID
|
// Set the vpc ID
|
||||||
p.SetVpcid(vpcid)
|
p.SetVpcid(vpcid)
|
||||||
|
|
||||||
// Since we're in a VPC, check if we want to assiciate an ACL list
|
// Since we're in a VPC, check if we want to assiciate an ACL list
|
||||||
aclid := d.Get("aclid").(string)
|
aclid := d.Get("aclid").(string)
|
||||||
if aclid != "" {
|
if aclid != "" {
|
||||||
// Set the acl UUID
|
// Set the acl ID
|
||||||
p.SetAclid(aclid)
|
p.SetAclid(aclid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a project supplied, we retrieve and set the project id
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
if project, ok := d.GetOk("project"); ok {
|
if project, ok := d.GetOk("project"); ok {
|
||||||
// Retrieve the project UUID
|
// Retrieve the project ID
|
||||||
projectid, e := retrieveUUID(cs, "project", project.(string))
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -167,9 +167,9 @@ func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) err
|
||||||
d.Set("display_text", n.Displaytext)
|
d.Set("display_text", n.Displaytext)
|
||||||
d.Set("cidr", n.Cidr)
|
d.Set("cidr", n.Cidr)
|
||||||
|
|
||||||
setValueOrUUID(d, "network_offering", n.Networkofferingname, n.Networkofferingid)
|
setValueOrID(d, "network_offering", n.Networkofferingname, n.Networkofferingid)
|
||||||
setValueOrUUID(d, "project", n.Project, n.Projectid)
|
setValueOrID(d, "project", n.Project, n.Projectid)
|
||||||
setValueOrUUID(d, "zone", n.Zonename, n.Zoneid)
|
setValueOrID(d, "zone", n.Zonename, n.Zoneid)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -200,8 +200,8 @@ func resourceCloudStackNetworkUpdate(d *schema.ResourceData, meta interface{}) e
|
||||||
|
|
||||||
// Check if the network offering is changed
|
// Check if the network offering is changed
|
||||||
if d.HasChange("network_offering") {
|
if d.HasChange("network_offering") {
|
||||||
// Retrieve the network_offering UUID
|
// Retrieve the network_offering ID
|
||||||
networkofferingid, e := retrieveUUID(cs, "network_offering", d.Get("network_offering").(string))
|
networkofferingid, e := retrieveID(cs, "network_offering", d.Get("network_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ func resourceCloudStackNetworkDelete(d *schema.ResourceData, meta interface{}) e
|
||||||
// Delete the network
|
// Delete the network
|
||||||
_, err := cs.Network.DeleteNetwork(p)
|
_, err := cs.Network.DeleteNetwork(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -43,8 +43,8 @@ func resourceCloudStackNetworkACLCreate(d *schema.ResourceData, meta interface{}
|
||||||
|
|
||||||
name := d.Get("name").(string)
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
// Retrieve the vpc UUID
|
// Retrieve the vpc ID
|
||||||
vpcid, e := retrieveUUID(cs, "vpc", d.Get("vpc").(string))
|
vpcid, e := retrieveID(cs, "vpc", d.Get("vpc").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func resourceCloudStackNetworkACLRead(d *schema.ResourceData, meta interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "vpc", v.Name, v.Id)
|
setValueOrID(d, "vpc", v.Name, v.Id)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ func resourceCloudStackNetworkACLDelete(d *schema.ResourceData, meta interface{}
|
||||||
// Delete the network ACL list
|
// Delete the network ACL list
|
||||||
_, err := cs.NetworkACL.DeleteNetworkACLList(p)
|
_, err := cs.NetworkACL.DeleteNetworkACLList(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -247,7 +247,7 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface
|
||||||
|
|
||||||
// Get the rule
|
// Get the rule
|
||||||
r, count, err := cs.NetworkACL.GetNetworkACLByID(id.(string))
|
r, count, err := cs.NetworkACL.GetNetworkACLByID(id.(string))
|
||||||
// If the count == 0, there is no object found for this UUID
|
// If the count == 0, there is no object found for this ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
delete(uuids, "icmp")
|
delete(uuids, "icmp")
|
||||||
|
@ -275,7 +275,7 @@ func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface
|
||||||
|
|
||||||
// Get the rule
|
// Get the rule
|
||||||
r, count, err := cs.NetworkACL.GetNetworkACLByID(id.(string))
|
r, count, err := cs.NetworkACL.GetNetworkACLByID(id.(string))
|
||||||
// If the count == 0, there is no object found for this UUID
|
// If the count == 0, there is no object found for this ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
delete(uuids, "all")
|
delete(uuids, "all")
|
||||||
|
@ -469,7 +469,7 @@ func resourceCloudStackNetworkACLRuleDeleteRule(
|
||||||
// Delete the rule
|
// Delete the rule
|
||||||
if _, err := cs.NetworkACL.DeleteNetworkACL(p); err != nil {
|
if _, err := cs.NetworkACL.DeleteNetworkACL(p); err != nil {
|
||||||
|
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", id.(string))) {
|
"or entity does not exist", id.(string))) {
|
||||||
|
|
|
@ -122,13 +122,13 @@ func testAccCheckCloudStackNetworkACLRulesExist(n string) resource.TestCheckFunc
|
||||||
return fmt.Errorf("No network ACL rule ID is set")
|
return fmt.Errorf("No network ACL rule ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||||
_, count, err := cs.NetworkACL.GetNetworkACLByID(uuid)
|
_, count, err := cs.NetworkACL.GetNetworkACLByID(id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,12 +155,12 @@ func testAccCheckCloudStackNetworkACLRuleDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No network ACL rule ID is set")
|
return fmt.Errorf("No network ACL rule ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
if !strings.Contains(k, ".uuids.") || strings.HasSuffix(k, ".uuids.#") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err := cs.NetworkACL.GetNetworkACLByID(uuid)
|
_, _, err := cs.NetworkACL.GetNetworkACLByID(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Network ACL rule %s still exists", rs.Primary.ID)
|
return fmt.Errorf("Network ACL rule %s still exists", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,14 @@ func resourceCloudStackNIC() *schema.Resource {
|
||||||
func resourceCloudStackNICCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackNICCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the network UUID
|
// Retrieve the network ID
|
||||||
networkid, e := retrieveUUID(cs, "network", d.Get("network").(string))
|
networkid, e := retrieveID(cs, "network", d.Get("network").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -103,8 +103,8 @@ func resourceCloudStackNICRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
for _, n := range vm.Nic {
|
for _, n := range vm.Nic {
|
||||||
if n.Id == d.Id() {
|
if n.Id == d.Id() {
|
||||||
d.Set("ipaddress", n.Ipaddress)
|
d.Set("ipaddress", n.Ipaddress)
|
||||||
setValueOrUUID(d, "network", n.Networkname, n.Networkid)
|
setValueOrID(d, "network", n.Networkname, n.Networkid)
|
||||||
setValueOrUUID(d, "virtual_machine", vm.Name, vm.Id)
|
setValueOrID(d, "virtual_machine", vm.Name, vm.Id)
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,8 @@ func resourceCloudStackNICRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
func resourceCloudStackNICDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackNICDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ func resourceCloudStackNICDelete(d *schema.ResourceData, meta interface{}) error
|
||||||
// Remove the NIC
|
// Remove the NIC
|
||||||
_, err := cs.VirtualMachine.RemoveNicFromVirtualMachine(p)
|
_, err := cs.VirtualMachine.RemoveNicFromVirtualMachine(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -72,8 +72,8 @@ func resourceCloudStackPortForward() *schema.Resource {
|
||||||
func resourceCloudStackPortForwardCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackPortForwardCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the ipaddress UUID
|
// Retrieve the ipaddress ID
|
||||||
ipaddressid, e := retrieveUUID(cs, "ipaddress", d.Get("ipaddress").(string))
|
ipaddressid, e := retrieveID(cs, "ipaddress", d.Get("ipaddress").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,8 @@ func resourceCloudStackPortForwardCreateForward(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", forward["virtual_machine"].(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", forward["virtual_machine"].(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
// Get the forward
|
// Get the forward
|
||||||
r, count, err := cs.Firewall.GetPortForwardingRuleByID(id.(string))
|
r, count, err := cs.Firewall.GetPortForwardingRuleByID(id.(string))
|
||||||
// If the count == 0, there is no object found for this UUID
|
// If the count == 0, there is no object found for this ID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
forward["uuid"] = ""
|
forward["uuid"] = ""
|
||||||
|
@ -192,7 +192,7 @@ func resourceCloudStackPortForwardRead(d *schema.ResourceData, meta interface{})
|
||||||
forward["private_port"] = privPort
|
forward["private_port"] = privPort
|
||||||
forward["public_port"] = pubPort
|
forward["public_port"] = pubPort
|
||||||
|
|
||||||
if isUUID(forward["virtual_machine"].(string)) {
|
if isID(forward["virtual_machine"].(string)) {
|
||||||
forward["virtual_machine"] = r.Virtualmachineid
|
forward["virtual_machine"] = r.Virtualmachineid
|
||||||
} else {
|
} else {
|
||||||
forward["virtual_machine"] = r.Virtualmachinename
|
forward["virtual_machine"] = r.Virtualmachinename
|
||||||
|
@ -317,7 +317,7 @@ func resourceCloudStackPortForwardDeleteForward(
|
||||||
|
|
||||||
// Delete the forward
|
// Delete the forward
|
||||||
if _, err := cs.Firewall.DeletePortForwardingRule(p); err != nil {
|
if _, err := cs.Firewall.DeletePortForwardingRule(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if !strings.Contains(err.Error(), fmt.Sprintf(
|
if !strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", forward["uuid"].(string))) {
|
"or entity does not exist", forward["uuid"].(string))) {
|
||||||
|
@ -325,6 +325,7 @@ func resourceCloudStackPortForwardDeleteForward(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empty the UUID of this rule
|
||||||
forward["uuid"] = ""
|
forward["uuid"] = ""
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -102,13 +102,13 @@ func testAccCheckCloudStackPortForwardsExist(n string) resource.TestCheckFunc {
|
||||||
return fmt.Errorf("No port forward ID is set")
|
return fmt.Errorf("No port forward ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, "uuid") {
|
if !strings.Contains(k, "uuid") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||||
_, count, err := cs.Firewall.GetPortForwardingRuleByID(uuid)
|
_, count, err := cs.Firewall.GetPortForwardingRuleByID(id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -135,12 +135,12 @@ func testAccCheckCloudStackPortForwardDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No port forward ID is set")
|
return fmt.Errorf("No port forward ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, uuid := range rs.Primary.Attributes {
|
for k, id := range rs.Primary.Attributes {
|
||||||
if !strings.Contains(k, "uuid") {
|
if !strings.Contains(k, "uuid") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err := cs.Firewall.GetPortForwardingRuleByID(uuid)
|
_, _, err := cs.Firewall.GetPortForwardingRuleByID(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Port forward %s still exists", rs.Primary.ID)
|
return fmt.Errorf("Port forward %s still exists", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,8 @@ func resourceCloudStackSecondaryIPAddressCreate(d *schema.ResourceData, meta int
|
||||||
|
|
||||||
nicid := d.Get("nicid").(string)
|
nicid := d.Get("nicid").(string)
|
||||||
if nicid == "" {
|
if nicid == "" {
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ func resourceCloudStackSecondaryIPAddressCreate(d *schema.ResourceData, meta int
|
||||||
func resourceCloudStackSecondaryIPAddressRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackSecondaryIPAddressRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
virtualmachineid, e := retrieveID(cs, "virtual_machine", d.Get("virtual_machine").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ func resourceCloudStackSecondaryIPAddressDelete(d *schema.ResourceData, meta int
|
||||||
|
|
||||||
log.Printf("[INFO] Removing secondary IP address: %s", d.Get("ipaddress").(string))
|
log.Printf("[INFO] Removing secondary IP address: %s", d.Get("ipaddress").(string))
|
||||||
if _, err := cs.Nic.RemoveIpFromNic(p); err != nil {
|
if _, err := cs.Nic.RemoveIpFromNic(p); err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -64,8 +64,8 @@ func testAccCheckCloudStackSecondaryIPAddressExists(
|
||||||
|
|
||||||
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(
|
virtualmachineid, e := retrieveID(
|
||||||
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
|
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
|
@ -136,8 +136,8 @@ func testAccCheckCloudStackSecondaryIPAddressDestroy(s *terraform.State) error {
|
||||||
return fmt.Errorf("No IP address ID is set")
|
return fmt.Errorf("No IP address ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the virtual_machine UUID
|
// Retrieve the virtual_machine ID
|
||||||
virtualmachineid, e := retrieveUUID(
|
virtualmachineid, e := retrieveID(
|
||||||
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
|
cs, "virtual_machine", rs.Primary.Attributes["virtual_machine"])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
|
|
|
@ -116,7 +116,7 @@ func resourceCloudStackSSHKeyPairDelete(d *schema.ResourceData, meta interface{}
|
||||||
// Remove the SSH Keypair
|
// Remove the SSH Keypair
|
||||||
_, err := cs.SSH.DeleteSSHKeyPair(p)
|
_, err := cs.SSH.DeleteSSHKeyPair(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"A key pair with name '%s' does not exist for account", d.Id())) {
|
"A key pair with name '%s' does not exist for account", d.Id())) {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -51,6 +51,12 @@ func resourceCloudStackTemplate() *schema.Resource {
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"project": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
"zone": &schema.Schema{
|
"zone": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -118,14 +124,14 @@ func resourceCloudStackTemplateCreate(d *schema.ResourceData, meta interface{})
|
||||||
displaytext = name
|
displaytext = name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the os_type UUID
|
// Retrieve the os_type ID
|
||||||
ostypeid, e := retrieveUUID(cs, "os_type", d.Get("os_type").(string))
|
ostypeid, e := retrieveID(cs, "os_type", d.Get("os_type").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the zone UUID
|
// Retrieve the zone ID
|
||||||
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
|
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -161,6 +167,17 @@ func resourceCloudStackTemplateCreate(d *schema.ResourceData, meta interface{})
|
||||||
p.SetPasswordenabled(v.(bool))
|
p.SetPasswordenabled(v.(bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
|
if project, ok := d.GetOk("project"); ok {
|
||||||
|
// Retrieve the project ID
|
||||||
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
|
if e != nil {
|
||||||
|
return e.Error()
|
||||||
|
}
|
||||||
|
// Set the default project ID
|
||||||
|
p.SetProjectid(projectid)
|
||||||
|
}
|
||||||
|
|
||||||
// Create the new template
|
// Create the new template
|
||||||
r, err := cs.Template.RegisterTemplate(p)
|
r, err := cs.Template.RegisterTemplate(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -219,8 +236,9 @@ func resourceCloudStackTemplateRead(d *schema.ResourceData, meta interface{}) er
|
||||||
d.Set("password_enabled", t.Passwordenabled)
|
d.Set("password_enabled", t.Passwordenabled)
|
||||||
d.Set("is_ready", t.Isready)
|
d.Set("is_ready", t.Isready)
|
||||||
|
|
||||||
setValueOrUUID(d, "os_type", t.Ostypename, t.Ostypeid)
|
setValueOrID(d, "os_type", t.Ostypename, t.Ostypeid)
|
||||||
setValueOrUUID(d, "zone", t.Zonename, t.Zoneid)
|
setValueOrID(d, "project", t.Project, t.Projectid)
|
||||||
|
setValueOrID(d, "zone", t.Zonename, t.Zoneid)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -249,7 +267,7 @@ func resourceCloudStackTemplateUpdate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("os_type") {
|
if d.HasChange("os_type") {
|
||||||
ostypeid, e := retrieveUUID(cs, "os_type", d.Get("os_type").(string))
|
ostypeid, e := retrieveID(cs, "os_type", d.Get("os_type").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -278,7 +296,7 @@ func resourceCloudStackTemplateDelete(d *schema.ResourceData, meta interface{})
|
||||||
log.Printf("[INFO] Deleting template: %s", d.Get("name").(string))
|
log.Printf("[INFO] Deleting template: %s", d.Get("name").(string))
|
||||||
_, err := cs.Template.DeleteTemplate(p)
|
_, err := cs.Template.DeleteTemplate(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -40,12 +40,23 @@ func resourceCloudStackVPC() *schema.Resource {
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"network_domain": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
|
||||||
"project": &schema.Schema{
|
"project": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"source_nat_ip": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
|
||||||
"zone": &schema.Schema{
|
"zone": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -60,14 +71,14 @@ func resourceCloudStackVPCCreate(d *schema.ResourceData, meta interface{}) error
|
||||||
|
|
||||||
name := d.Get("name").(string)
|
name := d.Get("name").(string)
|
||||||
|
|
||||||
// Retrieve the vpc_offering UUID
|
// Retrieve the vpc_offering ID
|
||||||
vpcofferingid, e := retrieveUUID(cs, "vpc_offering", d.Get("vpc_offering").(string))
|
vpcofferingid, e := retrieveID(cs, "vpc_offering", d.Get("vpc_offering").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the zone UUID
|
// Retrieve the zone ID
|
||||||
zoneid, e := retrieveUUID(cs, "zone", d.Get("zone").(string))
|
zoneid, e := retrieveID(cs, "zone", d.Get("zone").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -79,12 +90,24 @@ func resourceCloudStackVPCCreate(d *schema.ResourceData, meta interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new parameter struct
|
// Create a new parameter struct
|
||||||
p := cs.VPC.NewCreateVPCParams(d.Get("cidr").(string), displaytext.(string), name, vpcofferingid, zoneid)
|
p := cs.VPC.NewCreateVPCParams(
|
||||||
|
d.Get("cidr").(string),
|
||||||
|
displaytext.(string),
|
||||||
|
name,
|
||||||
|
vpcofferingid,
|
||||||
|
zoneid,
|
||||||
|
)
|
||||||
|
|
||||||
|
// If there is a network domain supplied, make sure to add it to the request
|
||||||
|
if networkDomain, ok := d.GetOk("network_domain"); ok {
|
||||||
|
// Set the network domain
|
||||||
|
p.SetNetworkdomain(networkDomain.(string))
|
||||||
|
}
|
||||||
|
|
||||||
// If there is a project supplied, we retrieve and set the project id
|
// If there is a project supplied, we retrieve and set the project id
|
||||||
if project, ok := d.GetOk("project"); ok {
|
if project, ok := d.GetOk("project"); ok {
|
||||||
// Retrieve the project UUID
|
// Retrieve the project ID
|
||||||
projectid, e := retrieveUUID(cs, "project", project.(string))
|
projectid, e := retrieveID(cs, "project", project.(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -122,6 +145,7 @@ func resourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.Set("name", v.Name)
|
d.Set("name", v.Name)
|
||||||
d.Set("display_text", v.Displaytext)
|
d.Set("display_text", v.Displaytext)
|
||||||
d.Set("cidr", v.Cidr)
|
d.Set("cidr", v.Cidr)
|
||||||
|
d.Set("network_domain", v.Networkdomain)
|
||||||
|
|
||||||
// Get the VPC offering details
|
// Get the VPC offering details
|
||||||
o, _, err := cs.VPC.GetVPCOfferingByID(v.Vpcofferingid)
|
o, _, err := cs.VPC.GetVPCOfferingByID(v.Vpcofferingid)
|
||||||
|
@ -129,9 +153,30 @@ func resourceCloudStackVPCRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "vpc_offering", o.Name, v.Vpcofferingid)
|
setValueOrID(d, "vpc_offering", o.Name, v.Vpcofferingid)
|
||||||
setValueOrUUID(d, "project", v.Project, v.Projectid)
|
setValueOrID(d, "project", v.Project, v.Projectid)
|
||||||
setValueOrUUID(d, "zone", v.Zonename, v.Zoneid)
|
setValueOrID(d, "zone", v.Zonename, v.Zoneid)
|
||||||
|
|
||||||
|
// Create a new parameter struct
|
||||||
|
p := cs.Address.NewListPublicIpAddressesParams()
|
||||||
|
p.SetVpcid(d.Id())
|
||||||
|
p.SetIssourcenat(true)
|
||||||
|
|
||||||
|
if _, ok := d.GetOk("project"); ok {
|
||||||
|
p.SetProjectid(v.Projectid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the source NAT IP assigned to the VPC
|
||||||
|
l, err := cs.Address.ListPublicIpAddresses(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.Count != 1 {
|
||||||
|
return fmt.Errorf("Unexpected number (%d) of source NAT IPs returned", l.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("source_nat_ip", l.PublicIpAddresses[0].Ipaddress)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -172,7 +217,7 @@ func resourceCloudStackVPCDelete(d *schema.ResourceData, meta interface{}) error
|
||||||
// Delete the VPC
|
// Delete the VPC
|
||||||
_, err := cs.VPC.DeleteVPC(p)
|
_, err := cs.VPC.DeleteVPC(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -76,6 +76,10 @@ func testAccCheckCloudStackVPCAttributes(
|
||||||
return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr)
|
return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vpc.Networkdomain != "terraform-domain" {
|
||||||
|
return fmt.Errorf("Bad network domain: %s", vpc.Networkdomain)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +111,7 @@ resource "cloudstack_vpc" "foo" {
|
||||||
display_text = "terraform-vpc-text"
|
display_text = "terraform-vpc-text"
|
||||||
cidr = "%s"
|
cidr = "%s"
|
||||||
vpc_offering = "%s"
|
vpc_offering = "%s"
|
||||||
|
network_domain = "terraform-domain"
|
||||||
zone = "%s"
|
zone = "%s"
|
||||||
}`,
|
}`,
|
||||||
CLOUDSTACK_VPC_CIDR_1,
|
CLOUDSTACK_VPC_CIDR_1,
|
||||||
|
|
|
@ -81,7 +81,7 @@ func resourceCloudStackVPNConnectionDelete(d *schema.ResourceData, meta interfac
|
||||||
// Delete the VPN Connection
|
// Delete the VPN Connection
|
||||||
_, err := cs.VPN.DeleteVpnConnection(p)
|
_, err := cs.VPN.DeleteVpnConnection(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -179,7 +179,7 @@ func resourceCloudStackVPNCustomerGatewayDelete(d *schema.ResourceData, meta int
|
||||||
// Delete the VPN Customer Gateway
|
// Delete the VPN Customer Gateway
|
||||||
_, err := cs.VPN.DeleteVpnCustomerGateway(p)
|
_, err := cs.VPN.DeleteVpnCustomerGateway(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -33,8 +33,8 @@ func resourceCloudStackVPNGateway() *schema.Resource {
|
||||||
func resourceCloudStackVPNGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceCloudStackVPNGatewayCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
cs := meta.(*cloudstack.CloudStackClient)
|
cs := meta.(*cloudstack.CloudStackClient)
|
||||||
|
|
||||||
// Retrieve the VPC UUID
|
// Retrieve the VPC ID
|
||||||
vpcid, e := retrieveUUID(cs, "vpc", d.Get("vpc").(string))
|
vpcid, e := retrieveID(cs, "vpc", d.Get("vpc").(string))
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.Error()
|
return e.Error()
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func resourceCloudStackVPNGatewayRead(d *schema.ResourceData, meta interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
setValueOrUUID(d, "vpc", d.Get("vpc").(string), v.Vpcid)
|
setValueOrID(d, "vpc", d.Get("vpc").(string), v.Vpcid)
|
||||||
|
|
||||||
d.Set("public_ip", v.Publicip)
|
d.Set("public_ip", v.Publicip)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func resourceCloudStackVPNGatewayDelete(d *schema.ResourceData, meta interface{}
|
||||||
// Delete the VPN Gateway
|
// Delete the VPN Gateway
|
||||||
_, err := cs.VPN.DeleteVpnGateway(p)
|
_, err := cs.VPN.DeleteVpnGateway(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is a very poor way to be told the UUID does no longer exist :(
|
// This is a very poor way to be told the ID does no longer exist :(
|
||||||
if strings.Contains(err.Error(), fmt.Sprintf(
|
if strings.Contains(err.Error(), fmt.Sprintf(
|
||||||
"Invalid parameter id value=%s due to incorrect long value format, "+
|
"Invalid parameter id value=%s due to incorrect long value format, "+
|
||||||
"or entity does not exist", d.Id())) {
|
"or entity does not exist", d.Id())) {
|
||||||
|
|
|
@ -10,6 +10,9 @@ import (
|
||||||
"github.com/xanzy/go-cloudstack/cloudstack"
|
"github.com/xanzy/go-cloudstack/cloudstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CloudStack uses a "special" ID of -1 to define an unlimited resource
|
||||||
|
const UnlimitedResourceID = "-1"
|
||||||
|
|
||||||
type retrieveError struct {
|
type retrieveError struct {
|
||||||
name string
|
name string
|
||||||
value string
|
value string
|
||||||
|
@ -17,43 +20,51 @@ type retrieveError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *retrieveError) Error() error {
|
func (e *retrieveError) Error() error {
|
||||||
return fmt.Errorf("Error retrieving UUID of %s %s: %s", e.name, e.value, e.err)
|
return fmt.Errorf("Error retrieving ID of %s %s: %s", e.name, e.value, e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setValueOrUUID(d *schema.ResourceData, key string, value string, uuid string) {
|
func setValueOrID(d *schema.ResourceData, key string, value string, id string) {
|
||||||
if isUUID(d.Get(key).(string)) {
|
if isID(d.Get(key).(string)) {
|
||||||
d.Set(key, uuid)
|
// If the given id is an empty string, check if the configured value matches
|
||||||
|
// the UnlimitedResourceID in which case we set id to UnlimitedResourceID
|
||||||
|
if id == "" && d.Get(key).(string) == UnlimitedResourceID {
|
||||||
|
id = UnlimitedResourceID
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set(key, id)
|
||||||
} else {
|
} else {
|
||||||
d.Set(key, value)
|
d.Set(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid string, e *retrieveError) {
|
func retrieveID(cs *cloudstack.CloudStackClient, name, value string) (id string, e *retrieveError) {
|
||||||
// If the supplied value isn't a UUID, try to retrieve the UUID ourselves
|
// If the supplied value isn't a ID, try to retrieve the ID ourselves
|
||||||
if isUUID(value) {
|
if isID(value) {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retrieving UUID of %s: %s", name, value)
|
log.Printf("[DEBUG] Retrieving ID of %s: %s", name, value)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch name {
|
switch name {
|
||||||
case "disk_offering":
|
case "disk_offering":
|
||||||
uuid, err = cs.DiskOffering.GetDiskOfferingID(value)
|
id, err = cs.DiskOffering.GetDiskOfferingID(value)
|
||||||
case "virtual_machine":
|
case "virtual_machine":
|
||||||
uuid, err = cs.VirtualMachine.GetVirtualMachineID(value)
|
id, err = cs.VirtualMachine.GetVirtualMachineID(value)
|
||||||
case "service_offering":
|
case "service_offering":
|
||||||
uuid, err = cs.ServiceOffering.GetServiceOfferingID(value)
|
id, err = cs.ServiceOffering.GetServiceOfferingID(value)
|
||||||
case "network_offering":
|
case "network_offering":
|
||||||
uuid, err = cs.NetworkOffering.GetNetworkOfferingID(value)
|
id, err = cs.NetworkOffering.GetNetworkOfferingID(value)
|
||||||
|
case "project":
|
||||||
|
id, err = cs.Project.GetProjectID(value)
|
||||||
case "vpc_offering":
|
case "vpc_offering":
|
||||||
uuid, err = cs.VPC.GetVPCOfferingID(value)
|
id, err = cs.VPC.GetVPCOfferingID(value)
|
||||||
case "vpc":
|
case "vpc":
|
||||||
uuid, err = cs.VPC.GetVPCID(value)
|
id, err = cs.VPC.GetVPCID(value)
|
||||||
case "network":
|
case "network":
|
||||||
uuid, err = cs.Network.GetNetworkID(value)
|
id, err = cs.Network.GetNetworkID(value)
|
||||||
case "zone":
|
case "zone":
|
||||||
uuid, err = cs.Zone.GetZoneID(value)
|
id, err = cs.Zone.GetZoneID(value)
|
||||||
case "ipaddress":
|
case "ipaddress":
|
||||||
p := cs.Address.NewListPublicIpAddressesParams()
|
p := cs.Address.NewListPublicIpAddressesParams()
|
||||||
p.SetIpaddress(value)
|
p.SetIpaddress(value)
|
||||||
|
@ -63,10 +74,10 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if l.Count == 1 {
|
if l.Count == 1 {
|
||||||
uuid = l.PublicIpAddresses[0].Id
|
id = l.PublicIpAddresses[0].Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("Could not find UUID of IP address: %s", value)
|
err = fmt.Errorf("Could not find ID of IP address: %s", value)
|
||||||
case "os_type":
|
case "os_type":
|
||||||
p := cs.GuestOS.NewListOsTypesParams()
|
p := cs.GuestOS.NewListOsTypesParams()
|
||||||
p.SetDescription(value)
|
p.SetDescription(value)
|
||||||
|
@ -76,43 +87,42 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if l.Count == 1 {
|
if l.Count == 1 {
|
||||||
uuid = l.OsTypes[0].Id
|
id = l.OsTypes[0].Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("Could not find UUID of OS Type: %s", value)
|
err = fmt.Errorf("Could not find ID of OS Type: %s", value)
|
||||||
case "project":
|
|
||||||
uuid, err = cs.Project.GetProjectID(value)
|
|
||||||
default:
|
default:
|
||||||
return uuid, &retrieveError{name: name, value: value,
|
return id, &retrieveError{name: name, value: value,
|
||||||
err: fmt.Errorf("Unknown request: %s", name)}
|
err: fmt.Errorf("Unknown request: %s", name)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uuid, &retrieveError{name: name, value: value, err: err}
|
return id, &retrieveError{name: name, value: value, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uuid, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveTemplateUUID(cs *cloudstack.CloudStackClient, zoneid, value string) (uuid string, e *retrieveError) {
|
func retrieveTemplateID(cs *cloudstack.CloudStackClient, zoneid, value string) (id string, e *retrieveError) {
|
||||||
// If the supplied value isn't a UUID, try to retrieve the UUID ourselves
|
// If the supplied value isn't a ID, try to retrieve the ID ourselves
|
||||||
if isUUID(value) {
|
if isID(value) {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retrieving UUID of template: %s", value)
|
log.Printf("[DEBUG] Retrieving ID of template: %s", value)
|
||||||
|
|
||||||
uuid, err := cs.Template.GetTemplateID(value, "executable", zoneid)
|
id, err := cs.Template.GetTemplateID(value, "executable", zoneid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return uuid, &retrieveError{name: "template", value: value, err: err}
|
return id, &retrieveError{name: "template", value: value, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uuid, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isUUID(s string) bool {
|
// ID can be either a UUID or a UnlimitedResourceID
|
||||||
re := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
|
func isID(id string) bool {
|
||||||
return re.MatchString(s)
|
re := regexp.MustCompile(`^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|-1)$`)
|
||||||
|
return re.MatchString(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RetryFunc is the function retried n times
|
// RetryFunc is the function retried n times
|
||||||
|
|
|
@ -39,6 +39,10 @@ func resourceDigitalOceanDroplet() *schema.Resource {
|
||||||
"size": &schema.Schema{
|
"size": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
StateFunc: func(val interface{}) string {
|
||||||
|
// DO API V2 size slug is always lowercase
|
||||||
|
return strings.ToLower(val.(string))
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
"status": &schema.Schema{
|
"status": &schema.Schema{
|
||||||
|
|
|
@ -148,7 +148,7 @@ func resourceDockerContainerRead(d *schema.ResourceData, meta interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
if container.State.Running ||
|
if container.State.Running ||
|
||||||
(!container.State.Running && !d.Get("must_run").(bool)) {
|
!container.State.Running && !d.Get("must_run").(bool) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue