Merge branch 'master' into gce_autoscaling

This commit is contained in:
Dave Cunningham 2015-07-27 20:48:39 -04:00
commit 5544dac7c6
64 changed files with 1646 additions and 367 deletions

View File

@ -1,31 +1,30 @@
## 0.6.1 (Unreleased)
## 0.6.2 (Unreleased)
IMPROVEMENTS:
* core: Add resource IDs to errors coming from `apply`/`refresh` [GH-2815]
* provider/aws: Validate credentials before walking the graph [GH-2730]
* provider/aws: Added website_domain for S3 buckets [GH-2210]
* provider/aws: ELB names are now optional, and generated by Terraform if omitted [GH-2571]
* provider/aws: Downcase RDS engine names to prevent continuous diffs [GH-2745]
* provider/aws: Added `source_dest_check` attribute to the aws_network_interface [GH-2741]
* provider/aws: Clean up externally removed Launch Configurations [GH-2806]
BUG FIXES:
* core: Prevent error duplication in `apply` [GH-2815]
* provider/aws: Fix issue with toggling monitoring in AWS Instances [GH-2794]
* provider/aws: Fix issue with Spot Instance Requests and cancellation [GH-2805]
* provider/aws: Fixx issue when unable to find a Root Block Device name of an Instance Backed
AMI [GH-2646]
## 0.6.1 (July 20, 2015)
FEATURES:
* **New resource: `google_container_cluster`** [GH-2357]
* **New resource: `aws_vpc_endpoint`** [GH-2695]
BUG FIXES:
* core: don't prompt for variables with defaults [GH-2613]
* core: Return correct number of planned updates [GH-2620]
* core: Fix "provider not found" error that can occur while running
a destroy plan with grandchildren modules [GH-2755]
* connection/ssh: fix issue on machines with an SSH Agent available
preventing `key_file` from being read without explicitly
setting `agent = false` [GH-2615]
* provider/aws: Allow uppercase characters in `aws_elb.name` [GH-2580]
* provider/aws: Allow underscores in `aws_db_subnet_group.name` (undocumented by AWS) [GH-2604]
* provider/aws: Allow dots in `aws_db_subnet_group.name` (undocumented by AWS) [GH-2665]
* provider/aws: Fix issue with pending Spot Instance requests [GH-2640]
* provider/aws: Fix issue in AWS Classic environment with referencing external
Security Groups [GH-2644]
* provider/aws: Bump internet gateway detach timeout [GH-2669]
* provider/aws: `ecs_cluster` rename (recreation) and deletion is handled correctly [GH-2698]
* provider/aws: `aws_route_table` ignores routes generated for VPC endpoints [GH-2695]
* provider/aws: Fix issue with Launch Configurations and enable_monitoring [GH-2735]
* provider/openstack: allow empty api_key and endpoint_type [GH-2626]
IMPROVEMENTS:
* connection/ssh: Print SSH bastion host details to output [GH-2684]
@ -36,6 +35,35 @@ IMPROVEMENTS:
Auto Scaling Groups updates [GH-2724]
* provider/google: Add metadata_startup_script to google_compute_instance [GH-2375]
BUG FIXES:
* core: don't prompt for variables with defaults [GH-2613]
* core: Return correct number of planned updates [GH-2620]
* core: Fix "provider not found" error that can occur while running
a destroy plan with grandchildren modules [GH-2755]
* core: Fix UUID showing up in diff for computed splat (`foo.*.bar`)
variables. [GH-2788]
* core: Orphan modules that contain no resources (only other modules)
are properly destroyed up to arbitrary depth [GH-2786]
* core: Fix "attribute not available" during destroy plans in
cases where the parameter is passed between modules [GH-2775]
* connection/ssh: fix issue on machines with an SSH Agent available
preventing `key_file` from being read without explicitly
setting `agent = false` [GH-2615]
* provider/aws: Allow uppercase characters in `aws_elb.name` [GH-2580]
* provider/aws: Allow underscores in `aws_db_subnet_group.name` (undocumented by AWS) [GH-2604]
* provider/aws: Allow dots in `aws_db_subnet_group.name` (undocumented by AWS) [GH-2665]
* provider/aws: Fix issue with pending Spot Instance requests [GH-2640]
* provider/aws: Fix issue in AWS Classic environment with referencing external
Security Groups [GH-2644]
* provider/aws: Bump internet gateway detach timeout [GH-2669]
* provider/aws: Fix issue with detecting differences in DB Parameters [GH-2728]
* provider/aws: `ecs_cluster` rename (recreation) and deletion is handled correctly [GH-2698]
* provider/aws: `aws_route_table` ignores routes generated for VPC endpoints [GH-2695]
* provider/aws: Fix issue with Launch Configurations and enable_monitoring [GH-2735]
* provider/openstack: allow empty api_key and endpoint_type [GH-2626]
* provisioner/chef: Fix permission denied error with ohai hints [GH-2781]
## 0.6.0 (June 30, 2015)
BACKWARDS INCOMPATIBILITIES:

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform/helper/multierror"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudwatch"
@ -85,6 +86,14 @@ func (c *Config) Client() (interface{}, error) {
MaxRetries: c.MaxRetries,
}
log.Println("[INFO] Initializing IAM Connection")
client.iamconn = iam.New(awsConfig)
err := c.ValidateCredentials(client.iamconn)
if err != nil {
errs = append(errs, err)
}
log.Println("[INFO] Initializing DynamoDB connection")
client.dynamodbconn = dynamodb.New(awsConfig)
@ -103,15 +112,12 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing RDS Connection")
client.rdsconn = rds.New(awsConfig)
log.Println("[INFO] Initializing IAM Connection")
client.iamconn = iam.New(awsConfig)
log.Println("[INFO] Initializing Kinesis Connection")
client.kinesisconn = kinesis.New(awsConfig)
err := c.ValidateAccountId(client.iamconn)
if err != nil {
errs = append(errs, err)
authErr := c.ValidateAccountId(client.iamconn)
if authErr != nil {
errs = append(errs, authErr)
}
log.Println("[INFO] Initializing AutoScaling connection")
@ -165,6 +171,19 @@ func (c *Config) ValidateRegion() error {
return fmt.Errorf("Not a valid region: %s", c.Region)
}
// Validate credentials early and fail before we do any graph walking
func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
_, err := iamconn.GetUser(nil)
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "SignatureDoesNotMatch" {
return fmt.Errorf("Failed authenticating with AWS: please verify credentials")
}
}
return err
}
// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccCustomerGateway_basic(t *testing.T) {
func TestAccAWSCustomerGateway_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,

View File

@ -46,6 +46,10 @@ func resourceAwsDbInstance() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
StateFunc: func(v interface{}) string {
value := v.(string)
return strings.ToLower(value)
},
},
"engine_version": &schema.Schema{

View File

@ -176,7 +176,7 @@ resource "aws_db_instance" "bar" {
identifier = "foobarbaz-test-terraform-%d"
allocated_storage = 10
engine = "mysql"
engine = "MySQL"
engine_version = "5.6.21"
instance_class = "db.t1.micro"
name = "baz"

View File

@ -166,7 +166,7 @@ func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{})
Parameters: parameters,
}
log.Printf("[DEBUG] Modify DB Parameter Group: %#v", modifyOpts)
log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts)
_, err = rdsconn.ModifyDBParameterGroup(&modifyOpts)
if err != nil {
return fmt.Errorf("Error modifying DB Parameter Group: %s", err)

View File

@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
@ -24,7 +25,8 @@ func resourceAwsElb() *schema.Resource {
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
@ -211,10 +213,18 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
return err
}
var elbName string
if v, ok := d.GetOk("name"); ok {
elbName = v.(string)
} else {
elbName = resource.PrefixedUniqueId("tf-lb-")
d.Set("name", elbName)
}
tags := tagsFromMapELB(d.Get("tags").(map[string]interface{}))
// Provision the elb
elbOpts := &elb.CreateLoadBalancerInput{
LoadBalancerName: aws.String(d.Get("name").(string)),
LoadBalancerName: aws.String(elbName),
Listeners: listeners,
Tags: tags,
}
@ -241,7 +251,7 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
}
// Assign the elb's unique identifier for use later
d.SetId(d.Get("name").(string))
d.SetId(elbName)
log.Printf("[INFO] ELB ID: %s", d.Id())
// Enable partial mode and record what we set

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccFlowLog_basic(t *testing.T) {
func TestAccAWSFlowLog_basic(t *testing.T) {
var flowLog ec2.FlowLog
lgn := os.Getenv("LOG_GROUP_NAME")
@ -31,7 +31,7 @@ func TestAccFlowLog_basic(t *testing.T) {
})
}
func TestAccFlowLog_subnet(t *testing.T) {
func TestAccAWSFlowLog_subnet(t *testing.T) {
var flowLog ec2.FlowLog
lgn := os.Getenv("LOG_GROUP_NAME")

View File

@ -13,7 +13,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccIAMServerCertificate_basic(t *testing.T) {
func TestAccAWSIAMServerCertificate_basic(t *testing.T) {
var cert iam.ServerCertificate
resource.Test(t, resource.TestCase{

View File

@ -579,6 +579,24 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
}
}
if d.HasChange("monitoring") {
var mErr error
if d.Get("monitoring").(bool) {
log.Printf("[DEBUG] Enabling monitoring for Instance (%s)", d.Id())
_, mErr = conn.MonitorInstances(&ec2.MonitorInstancesInput{
InstanceIDs: []*string{aws.String(d.Id())},
})
} else {
log.Printf("[DEBUG] Disabling monitoring for Instance (%s)", d.Id())
_, mErr = conn.UnmonitorInstances(&ec2.UnmonitorInstancesInput{
InstanceIDs: []*string{aws.String(d.Id())},
})
}
if mErr != nil {
return fmt.Errorf("[WARN] Error updating Instance monitoring: %s", mErr)
}
}
// TODO(mitchellh): wait for the attributes we modified to
// persist the change...
@ -760,6 +778,10 @@ func fetchRootDeviceName(ami string, conn *ec2.EC2) (*string, error) {
rootDeviceName = image.BlockDeviceMappings[0].DeviceName
}
if rootDeviceName == nil {
return nil, fmt.Errorf("[WARN] Error finding Root Device Name for AMI (%s)", ami)
}
return rootDeviceName, nil
}

View File

@ -13,7 +13,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccKinesisStream_basic(t *testing.T) {
func TestAccAWSKinesisStream_basic(t *testing.T) {
var stream kinesis.StreamDescription
resource.Test(t, resource.TestCase{

View File

@ -10,7 +10,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSLambdaFunction_normal(t *testing.T) {
func TestAccAWSLambdaFunction_basic(t *testing.T) {
var conf lambda.GetFunctionOutput
resource.Test(t, resource.TestCase{

View File

@ -480,7 +480,8 @@ func resourceAwsLaunchConfigurationDelete(d *schema.ResourceData, meta interface
})
if err != nil {
autoscalingerr, ok := err.(awserr.Error)
if ok && autoscalingerr.Code() == "InvalidConfiguration.NotFound" {
if ok && (autoscalingerr.Code() == "InvalidConfiguration.NotFound" || autoscalingerr.Code() == "ValidationError") {
log.Printf("[DEBUG] Launch configuration (%s) not found", d.Id())
return nil
}

View File

@ -46,6 +46,12 @@ func resourceAwsNetworkInterface() *schema.Resource {
Set: schema.HashString,
},
"source_dest_check": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"attachment": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
@ -127,6 +133,7 @@ func resourceAwsNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e
d.Set("subnet_id", eni.SubnetID)
d.Set("private_ips", flattenNetworkInterfacesPrivateIPAddesses(eni.PrivateIPAddresses))
d.Set("security_groups", flattenGroupIdentifiers(eni.Groups))
d.Set("source_dest_check", eni.SourceDestCheck)
// Tags
d.Set("tags", tagsToMap(eni.TagSet))
@ -221,6 +228,18 @@ func resourceAwsNetworkInterfaceUpdate(d *schema.ResourceData, meta interface{})
d.SetPartial("attachment")
}
request := &ec2.ModifyNetworkInterfaceAttributeInput{
NetworkInterfaceID: aws.String(d.Id()),
SourceDestCheck: &ec2.AttributeBooleanValue{Value: aws.Boolean(d.Get("source_dest_check").(bool))},
}
_, err := conn.ModifyNetworkInterfaceAttribute(request)
if err != nil {
return fmt.Errorf("Failure updating ENI: %s", err)
}
d.SetPartial("source_dest_check")
if d.HasChange("security_groups") {
request := &ec2.ModifyNetworkInterfaceAttributeInput{
NetworkInterfaceID: aws.String(d.Id()),

View File

@ -57,6 +57,26 @@ func TestAccAWSENI_attached(t *testing.T) {
})
}
func TestAccAWSENI_sourceDestCheck(t *testing.T) {
var conf ec2.NetworkInterface
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSENIDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSENIConfigWithSourceDestCheck,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSENIExists("aws_network_interface.bar", &conf),
resource.TestCheckResourceAttr(
"aws_network_interface.bar", "source_dest_check", "false"),
),
},
},
})
}
func testAccCheckAWSENIExists(n string, res *ec2.NetworkInterface) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
@ -108,6 +128,10 @@ func testAccCheckAWSENIAttributes(conf *ec2.NetworkInterface) resource.TestCheck
return fmt.Errorf("expected private ip to be 172.16.10.100, but was %s", *conf.PrivateIPAddress)
}
if *conf.SourceDestCheck != true {
return fmt.Errorf("expected source_dest_check to be true, but was %t", *conf.SourceDestCheck)
}
if len(conf.TagSet) == 0 {
return fmt.Errorf("expected tags")
}
@ -201,6 +225,24 @@ resource "aws_network_interface" "bar" {
}
`
const testAccAWSENIConfigWithSourceDestCheck = `
resource "aws_vpc" "foo" {
cidr_block = "172.16.0.0/16"
}
resource "aws_subnet" "foo" {
vpc_id = "${aws_vpc.foo.id}"
cidr_block = "172.16.10.0/24"
availability_zone = "us-west-2a"
}
resource "aws_network_interface" "bar" {
subnet_id = "${aws_subnet.foo.id}"
source_dest_check = false
private_ips = ["172.16.10.100"]
}
`
const testAccAWSENIConfigWithAttachment = `
resource "aws_vpc" "foo" {
cidr_block = "172.16.0.0/16"

View File

@ -13,7 +13,7 @@ import (
"github.com/aws/aws-sdk-go/service/route53"
)
func TestAccRoute53DelegationSet_basic(t *testing.T) {
func TestAccAWSRoute53DelegationSet_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -29,7 +29,7 @@ func TestAccRoute53DelegationSet_basic(t *testing.T) {
})
}
func TestAccRoute53DelegationSet_withZones(t *testing.T) {
func TestAccAWSRoute53DelegationSet_withZones(t *testing.T) {
var zone route53.GetHostedZoneOutput
resource.Test(t, resource.TestCase{

View File

@ -10,7 +10,7 @@ import (
"github.com/aws/aws-sdk-go/service/route53"
)
func TestAccRoute53HealthCheck_basic(t *testing.T) {
func TestAccAWSRoute53HealthCheck_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -34,7 +34,7 @@ func TestAccRoute53HealthCheck_basic(t *testing.T) {
})
}
func TestAccRoute53HealthCheck_IpConfig(t *testing.T) {
func TestAccAWSRoute53HealthCheck_IpConfig(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,

View File

@ -50,7 +50,7 @@ func TestExpandRecordName(t *testing.T) {
}
}
func TestAccRoute53Record_basic(t *testing.T) {
func TestAccAWSRoute53Record_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -66,7 +66,7 @@ func TestAccRoute53Record_basic(t *testing.T) {
})
}
func TestAccRoute53Record_txtSupport(t *testing.T) {
func TestAccAWSRoute53Record_txtSupport(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -82,7 +82,7 @@ func TestAccRoute53Record_txtSupport(t *testing.T) {
})
}
func TestAccRoute53Record_generatesSuffix(t *testing.T) {
func TestAccAWSRoute53Record_generatesSuffix(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -98,7 +98,7 @@ func TestAccRoute53Record_generatesSuffix(t *testing.T) {
})
}
func TestAccRoute53Record_wildcard(t *testing.T) {
func TestAccAWSRoute53Record_wildcard(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -122,7 +122,7 @@ func TestAccRoute53Record_wildcard(t *testing.T) {
})
}
func TestAccRoute53Record_weighted(t *testing.T) {
func TestAccAWSRoute53Record_weighted(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -139,7 +139,7 @@ func TestAccRoute53Record_weighted(t *testing.T) {
})
}
func TestAccRoute53Record_alias(t *testing.T) {
func TestAccAWSRoute53Record_alias(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -155,7 +155,23 @@ func TestAccRoute53Record_alias(t *testing.T) {
})
}
func TestAccRoute53Record_weighted_alias(t *testing.T) {
func TestAccAWSRoute53Record_s3_alias(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRoute53RecordDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRoute53S3AliasRecord,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53RecordExists("aws_route53_record.alias"),
),
},
},
})
}
func TestAccAWSRoute53Record_weighted_alias(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -182,7 +198,7 @@ func TestAccRoute53Record_weighted_alias(t *testing.T) {
})
}
func TestAccRoute53Record_TypeChange(t *testing.T) {
func TestAccAWSRoute53Record_TypeChange(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
@ -449,6 +465,32 @@ resource "aws_route53_record" "alias" {
}
`
const testAccRoute53S3AliasRecord = `
resource "aws_route53_zone" "main" {
name = "notexample.com"
}
resource "aws_s3_bucket" "website" {
bucket = "website.notexample.com"
acl = "public-read"
website {
index_document = "index.html"
}
}
resource "aws_route53_record" "alias" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "www"
type = "A"
alias {
zone_id = "${aws_s3_bucket.website.hosted_zone_id}"
name = "${aws_s3_bucket.website.website_domain}"
evaluate_target_health = true
}
}
`
const testAccRoute53WeightedElbAliasRecord = `
resource "aws_route53_zone" "main" {
name = "notexample.com"

View File

@ -12,7 +12,7 @@ import (
"github.com/aws/aws-sdk-go/service/route53"
)
func TestAccRoute53ZoneAssociation_basic(t *testing.T) {
func TestAccAWSRoute53ZoneAssociation_basic(t *testing.T) {
var zone route53.HostedZone
resource.Test(t, resource.TestCase{
@ -30,7 +30,7 @@ func TestAccRoute53ZoneAssociation_basic(t *testing.T) {
})
}
func TestAccRoute53ZoneAssociation_region(t *testing.T) {
func TestAccAWSRoute53ZoneAssociation_region(t *testing.T) {
var zone route53.HostedZone
// record the initialized providers so that we can use them to

View File

@ -64,7 +64,7 @@ func TestCleanChangeID(t *testing.T) {
}
}
func TestAccRoute53Zone_basic(t *testing.T) {
func TestAccAWSRoute53Zone_basic(t *testing.T) {
var zone route53.GetHostedZoneOutput
var td route53.ResourceTagSet
@ -85,7 +85,7 @@ func TestAccRoute53Zone_basic(t *testing.T) {
})
}
func TestAccRoute53Zone_private_basic(t *testing.T) {
func TestAccAWSRoute53Zone_private_basic(t *testing.T) {
var zone route53.GetHostedZoneOutput
resource.Test(t, resource.TestCase{
@ -104,7 +104,7 @@ func TestAccRoute53Zone_private_basic(t *testing.T) {
})
}
func TestAccRoute53Zone_private_region(t *testing.T) {
func TestAccAWSRoute53Zone_private_region(t *testing.T) {
var zone route53.GetHostedZoneOutput
// record the initialized providers so that we can use them to

View File

@ -77,12 +77,16 @@ func resourceAwsS3Bucket() *schema.Resource {
Optional: true,
Computed: true,
},
"website_endpoint": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"website_domain": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"tags": tagsSchema(),
@ -237,13 +241,18 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
}
// Add website_endpoint as an attribute
endpoint, err := websiteEndpoint(s3conn, d)
websiteEndpoint, err := websiteEndpoint(s3conn, d)
if err != nil {
return err
}
if err := d.Set("website_endpoint", endpoint); err != nil {
if websiteEndpoint != nil {
if err := d.Set("website_endpoint", websiteEndpoint.Endpoint); err != nil {
return err
}
if err := d.Set("website_domain", websiteEndpoint.Domain); err != nil {
return err
}
}
tagSet, err := getTagSetS3(s3conn, d.Id())
if err != nil {
@ -405,11 +414,11 @@ func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) err
return nil
}
func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) {
func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (*S3Website, error) {
// If the bucket doesn't have a website configuration, return an empty
// endpoint
if _, ok := d.GetOk("website"); !ok {
return "", nil
return nil, nil
}
bucket := d.Get("bucket").(string)
@ -421,26 +430,31 @@ func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (string, error) {
},
)
if err != nil {
return "", err
return nil, err
}
var region string
if location.LocationConstraint != nil {
region = *location.LocationConstraint
}
return WebsiteEndpointUrl(bucket, region), nil
return WebsiteEndpoint(bucket, region), nil
}
func WebsiteEndpointUrl(bucket string, region string) string {
func WebsiteEndpoint(bucket string, region string) *S3Website {
domain := WebsiteDomainUrl(region)
return &S3Website{Endpoint: fmt.Sprintf("%s.%s", bucket, domain), Domain: domain}
}
func WebsiteDomainUrl(region string) string {
region = normalizeRegion(region)
// Frankfurt(and probably future) regions uses different syntax for website endpoints
// http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html
if region == "eu-central-1" {
return fmt.Sprintf("%s.s3-website.%s.amazonaws.com", bucket, region)
return fmt.Sprintf("s3-website.%s.amazonaws.com", region)
}
return fmt.Sprintf("%s.s3-website-%s.amazonaws.com", bucket, region)
return fmt.Sprintf("s3-website-%s.amazonaws.com", region)
}
func normalizeJson(jsonString interface{}) string {
@ -465,3 +479,7 @@ func normalizeRegion(region string) string {
return region
}
type S3Website struct {
Endpoint, Domain string
}

View File

@ -160,7 +160,7 @@ func resourceAwsSpotInstanceRequestRead(d *schema.ResourceData, meta interface{}
request := resp.SpotInstanceRequests[0]
// if the request is cancelled, then it is gone
if *request.State == "canceled" {
if *request.State == "cancelled" {
d.SetId("")
return nil
}

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccDHCPOptions_basic(t *testing.T) {
func TestAccAWSDHCPOptions_basic(t *testing.T) {
var d ec2.DHCPOptions
resource.Test(t, resource.TestCase{

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccVpcEndpoint_basic(t *testing.T) {
func TestAccAWSVpcEndpoint_basic(t *testing.T) {
var endpoint ec2.VPCEndpoint
resource.Test(t, resource.TestCase{
@ -29,7 +29,7 @@ func TestAccVpcEndpoint_basic(t *testing.T) {
})
}
func TestAccVpcEndpoint_withRouteTableAndPolicy(t *testing.T) {
func TestAccAWSVpcEndpoint_withRouteTableAndPolicy(t *testing.T) {
var endpoint ec2.VPCEndpoint
var routeTable ec2.RouteTable

View File

@ -11,7 +11,7 @@ import (
"github.com/hashicorp/terraform/terraform"
)
func TestAccVpc_basic(t *testing.T) {
func TestAccAWSVpc_basic(t *testing.T) {
var vpc ec2.VPC
resource.Test(t, resource.TestCase{
@ -32,7 +32,7 @@ func TestAccVpc_basic(t *testing.T) {
})
}
func TestAccVpc_dedicatedTenancy(t *testing.T) {
func TestAccAWSVpc_dedicatedTenancy(t *testing.T) {
var vpc ec2.VPC
resource.Test(t, resource.TestCase{
@ -52,7 +52,7 @@ func TestAccVpc_dedicatedTenancy(t *testing.T) {
})
}
func TestAccVpc_tags(t *testing.T) {
func TestAccAWSVpc_tags(t *testing.T) {
var vpc ec2.VPC
resource.Test(t, resource.TestCase{
@ -83,7 +83,7 @@ func TestAccVpc_tags(t *testing.T) {
})
}
func TestAccVpcUpdate(t *testing.T) {
func TestAccAWSVpc_update(t *testing.T) {
var vpc ec2.VPC
resource.Test(t, resource.TestCase{
@ -187,7 +187,7 @@ func testAccCheckVpcExists(n string, vpc *ec2.VPC) resource.TestCheckFunc {
}
// https://github.com/hashicorp/terraform/issues/1301
func TestAccVpc_bothDnsOptionsSet(t *testing.T) {
func TestAccAWSVpc_bothDnsOptionsSet(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,

View File

@ -87,7 +87,7 @@ func TestAccAWSVpnGateway_delete(t *testing.T) {
})
}
func TestAccVpnGateway_tags(t *testing.T) {
func TestAccAWSVpnGateway_tags(t *testing.T) {
var v ec2.VPNGateway
resource.Test(t, resource.TestCase{

View File

@ -186,13 +186,17 @@ func expandIPPerms(
// Takes the result of flatmap.Expand for an array of parameters and
// returns Parameter API compatible objects
func expandParameters(configured []interface{}) ([]*rds.Parameter, error) {
parameters := make([]*rds.Parameter, 0, len(configured))
var parameters []*rds.Parameter
// Loop over our configured parameters and create
// an array of aws-sdk-go compatabile objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
if data["name"].(string) == "" {
continue
}
p := &rds.Parameter{
ApplyMethod: aws.String(data["apply_method"].(string)),
ParameterName: aws.String(data["name"].(string)),

View File

@ -20,9 +20,9 @@ var websiteEndpoints = []struct {
func TestWebsiteEndpointUrl(t *testing.T) {
for _, tt := range websiteEndpoints {
s := WebsiteEndpointUrl("bucket-name", tt.in)
if s != tt.out {
t.Errorf("WebsiteEndpointUrl(\"bucket-name\", %q) => %q, want %q", tt.in, s, tt.out)
s := WebsiteEndpoint("bucket-name", tt.in)
if s.Endpoint != tt.out {
t.Errorf("WebsiteEndpointUrl(\"bucket-name\", %q) => %q, want %q", tt.in, s.Endpoint, tt.out)
}
}
}

View File

@ -71,12 +71,29 @@ func (p *Provisioner) linuxCreateConfigFiles(
return err
}
if err := p.deployOhaiHints(o, comm, hintsDir); err != nil {
// Make sure we have enough rights to upload the hints if using sudo
if p.useSudo {
if err := p.runCommand(o, comm, "chmod 777 "+hintsDir); err != nil {
return err
}
}
// When done copying the files restore the rights and make sure root is owner
if err := p.deployOhaiHints(o, comm, hintsDir); err != nil {
return err
}
// When done copying the hints restore the rights and make sure root is owner
if p.useSudo {
if err := p.runCommand(o, comm, "chmod 755 "+hintsDir); err != nil {
return err
}
if err := p.runCommand(o, comm, "chown -R root.root "+hintsDir); err != nil {
return err
}
}
}
// When done copying all files restore the rights and make sure root is owner
if p.useSudo {
if err := p.runCommand(o, comm, "chmod 755 "+linuxConfDir); err != nil {
return err

View File

@ -166,6 +166,9 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
"sudo mkdir -p " + linuxConfDir: true,
"sudo chmod 777 " + linuxConfDir: true,
"sudo mkdir -p " + path.Join(linuxConfDir, "ohai/hints"): true,
"sudo chmod 777 " + path.Join(linuxConfDir, "ohai/hints"): true,
"sudo chmod 755 " + path.Join(linuxConfDir, "ohai/hints"): true,
"sudo chown -R root.root " + path.Join(linuxConfDir, "ohai/hints"): true,
"sudo chmod 755 " + linuxConfDir: true,
"sudo chown -R root.root " + linuxConfDir: true,
},

View File

@ -140,7 +140,8 @@ func (h *UiHook) PostApply(
}
if applyerr != nil {
msg = fmt.Sprintf("Error: %s", applyerr)
// Errors are collected and printed in ApplyCommand, no need to duplicate
return terraform.HookActionContinue, nil
}
h.ui.Output(h.Colorize.Color(fmt.Sprintf(

385
deps/v0-6-1.json vendored Normal file
View File

@ -0,0 +1,385 @@
{
"ImportPath": "github.com/hashicorp/terraform",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "code.google.com/p/go-uuid/uuid",
"Comment": "null-15",
"Rev": "35bc42037350f0078e3c974c6ea690f1926603ab"
},
{
"ImportPath": "github.com/Azure/azure-sdk-for-go/core/http",
"Comment": "v1.2-216-g9197765",
"Rev": "91977650587a7bc48318c0430649d7fea886f111"
},
{
"ImportPath": "github.com/Azure/azure-sdk-for-go/core/tls",
"Comment": "v1.2-216-g9197765",
"Rev": "91977650587a7bc48318c0430649d7fea886f111"
},
{
"ImportPath": "github.com/Azure/azure-sdk-for-go/management",
"Comment": "v1.2-216-g9197765",
"Rev": "91977650587a7bc48318c0430649d7fea886f111"
},
{
"ImportPath": "github.com/Azure/azure-sdk-for-go/storage",
"Comment": "v1.2-216-g9197765",
"Rev": "91977650587a7bc48318c0430649d7fea886f111"
},
{
"ImportPath": "github.com/Azure/go-pkcs12",
"Rev": "a635c0684cd517745ca5c9552a312627791d5ba0"
},
{
"ImportPath": "github.com/armon/circbuf",
"Rev": "f092b4f207b6e5cce0569056fba9e1a2735cb6cf"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/endpoints",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/ec2query",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/json/jsonutil",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/jsonrpc",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/query",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/rest",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/restjson",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/restxml",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/protocol/xml/xmlutil",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/signer/v4",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/dynamodb",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ecs",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elasticache",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elb",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/iam",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/kinesis",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/lambda",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/rds",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/route53",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/s3",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/sns",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/sqs",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/awslabs/aws-sdk-go/aws/credentials",
"Comment": "v0.6.7-3-g2a6648c",
"Rev": "2a6648c479175ce005bca95780f948a196a46062"
},
{
"ImportPath": "github.com/cyberdelia/heroku-go/v3",
"Rev": "594d483b9b6a8ddc7cd2f1e3e7d1de92fa2de665"
},
{
"ImportPath": "github.com/dylanmei/iso8601",
"Rev": "2075bf119b58e5576c6ed9f867b8f3d17f2e54d4"
},
{
"ImportPath": "github.com/dylanmei/winrmtest",
"Rev": "3e9661c52c45dab9a8528966a23d421922fca9b9"
},
{
"ImportPath": "github.com/fsouza/go-dockerclient",
"Rev": "f6e9f5396e0e8f34472efe443d0cb7f9af162b88"
},
{
"ImportPath": "github.com/hashicorp/atlas-go/archive",
"Comment": "20141209094003-71-g785958f",
"Rev": "785958ffcd6a8857890651f3f4d9a289ddc27633"
},
{
"ImportPath": "github.com/hashicorp/atlas-go/v1",
"Comment": "20141209094003-71-g785958f",
"Rev": "785958ffcd6a8857890651f3f4d9a289ddc27633"
},
{
"ImportPath": "github.com/hashicorp/consul/api",
"Comment": "v0.5.2-159-gc34bcb4",
"Rev": "c34bcb45c670af076846826ea72c436fbd0e2c35"
},
{
"ImportPath": "github.com/hashicorp/errwrap",
"Rev": "7554cd9344cec97297fa6649b055a8c98c2a1e55"
},
{
"ImportPath": "github.com/hashicorp/go-checkpoint",
"Rev": "88326f6851319068e7b34981032128c0b1a6524d"
},
{
"ImportPath": "github.com/hashicorp/go-multierror",
"Rev": "56912fb08d85084aa318edcf2bba735b97cf35c5"
},
{
"ImportPath": "github.com/hashicorp/go-version",
"Rev": "999359b6b7a041ce16e695d51e92145b83f01087"
},
{
"ImportPath": "github.com/hashicorp/hcl",
"Rev": "54864211433d45cb780682431585b3e573b49e4a"
},
{
"ImportPath": "github.com/hashicorp/yamux",
"Rev": "8e00b30457b1486b012f204b82ec92ae6b547de8"
},
{
"ImportPath": "github.com/imdario/mergo",
"Comment": "0.2.0-5-g61a5285",
"Rev": "61a52852277811e93e06d28e0d0c396284a7730b"
},
{
"ImportPath": "github.com/masterzen/simplexml/dom",
"Rev": "95ba30457eb1121fa27753627c774c7cd4e90083"
},
{
"ImportPath": "github.com/masterzen/winrm/soap",
"Rev": "23128e7b3dc1f8091aeff7aae82cb2112ce53c75"
},
{
"ImportPath": "github.com/masterzen/winrm/winrm",
"Rev": "23128e7b3dc1f8091aeff7aae82cb2112ce53c75"
},
{
"ImportPath": "github.com/masterzen/xmlpath",
"Rev": "13f4951698adc0fa9c1dda3e275d489a24201161"
},
{
"ImportPath": "github.com/mitchellh/cli",
"Rev": "8102d0ed5ea2709ade1243798785888175f6e415"
},
{
"ImportPath": "github.com/mitchellh/colorstring",
"Rev": "61164e49940b423ba1f12ddbdf01632ac793e5e9"
},
{
"ImportPath": "github.com/mitchellh/copystructure",
"Rev": "6fc66267e9da7d155a9d3bd489e00dad02666dc6"
},
{
"ImportPath": "github.com/mitchellh/go-homedir",
"Rev": "1f6da4a72e57d4e7edd4a7295a585e0a3999a2d4"
},
{
"ImportPath": "github.com/mitchellh/go-linereader",
"Rev": "07bab5fdd9580500aea6ada0e09df4aa28e68abd"
},
{
"ImportPath": "github.com/mitchellh/mapstructure",
"Rev": "281073eb9eb092240d33ef253c404f1cca550309"
},
{
"ImportPath": "github.com/mitchellh/osext",
"Rev": "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"
},
{
"ImportPath": "github.com/mitchellh/packer/common/uuid",
"Comment": "v0.8.2-4-g2010a0c",
"Rev": "2010a0c966175b3c0fa8d158a879c10acbba0d76"
},
{
"ImportPath": "github.com/mitchellh/panicwrap",
"Rev": "45cbfd3bae250c7676c077fb275be1a2968e066a"
},
{
"ImportPath": "github.com/mitchellh/prefixedio",
"Rev": "89d9b535996bf0a185f85b59578f2e245f9e1724"
},
{
"ImportPath": "github.com/mitchellh/reflectwalk",
"Rev": "eecf4c70c626c7cfbb95c90195bc34d386c74ac6"
},
{
"ImportPath": "github.com/nu7hatch/gouuid",
"Rev": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3"
},
{
"ImportPath": "github.com/packer-community/winrmcp/winrmcp",
"Rev": "743b1afe5ee3f6d5ba71a0d50673fa0ba2123d6b"
},
{
"ImportPath": "github.com/pearkes/cloudflare",
"Rev": "19e280b056f3742e535ea12ae92a37ea7767ea82"
},
{
"ImportPath": "github.com/pearkes/digitalocean",
"Rev": "e966f00c2d9de5743e87697ab77c7278f5998914"
},
{
"ImportPath": "github.com/pearkes/dnsimple",
"Rev": "2a807d118c9e52e94819f414a6ec0293b45cad01"
},
{
"ImportPath": "github.com/pearkes/mailgun",
"Rev": "5b02e7e9ffee9869f81393e80db138f6ff726260"
},
{
"ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v1.0.0-623-ge83aa01",
"Rev": "e83aa011e019917c7bd951444d61c42431b4d21d"
},
{
"ImportPath": "github.com/satori/go.uuid",
"Rev": "afe1e2ddf0f05b7c29d388a3f8e76cb15c2231ca"
},
{
"ImportPath": "github.com/soniah/dnsmadeeasy",
"Comment": "v1.1-2-g5578a8c",
"Rev": "5578a8c15e33958c61cf7db720b6181af65f4a9e"
},
{
"ImportPath": "github.com/vaughan0/go-ini",
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
},
{
"ImportPath": "github.com/xanzy/go-cloudstack/cloudstack",
"Comment": "v1.2.0-36-g0031956",
"Rev": "00319560eeca5e6ffef3ba048c97c126a465854f"
},
{
"ImportPath": "golang.org/x/crypto/ssh",
"Rev": "7d5b0be716b9d6d4269afdaae10032bb296d3cdf"
},
{
"ImportPath": "golang.org/x/net/context",
"Rev": "f0cf018861e2b54077eced91659e255072b5f215"
},
{
"ImportPath": "golang.org/x/oauth2",
"Rev": "8914e5017ca260f2a3a1575b1e6868874050d95e"
},
{
"ImportPath": "google.golang.org/api/compute/v1",
"Rev": "18450f4e95c7e76ce3a5dc3a8cb7178ab6d56121"
},
{
"ImportPath": "google.golang.org/api/container/v1",
"Rev": "18450f4e95c7e76ce3a5dc3a8cb7178ab6d56121"
},
{
"ImportPath": "google.golang.org/api/dns/v1",
"Rev": "18450f4e95c7e76ce3a5dc3a8cb7178ab6d56121"
},
{
"ImportPath": "google.golang.org/api/googleapi",
"Rev": "18450f4e95c7e76ce3a5dc3a8cb7178ab6d56121"
},
{
"ImportPath": "google.golang.org/api/storage/v1",
"Rev": "18450f4e95c7e76ce3a5dc3a8cb7178ab6d56121"
},
{
"ImportPath": "google.golang.org/cloud/compute/metadata",
"Rev": "522a8ceb4bb83c2def27baccf31d646bce11a4b2"
},
{
"ImportPath": "google.golang.org/cloud/internal",
"Rev": "522a8ceb4bb83c2def27baccf31d646bce11a4b2"
}
]
}

View File

@ -9,13 +9,18 @@ import (
const UniqueIdPrefix = `terraform-`
// Helper for a resource to generate a unique identifier
// Helper for a resource to generate a unique identifier w/ default prefix
func UniqueId() string {
return PrefixedUniqueId(UniqueIdPrefix)
}
// Helper for a resource to generate a unique identifier w/ given prefix
//
// This uses a simple RFC 4122 v4 UUID with some basic cosmetic filters
// applied (base32, remove padding, downcase) to make visually distinguishing
// identifiers easier.
func UniqueId() string {
return fmt.Sprintf("%s%s", UniqueIdPrefix,
func PrefixedUniqueId(prefix string) string {
return fmt.Sprintf("%s%s", prefix,
strings.ToLower(
strings.Replace(
base32.StdEncoding.EncodeToString(uuidV4()),

View File

@ -215,6 +215,49 @@ func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) {
}
}
func TestContext2Apply_destroyComputed(t *testing.T) {
m := testModule(t, "apply-destroy-computed")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.foo": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "foo",
Attributes: map[string]string{
"output": "value",
},
},
},
},
},
},
}
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
State: state,
Destroy: true,
})
if p, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
} else {
t.Logf(p.String())
}
if _, err := ctx.Apply(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestContext2Apply_minimal(t *testing.T) {
m := testModule(t, "apply-minimal")
p := testProvider("aws")
@ -2097,6 +2140,105 @@ func TestContext2Apply_destroy(t *testing.T) {
}
}
func TestContext2Apply_destroyNestedModule(t *testing.T) {
m := testModule(t, "apply-destroy-nested-module")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
s := &State{
Modules: []*ModuleState{
&ModuleState{
Path: []string{"root", "child", "subchild"},
Resources: map[string]*ResourceState{
"aws_instance.bar": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
},
},
},
},
},
}
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
State: s,
})
// First plan and apply a create operation
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
// Test that things were destroyed
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testTerraformApplyDestroyNestedModuleStr)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
}
}
func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) {
m := testModule(t, "apply-destroy-deeply-nested-module")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
s := &State{
Modules: []*ModuleState{
&ModuleState{
Path: []string{"root", "child", "subchild", "subsubchild"},
Resources: map[string]*ResourceState{
"aws_instance.bar": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
},
},
},
},
},
}
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
State: s,
})
// First plan and apply a create operation
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
// Test that things were destroyed
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(`
module.child.subchild.subsubchild:
<no state>
`)
if actual != expected {
t.Fatalf("bad: \n%s", actual)
}
}
func TestContext2Apply_destroyOutputs(t *testing.T) {
m := testModule(t, "apply-destroy-outputs")
h := new(HookRecordApplyOrder)

View File

@ -94,7 +94,8 @@ func (n *EvalApply) Eval(ctx EvalContext) (interface{}, error) {
// if we have one, otherwise we just output it.
if err != nil {
if n.Error != nil {
*n.Error = multierror.Append(*n.Error, err)
helpfulErr := fmt.Errorf("%s: %s", n.Info.Id, err.Error())
*n.Error = multierror.Append(*n.Error, helpfulErr)
} else {
return nil, err
}

View File

@ -1,6 +1,7 @@
package terraform
import (
"fmt"
"log"
)
@ -35,7 +36,7 @@ func (n *EvalRefresh) Eval(ctx EvalContext) (interface{}, error) {
// Refresh!
state, err = provider.Refresh(n.Info, state)
if err != nil {
return nil, err
return nil, fmt.Errorf("%s: %s", n.Info.Id, err.Error())
}
// Call post-refresh hook

View File

@ -164,6 +164,9 @@ func (b *BuiltinGraphBuilder) Steps(path []string) []GraphTransformer {
Then: &PruneDestroyTransformer{Diff: b.Diff, State: b.State},
}),
// Remove the noop nodes
&PruneNoopTransformer{Diff: b.Diff, State: b.State},
// Insert nodes to close opened plugin connections
&CloseProviderTransformer{},
&CloseProvisionerTransformer{},

View File

@ -249,6 +249,41 @@ func (n *GraphNodeConfigResource) DestroyNode(mode GraphNodeDestroyMode) GraphNo
return result
}
// GraphNodeNoopPrunable
func (n *GraphNodeConfigResource) Noop(opts *NoopOpts) bool {
// We don't have any noop optimizations for destroy nodes yet
if n.DestroyMode != DestroyNone {
return false
}
// If there is no diff, then we aren't a noop since something needs to
// be done (such as a plan). We only check if we're a noop in a diff.
if opts.Diff == nil || opts.Diff.Empty() {
return false
}
// If we have no module diff, we're certainly a noop. This is because
// it means there is a diff, and that the module we're in just isn't
// in it, meaning we're not doing anything.
if opts.ModDiff == nil || opts.ModDiff.Empty() {
return true
}
// Grab the ID which is the prefix (in the case count > 0 at some point)
prefix := n.Resource.Id()
// Go through the diff and if there are any with our name on it, keep us
found := false
for k, _ := range opts.ModDiff.Resources {
if strings.HasPrefix(k, prefix) {
found = true
break
}
}
return !found
}
// Same as GraphNodeConfigResource, but for flattening
type GraphNodeConfigResourceFlat struct {
*GraphNodeConfigResource

View File

@ -75,6 +75,28 @@ func (n *GraphNodeConfigVariable) DestroyEdgeInclude(v dag.Vertex) bool {
return false
}
// GraphNodeNoopPrunable
func (n *GraphNodeConfigVariable) Noop(opts *NoopOpts) bool {
// If we have no diff, always keep this in the graph. We have to do
// this primarily for validation: we want to validate that variable
// interpolations are valid even if there are no resources that
// depend on them.
if opts.Diff == nil || opts.Diff.Empty() {
return false
}
for _, v := range opts.Graph.UpEdges(opts.Vertex).List() {
// This is terrible, but I can't think of a better way to do this.
if dag.VertexName(v) == rootNodeName {
continue
}
return false
}
return true
}
// GraphNodeProxy impl.
func (n *GraphNodeConfigVariable) Proxy() bool {
return true

View File

@ -370,7 +370,7 @@ MISSING:
// be unknown. Instead, we return that the value is computed so
// that the graph can continue to refresh other nodes. It doesn't
// matter because the config isn't interpolated anyways.
if i.Operation == walkRefresh {
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy {
return config.UnknownVariableValue, nil
}
@ -433,6 +433,11 @@ func (i *Interpolater) computeResourceMultiVariable(
continue
}
// If any value is unknown, the whole thing is unknown
if attr == config.UnknownVariableValue {
return config.UnknownVariableValue, nil
}
values = append(values, attr)
}
@ -441,7 +446,7 @@ func (i *Interpolater) computeResourceMultiVariable(
// be unknown. Instead, we return that the value is computed so
// that the graph can continue to refresh other nodes. It doesn't
// matter because the config isn't interpolated anyways.
if i.Operation == walkRefresh {
if i.Operation == walkRefresh || i.Operation == walkPlanDestroy {
return config.UnknownVariableValue, nil
}

View File

@ -136,6 +136,80 @@ func TestInterpolater_pathRoot(t *testing.T) {
})
}
func TestInterpolater_resourceVariable(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.web": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": "bar",
},
},
},
},
},
},
}
i := &Interpolater{
Module: testModule(t, "interpolate-resource-variable"),
State: state,
StateLock: lock,
}
scope := &InterpolationScope{
Path: rootModulePath,
}
testInterpolate(t, i, scope, "aws_instance.web.foo", ast.Variable{
Value: "bar",
Type: ast.TypeString,
})
}
func TestInterpolater_resourceVariableMulti(t *testing.T) {
lock := new(sync.RWMutex)
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.web": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{
"foo": config.UnknownVariableValue,
},
},
},
},
},
},
}
i := &Interpolater{
Module: testModule(t, "interpolate-resource-variable"),
State: state,
StateLock: lock,
}
scope := &InterpolationScope{
Path: rootModulePath,
}
testInterpolate(t, i, scope, "aws_instance.web.*.foo", ast.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
})
}
func testInterpolate(
t *testing.T, i *Interpolater,
scope *InterpolationScope,

View File

@ -104,24 +104,56 @@ func (s *State) ModuleByPath(path []string) *ModuleState {
// returning their full paths. These paths can be used with ModuleByPath
// to return the actual state.
func (s *State) ModuleOrphans(path []string, c *config.Config) [][]string {
// direct keeps track of what direct children we have both in our config
// and in our state. childrenKeys keeps track of what isn't an orphan.
direct := make(map[string]struct{})
childrenKeys := make(map[string]struct{})
if c != nil {
for _, m := range c.Modules {
childrenKeys[m.Name] = struct{}{}
direct[m.Name] = struct{}{}
}
}
// Go over the direct children and find any that aren't in our
// keys.
// Go over the direct children and find any that aren't in our keys.
var orphans [][]string
for _, m := range s.Children(path) {
if _, ok := childrenKeys[m.Path[len(m.Path)-1]]; ok {
key := m.Path[len(m.Path)-1]
// Record that we found this key as a direct child. We use this
// later to find orphan nested modules.
direct[key] = struct{}{}
// If we have a direct child still in our config, it is not an orphan
if _, ok := childrenKeys[key]; ok {
continue
}
orphans = append(orphans, m.Path)
}
// Find the orphans that are nested...
for _, m := range s.Modules {
// We only want modules that are at least grandchildren
if len(m.Path) < len(path)+2 {
continue
}
// If it isn't part of our tree, continue
if !reflect.DeepEqual(path, m.Path[:len(path)]) {
continue
}
// If we have the direct child, then just skip it.
key := m.Path[len(path)]
if _, ok := direct[key]; ok {
continue
}
// Add this orphan
orphans = append(orphans, m.Path[:len(path)+1])
}
return orphans
}

View File

@ -85,6 +85,28 @@ func TestStateModuleOrphans(t *testing.T) {
}
}
func TestStateModuleOrphans_nested(t *testing.T) {
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: RootModulePath,
},
&ModuleState{
Path: []string{RootModuleName, "foo", "bar"},
},
},
}
actual := state.ModuleOrphans(RootModulePath, nil)
expected := [][]string{
[]string{RootModuleName, "foo"},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
func TestStateModuleOrphans_nilConfig(t *testing.T) {
state := &State{
Modules: []*ModuleState{

View File

@ -483,6 +483,11 @@ const testTerraformApplyDestroyStr = `
<no state>
`
const testTerraformApplyDestroyNestedModuleStr = `
module.child.subchild:
<no state>
`
const testTerraformApplyErrorStr = `
aws_instance.bar:
ID = bar

View File

@ -0,0 +1,5 @@
variable "value" {}
resource "aws_instance" "bar" {
value = "${var.value}"
}

View File

@ -0,0 +1,6 @@
resource "aws_instance" "foo" {}
module "child" {
source = "./child"
value = "${aws_instance.foo.output}"
}

View File

@ -0,0 +1,3 @@
module "subchild" {
source = "./subchild"
}

View File

@ -0,0 +1,5 @@
/*
module "subsubchild" {
source = "./subsubchild"
}
*/

View File

@ -0,0 +1 @@
resource "aws_instance" "bar" {}

View File

@ -0,0 +1,3 @@
module "child" {
source = "./child"
}

View File

@ -0,0 +1,3 @@
module "subchild" {
source = "./subchild"
}

View File

@ -0,0 +1 @@
resource "aws_instance" "bar" {}

View File

@ -0,0 +1,5 @@
/*
module "child" {
source = "./child"
}
*/

View File

@ -0,0 +1 @@
resource "aws_instance" "web" {}

104
terraform/transform_noop.go Normal file
View File

@ -0,0 +1,104 @@
package terraform
import (
"github.com/hashicorp/terraform/dag"
)
// GraphNodeNoopPrunable can be implemented by nodes that can be
// pruned if they are noops.
type GraphNodeNoopPrunable interface {
Noop(*NoopOpts) bool
}
// NoopOpts are the options available to determine if your node is a noop.
type NoopOpts struct {
Graph *Graph
Vertex dag.Vertex
Diff *Diff
State *State
ModDiff *ModuleDiff
ModState *ModuleState
}
// PruneNoopTransformer is a graph transform that prunes nodes that
// consider themselves no-ops. This is done to both simplify the graph
// as well as to remove graph nodes that might otherwise cause problems
// during the graph run. Therefore, this transformer isn't completely
// an optimization step, and can instead be considered critical to
// Terraform operations.
//
// Example of the above case: variables for modules interpolate their values.
// Interpolation will fail on destruction (since attributes are being deleted),
// but variables shouldn't even eval if there is nothing that will consume
// the variable. Therefore, variables can note that they can be omitted
// safely in this case.
//
// The PruneNoopTransformer will prune nodes depth first, and will automatically
// create connect through the dependencies of pruned nodes. For example,
// if we have a graph A => B => C (A depends on B, etc.), and B decides to
// be removed, we'll still be left with A => C; the edge will be properly
// connected.
type PruneNoopTransformer struct {
Diff *Diff
State *State
}
func (t *PruneNoopTransformer) Transform(g *Graph) error {
// Find the leaves.
leaves := make([]dag.Vertex, 0, 10)
for _, v := range g.Vertices() {
if g.DownEdges(v).Len() == 0 {
leaves = append(leaves, v)
}
}
// Do a depth first walk from the leaves and remove things.
return g.ReverseDepthFirstWalk(leaves, func(v dag.Vertex, depth int) error {
// We need a prunable
pn, ok := v.(GraphNodeNoopPrunable)
if !ok {
return nil
}
// Start building the noop opts
path := g.Path
if pn, ok := v.(GraphNodeSubPath); ok {
path = pn.Path()
}
var modDiff *ModuleDiff
var modState *ModuleState
if t.Diff != nil {
modDiff = t.Diff.ModuleByPath(path)
}
if t.State != nil {
modState = t.State.ModuleByPath(path)
}
// Determine if its a noop. If it isn't, just return
noop := pn.Noop(&NoopOpts{
Graph: g,
Vertex: v,
Diff: t.Diff,
State: t.State,
ModDiff: modDiff,
ModState: modState,
})
if !noop {
return nil
}
// It is a noop! We first preserve edges.
up := g.UpEdges(v).List()
for _, downV := range g.DownEdges(v).List() {
for _, upV := range up {
g.Connect(dag.BasicEdge(upV, downV))
}
}
// Then remove it
g.Remove(v)
return nil
})
}

View File

@ -0,0 +1,54 @@
package terraform
import (
"strings"
"testing"
"github.com/hashicorp/terraform/dag"
)
func TestPruneNoopTransformer(t *testing.T) {
g := Graph{Path: RootModulePath}
a := &testGraphNodeNoop{NameValue: "A"}
b := &testGraphNodeNoop{NameValue: "B", Value: true}
c := &testGraphNodeNoop{NameValue: "C"}
g.Add(a)
g.Add(b)
g.Add(c)
g.Connect(dag.BasicEdge(a, b))
g.Connect(dag.BasicEdge(b, c))
{
tf := &PruneNoopTransformer{}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformPruneNoopStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
const testTransformPruneNoopStr = `
A
C
C
`
type testGraphNodeNoop struct {
NameValue string
Value bool
}
func (v *testGraphNodeNoop) Name() string {
return v.NameValue
}
func (v *testGraphNodeNoop) Noop(*NoopOpts) bool {
return v.Value
}

View File

@ -100,9 +100,14 @@ func (t *OrphanTransformer) Transform(g *Graph) error {
moduleOrphans := t.State.ModuleOrphans(g.Path, config)
moduleVertexes := make([]dag.Vertex, len(moduleOrphans))
for i, path := range moduleOrphans {
var deps []string
if s := t.State.ModuleByPath(path); s != nil {
deps = s.Dependencies
}
moduleVertexes[i] = g.Add(&graphNodeOrphanModule{
Path: path,
dependentOn: t.State.ModuleByPath(path).Dependencies,
dependentOn: deps,
})
}
@ -356,3 +361,9 @@ func (n *graphNodeOrphanResourceFlat) CreateBeforeDestroy() bool {
func (n *graphNodeOrphanResourceFlat) CreateNode() dag.Vertex {
return n
}
func (n *graphNodeOrphanResourceFlat) ProvidedBy() []string {
return modulePrefixList(
n.graphNodeOrphanResource.ProvidedBy(),
modulePrefixStr(n.PathValue))
}

View File

@ -2,6 +2,8 @@ package terraform
import "github.com/hashicorp/terraform/dag"
const rootNodeName = "root"
// RootTransformer is a GraphTransformer that adds a root to the graph.
type RootTransformer struct{}
@ -32,7 +34,7 @@ func (t *RootTransformer) Transform(g *Graph) error {
type graphNodeRoot struct{}
func (n graphNodeRoot) Name() string {
return "root"
return rootNodeName
}
func (n graphNodeRoot) Flatten(p []string) (dag.Vertex, error) {

View File

@ -1,7 +1,7 @@
package terraform
// The main version number that is being run at the moment.
const Version = "0.6.1"
const Version = "0.6.2"
// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release

View File

@ -57,7 +57,7 @@ resource "aws_elb" "bar" {
The following arguments are supported:
* `name` - (Required) The name of the ELB
* `name` - (Optional) The name of the ELB. By default generated by terraform.
* `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in.
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
* `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB.

View File

@ -32,6 +32,7 @@ The following arguments are supported:
* `private_ips` - (Optional) List of private IPs to assign to the ENI.
* `security_groups` - (Optional) List of security group IDs to assign to the ENI.
* `attachment` - (Required) Block to define the attachment of the ENI. Documented below.
* `source_dest_check` - (Optional) Whether to enable source destination checking for the ENI. Default true.
* `tags` - (Optional) A mapping of tags to assign to the resource.
The `attachment` block supports:
@ -47,5 +48,6 @@ The following attributes are exported:
* `private_ips` - List of private IPs assigned to the ENI.
* `security_groups` - List of security groups attached to the ENI.
* `attachment` - Block defining the attachment of the ENI.
* `source_dest_check` - Whether source destination checking is enabled
* `tags` - Tags assigned to the ENI.

View File

@ -67,3 +67,4 @@ The following attributes are exported:
* `hosted_zone_id` - The [Route 53 Hosted Zone ID](http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_website_region_endpoints) for this bucket's region.
* `region` - The AWS region this bucket resides in.
* `website_endpoint` - The website endpoint, if the bucket is configured with a website. If not, this will be an empty string.
* `website_domain` - The domain of the website endpoint, if the bucket is configured with a website. If not, this will be an empty string. This is used to create Route 53 alias records.

View File

@ -10,12 +10,39 @@
<a href="/docs/providers/aws/index.html">AWS Provider</a>
</li>
<li<%= sidebar_current(/^docs-aws-resource/) %>>
<a href="#">Resources</a>
<li<%= sidebar_current(/^docs-aws-resource-cloudwatch/) %>>
<a href="#">CloudWatch Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-app-cookie-stickiness-policy") %>>
<li<%= sidebar_current("docs-aws-resource-cloudwatch-metric-alarm") %>>
<a href="/docs/providers/aws/r/cloudwatch_metric_alarm.html">aws_cloudwatch_metric_alarm</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-dynamodb/) %>>
<a href="#">DynamoDB Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-dynamodb-table") %>>
<a href="/docs/providers/aws/r/dynamodb_table.html">aws_dynamodb_table</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-(app|autoscaling|ebs|elb|eip|instance|launch|lb|proxy|spot|volume)/) %>>
<a href="#">EC2 Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-app-cookie-stickiness-policy") %>>
<a href="/docs/providers/aws/r/app_cookie_stickiness_policy.html">aws_app_cookie_stickiness_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-autoscaling-group") %>>
<a href="/docs/providers/aws/r/autoscaling_group.html">aws_autoscaling_group</a>
</li>
@ -28,38 +55,51 @@
<a href="/docs/providers/aws/r/autoscaling_policy.html">aws_autoscaling_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-cloudwatch-metric-alarm") %>>
<a href="/docs/providers/aws/r/cloudwatch_metric_alarm.html">aws_cloudwatch_metric_alarm</a>
</li>
<li<%= sidebar_current("docs-aws-resource-customer-gateway") %>>
<a href="/docs/providers/aws/r/customer_gateway.html">aws_customer_gateway</a>
</li>
<li<%= sidebar_current("docs-aws-resource-db-instance") %>>
<a href="/docs/providers/aws/r/db_instance.html">aws_db_instance</a>
</li>
<li<%= sidebar_current("docs-aws-resource-db-parameter-group") %>>
<a href="/docs/providers/aws/r/db_parameter_group.html">aws_db_parameter_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-db-security-group") %>>
<a href="/docs/providers/aws/r/db_security_group.html">aws_db_security_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-db-subnet-group") %>>
<a href="/docs/providers/aws/r/db_subnet_group.html">aws_db_subnet_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-dynamodb-table") %>>
<a href="/docs/providers/aws/r/dynamodb_table.html">aws_dynamodb_table</a>
</li>
<li<%= sidebar_current("docs-aws-resource-ebs-volume") %>>
<a href="/docs/providers/aws/r/ebs_volume.html">aws_ebs_volume</a>
</li>
<li<%= sidebar_current("docs-aws-resource-eip") %>>
<a href="/docs/providers/aws/r/eip.html">aws_eip</a>
</li>
<li<%= sidebar_current("docs-aws-resource-elb") %>>
<a href="/docs/providers/aws/r/elb.html">aws_elb</a>
</li>
<li<%= sidebar_current("docs-aws-resource-instance") %>>
<a href="/docs/providers/aws/r/instance.html">aws_instance</a>
</li>
<li<%= sidebar_current("docs-aws-resource-launch-configuration") %>>
<a href="/docs/providers/aws/r/launch_configuration.html">aws_launch_configuration</a>
</li>
<li<%= sidebar_current("docs-aws-resource-lb-cookie-stickiness-policy") %>>
<a href="/docs/providers/aws/r/lb_cookie_stickiness_policy.html">aws_lb_cookie_stickiness_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-proxy-protocol-policy") %>>
<a href="/docs/providers/aws/r/proxy_protocol_policy.html">aws_proxy_protocol_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-spot-instance-request") %>>
<a href="/docs/providers/aws/r/spot_instance_request.html">aws_spot_instance_request</a>
</li>
<li<%= sidebar_current("docs-aws-resource-volume-attachment") %>>
<a href="/docs/providers/aws/r/volume_attachment.html">aws_volume_attachment</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-ecs/) %>>
<a href="#">ECS Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-ecs-cluster") %>>
<a href="/docs/providers/aws/r/ecs_cluster.html">aws_ecs_cluster</a>
</li>
@ -72,10 +112,14 @@
<a href="/docs/providers/aws/r/ecs_task_definition.html">aws_ecs_task_definition</a>
</li>
<li<%= sidebar_current("docs-aws-resource-eip") %>>
<a href="/docs/providers/aws/r/eip.html">aws_eip</a>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-elasticache/) %>>
<a href="#">ElastiCache Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-elasticache-cluster") %>>
<a href="/docs/providers/aws/r/elasticache_cluster.html">aws_elasticache_cluster</a>
</li>
@ -92,13 +136,13 @@
<a href="/docs/providers/aws/r/elasticache_subnet_group.html">aws_elasticache_subnet_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-elb") %>>
<a href="/docs/providers/aws/r/elb.html">aws_elb</a>
</ul>
</li>
<li<%= sidebar_current("docs-aws-resource-flow-log") %>>
<a href="/docs/providers/aws/r/flow_log.html">aws_flow_log</a>
</li>
<li<%= sidebar_current(/^docs-aws-resource-iam/) %>>
<a href="#">IAM Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-iam-access-key") %>>
<a href="/docs/providers/aws/r/iam_access_key.html">aws_iam_access_key</a>
@ -147,50 +191,62 @@
<a href="/docs/providers/aws/r/iam_user_policy.html">aws_iam_user_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-instance") %>>
<a href="/docs/providers/aws/r/instance.html">aws_instance</a>
</ul>
</li>
<li<%= sidebar_current("docs-aws-resource-internet-gateway") %>>
<a href="/docs/providers/aws/r/internet_gateway.html">aws_internet_gateway</a>
</li>
<li<%= sidebar_current("docs-aws-resource-key-pair") %>>
<a href="/docs/providers/aws/r/key_pair.html">aws_key_pair</a>
</li>
<li<%= sidebar_current(/^docs-aws-resource-kinesis/) %>>
<a href="#">Kinesis Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-kinesis-stream") %>>
<a href="/docs/providers/aws/r/kinesis_stream.html">aws_kinesis_stream</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-lambda/) %>>
<a href="#">Lambda Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-lambda-function") %>>
<a href="/docs/providers/aws/r/lambda_function.html">aws_lambda_function</a>
</li>
<li<%= sidebar_current("docs-aws-resource-launch-configuration") %>>
<a href="/docs/providers/aws/r/launch_configuration.html">aws_launch_configuration</a>
</ul>
</li>
<li<%= sidebar_current("docs-aws-resource-lb-cookie-stickiness-policy") %>>
<a href="/docs/providers/aws/r/lb_cookie_stickiness_policy.html">aws_lb_cookie_stickiness_policy</a>
<li<%= sidebar_current(/^docs-aws-resource-db/) %>>
<a href="#">RDS Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-db-instance") %>>
<a href="/docs/providers/aws/r/db_instance.html">aws_db_instance</a>
</li>
<li<%= sidebar_current("docs-aws-resource-main-route-table-assoc") %>>
<a href="/docs/providers/aws/r/main_route_table_assoc.html">aws_main_route_table_association</a>
<li<%= sidebar_current("docs-aws-resource-db-parameter-group") %>>
<a href="/docs/providers/aws/r/db_parameter_group.html">aws_db_parameter_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-network-acl") %>>
<a href="/docs/providers/aws/r/network_acl.html">aws_network_acl</a>
<li<%= sidebar_current("docs-aws-resource-db-security-group") %>>
<a href="/docs/providers/aws/r/db_security_group.html">aws_db_security_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-network-interface") %>>
<a href="/docs/providers/aws/r/network_interface.html">aws_network_interface</a>
<li<%= sidebar_current("docs-aws-resource-db-subnet-group") %>>
<a href="/docs/providers/aws/r/db_subnet_group.html">aws_db_subnet_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-proxy-protocol-policy") %>>
<a href="/docs/providers/aws/r/proxy_protocol_policy.html">aws_proxy_protocol_policy</a>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-route53/) %>>
<a href="#">Route53 Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-route53-delegation-set") %>>
<a href="/docs/providers/aws/r/route53_delegation_set.html">aws_route53_delegation_set</a>
</li>
@ -211,6 +267,82 @@
<a href="/docs/providers/aws/r/route53_zone_association.html">aws_route53_zone_association</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-s3/) %>>
<a href="#">S3 Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-s3-bucket") %>>
<a href="/docs/providers/aws/r/s3_bucket.html">aws_s3_bucket</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-sns/) %>>
<a href="#">SNS Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-sns-topic") %>>
<a href="/docs/providers/aws/r/sns_topic.html">aws_sns_topic</a>
</li>
<li<%= sidebar_current("docs-aws-resource-sns-topic-subscription") %>>
<a href="/docs/providers/aws/r/sns_topic_subscription.html">aws_sns_topic_subscription</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-sqs/) %>>
<a href="#">SQS Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-sqs-queue") %>>
<a href="/docs/providers/aws/r/sqs_queue.html">aws_sqs_queue</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-(customer|flow|internet-gateway|key-pair|main-route|network|route-|security-group|subnet|vpc|vpn)/) %>>
<a href="#">VPC Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-customer-gateway") %>>
<a href="/docs/providers/aws/r/customer_gateway.html">aws_customer_gateway</a>
</li>
<li<%= sidebar_current("docs-aws-resource-flow-log") %>>
<a href="/docs/providers/aws/r/flow_log.html">aws_flow_log</a>
</li>
<li<%= sidebar_current("docs-aws-resource-internet-gateway") %>>
<a href="/docs/providers/aws/r/internet_gateway.html">aws_internet_gateway</a>
</li>
<li<%= sidebar_current("docs-aws-resource-key-pair") %>>
<a href="/docs/providers/aws/r/key_pair.html">aws_key_pair</a>
</li>
<li<%= sidebar_current("docs-aws-resource-main-route-table-assoc") %>>
<a href="/docs/providers/aws/r/main_route_table_assoc.html">aws_main_route_table_association</a>
</li>
<li<%= sidebar_current("docs-aws-resource-network-acl") %>>
<a href="/docs/providers/aws/r/network_acl.html">aws_network_acl</a>
</li>
<li<%= sidebar_current("docs-aws-resource-network-interface") %>>
<a href="/docs/providers/aws/r/network_interface.html">aws_network_interface</a>
</li>
<li<%= sidebar_current("docs-aws-resource-route-table|") %>>
<a href="/docs/providers/aws/r/route_table.html">aws_route_table</a>
</li>
@ -219,10 +351,6 @@
<a href="/docs/providers/aws/r/route_table_association.html">aws_route_table_association</a>
</li>
<li<%= sidebar_current("docs-aws-resource-s3-bucket") %>>
<a href="/docs/providers/aws/r/s3_bucket.html">aws_s3_bucket</a>
</li>
<li<%= sidebar_current("docs-aws-resource-security-group") %>>
<a href="/docs/providers/aws/r/security_group.html">aws_security_group</a>
</li>
@ -231,30 +359,10 @@
<a href="/docs/providers/aws/r/security_group_rule.html">aws_security_group_rule</a>
</li>
<li<%= sidebar_current("docs-aws-resource-sns-topic") %>>
<a href="/docs/providers/aws/r/sns_topic.html">aws_sns_topic</a>
</li>
<li<%= sidebar_current("docs-aws-resource-sns-topic-subscription") %>>
<a href="/docs/providers/aws/r/sns_topic_subscription.html">aws_sns_topic_subscription</a>
</li>
<li<%= sidebar_current("docs-aws-resource-spot-instance-request") %>>
<a href="/docs/providers/aws/r/spot_instance_request.html">aws_spot_instance_request</a>
</li>
<li<%= sidebar_current("docs-aws-resource-sqs-queue") %>>
<a href="/docs/providers/aws/r/sqs_queue.html">aws_sqs_queue</a>
</li>
<li<%= sidebar_current("docs-aws-resource-subnet") %>>
<a href="/docs/providers/aws/r/subnet.html">aws_subnet</a>
</li>
<li<%= sidebar_current("docs-aws-resource-volume-attachment") %>>
<a href="/docs/providers/aws/r/volume_attachment.html">aws_volume_attachment</a>
</li>
<li<%= sidebar_current("docs-aws-resource-vpc") %>>
<a href="/docs/providers/aws/r/vpc.html">aws_vpc</a>
</li>
@ -286,8 +394,10 @@
<li<%= sidebar_current("docs-aws-resource-vpn-gateway") %>>
<a href="/docs/providers/aws/r/vpn_gateway.html">aws_vpn_gateway</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>

View File

@ -54,7 +54,7 @@
</li>
<li<%= sidebar_current("docs-cloudstack-resource-secondary-ipaddress") %>>
<a href="/docs/providers/cloudstack/r/secondary_ipaddres.html">cloudstack_secondary_ipaddress</a>
<a href="/docs/providers/cloudstack/r/secondary_ipaddress.html">cloudstack_secondary_ipaddress</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-ssh-keypair") %>>