diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc6c6e60..8470f90b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ IMPROVEMENTS: info. [GH-1029] * **New config function: `split`** - Split a value based on a delimiter. This is useful for faking lists as parameters to modules. + * **New resource: `digitalocean_ssh_key`** [GH-1074] * core: The serial of the state is only updated if there is an actual change. This will lower the amount of state changing on things like refresh. @@ -48,7 +49,8 @@ BUG FIXES: "resource.0" would ignore the latter completely. [GH-1086] * providers/aws: manually deleted VPC removes it from the state * providers/aws: `source_dest_check` regression fixed (now works). [GH-1020] - * providers/aws: Longer wait times for DB instances + * providers/aws: Longer wait times for DB instances. + * providers/aws: Longer wait times for route53 records (30 mins). [GH-1164] * providers/digitalocean: Waits until droplet is ready to be destroyed [GH-1057] * providers/digitalocean: More lenient about 404's while waiting [GH-1062] * providers/google: Network data in state was not being stored. [GH-1095] diff --git a/Makefile b/Makefile index fdb9ab3d6..21a54a8f8 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ bin: generate dev: generate @TF_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'" +quickdev: generate + @TF_QUICKDEV=1 TF_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'" + # test runs the unit tests and vets the code test: generate TF_ACC= go test $(TEST) $(TESTARGS) -timeout=30s -parallel=4 diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 8bc9adab5..672af6f07 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -3,21 +3,16 @@ package aws import ( "fmt" "log" - "strings" - "unicode" "github.com/hashicorp/terraform/helper/multierror" - "github.com/mitchellh/goamz/aws" - "github.com/mitchellh/goamz/ec2" - awsGo "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/aws" "github.com/hashicorp/aws-sdk-go/gen/autoscaling" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/aws-sdk-go/gen/elb" "github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/aws-sdk-go/gen/route53" "github.com/hashicorp/aws-sdk-go/gen/s3" - - awsEC2 "github.com/hashicorp/aws-sdk-go/gen/ec2" ) type Config struct { @@ -29,7 +24,6 @@ type Config struct { type AWSClient struct { ec2conn *ec2.EC2 - awsEC2conn *awsEC2.EC2 elbconn *elb.ELB autoscalingconn *autoscaling.AutoScaling s3conn *s3.S3 @@ -45,14 +39,9 @@ func (c *Config) Client() (interface{}, error) { // Get the auth and region. This can fail if keys/regions were not // specified and we're attempting to use the environment. var errs []error - log.Println("[INFO] Building AWS auth structure") - auth, err := c.AWSAuth() - if err != nil { - errs = append(errs, err) - } log.Println("[INFO] Building AWS region structure") - region, err := c.AWSRegion() + err := c.ValidateRegion() if err != nil { errs = append(errs, err) } @@ -62,10 +51,9 @@ func (c *Config) Client() (interface{}, error) { // bucket storage in S3 client.region = c.Region - creds := awsGo.Creds(c.AccessKey, c.SecretKey, c.Token) + log.Println("[INFO] Building AWS auth structure") + creds := aws.Creds(c.AccessKey, c.SecretKey, c.Token) - log.Println("[INFO] Initializing EC2 connection") - client.ec2conn = ec2.New(auth, region) log.Println("[INFO] Initializing ELB connection") client.elbconn = elb.New(creds, c.Region, nil) log.Println("[INFO] Initializing AutoScaling connection") @@ -80,8 +68,8 @@ func (c *Config) Client() (interface{}, error) { // See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html log.Println("[INFO] Initializing Route53 connection") client.r53conn = route53.New(creds, "us-east-1", nil) - log.Println("[INFO] Initializing AWS-GO EC2 Connection") - client.awsEC2conn = awsEC2.New(creds, c.Region, nil) + log.Println("[INFO] Initializing EC2 Connection") + client.ec2conn = ec2.New(creds, c.Region, nil) } if len(errs) > 0 { @@ -91,54 +79,17 @@ func (c *Config) Client() (interface{}, error) { return &client, nil } -// AWSAuth returns a valid aws.Auth object for access to AWS services, or -// an error if the authentication couldn't be resolved. -// -// TODO(mitchellh): Test in some way. -func (c *Config) AWSAuth() (aws.Auth, error) { - auth, err := aws.GetAuth(c.AccessKey, c.SecretKey) - if err == nil { - // Store the accesskey and secret that we got... - c.AccessKey = auth.AccessKey - c.SecretKey = auth.SecretKey - c.Token = auth.Token - } - - return auth, err -} - // IsValidRegion returns true if the configured region is a valid AWS // region and false if it's not -func (c *Config) IsValidRegion() bool { +func (c *Config) ValidateRegion() error { var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1", "eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "sa-east-1", "cn-north-1", "us-gov-west-1"} for _, valid := range regions { if c.Region == valid { - return true + return nil } } - return false -} - -// AWSRegion returns the configured region. -// -// TODO(mitchellh): Test in some way. -func (c *Config) AWSRegion() (aws.Region, error) { - if c.Region != "" { - if c.IsValidRegion() { - return aws.Regions[c.Region], nil - } else { - return aws.Region{}, fmt.Errorf("Not a valid region: %s", c.Region) - } - } - - md, err := aws.GetMetaData("placement/availability-zone") - if err != nil { - return aws.Region{}, err - } - - region := strings.TrimRightFunc(string(md), unicode.IsLetter) - return aws.Regions[region], nil + return fmt.Errorf("Not a valid region: %s", c.Region) } diff --git a/builtin/providers/aws/network_acl_entry.go b/builtin/providers/aws/network_acl_entry.go index 8ce88d81a..09954083a 100644 --- a/builtin/providers/aws/network_acl_entry.go +++ b/builtin/providers/aws/network_acl_entry.go @@ -2,11 +2,14 @@ package aws import ( "fmt" - "github.com/mitchellh/goamz/ec2" + "strconv" + + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" ) -func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.NetworkAclEntry, error) { - entries := make([]ec2.NetworkAclEntry, 0, len(configured)) +func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.NetworkACLEntry, error) { + entries := make([]ec2.NetworkACLEntry, 0, len(configured)) for _, eRaw := range configured { data := eRaw.(map[string]interface{}) protocol := data["protocol"].(string) @@ -15,16 +18,16 @@ func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2. return nil, fmt.Errorf("Invalid Protocol %s for rule %#v", protocol, data) } p := extractProtocolInteger(data["protocol"].(string)) - e := ec2.NetworkAclEntry{ - Protocol: p, - PortRange: ec2.PortRange{ - From: data["from_port"].(int), - To: data["to_port"].(int), + e := ec2.NetworkACLEntry{ + Protocol: aws.String(strconv.Itoa(p)), + PortRange: &ec2.PortRange{ + From: aws.Integer(data["from_port"].(int)), + To: aws.Integer(data["to_port"].(int)), }, - Egress: (entryType == "egress"), - RuleAction: data["action"].(string), - RuleNumber: data["rule_no"].(int), - CidrBlock: data["cidr_block"].(string), + Egress: aws.Boolean((entryType == "egress")), + RuleAction: aws.String(data["action"].(string)), + RuleNumber: aws.Integer(data["rule_no"].(int)), + CIDRBlock: aws.String(data["cidr_block"].(string)), } entries = append(entries, e) } @@ -33,17 +36,17 @@ func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2. } -func flattenNetworkAclEntries(list []ec2.NetworkAclEntry) []map[string]interface{} { +func flattenNetworkAclEntries(list []ec2.NetworkACLEntry) []map[string]interface{} { entries := make([]map[string]interface{}, 0, len(list)) for _, entry := range list { entries = append(entries, map[string]interface{}{ - "from_port": entry.PortRange.From, - "to_port": entry.PortRange.To, - "action": entry.RuleAction, - "rule_no": entry.RuleNumber, - "protocol": extractProtocolString(entry.Protocol), - "cidr_block": entry.CidrBlock, + "from_port": *entry.PortRange.From, + "to_port": *entry.PortRange.To, + "action": *entry.RuleAction, + "rule_no": *entry.RuleNumber, + "protocol": *entry.Protocol, + "cidr_block": *entry.CIDRBlock, }) } return entries diff --git a/builtin/providers/aws/network_acl_entry_test.go b/builtin/providers/aws/network_acl_entry_test.go index a2d60abb8..50f6fdc85 100644 --- a/builtin/providers/aws/network_acl_entry_test.go +++ b/builtin/providers/aws/network_acl_entry_test.go @@ -4,10 +4,11 @@ import ( "reflect" "testing" - "github.com/mitchellh/goamz/ec2" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" ) -func Test_expandNetworkAclEntry(t *testing.T) { +func Test_expandNetworkACLEntry(t *testing.T) { input := []interface{}{ map[string]interface{}{ "protocol": "tcp", @@ -28,30 +29,28 @@ func Test_expandNetworkAclEntry(t *testing.T) { } expanded, _ := expandNetworkAclEntries(input, "egress") - expected := []ec2.NetworkAclEntry{ - ec2.NetworkAclEntry{ - Protocol: 6, - PortRange: ec2.PortRange{ - From: 22, - To: 22, + expected := []ec2.NetworkACLEntry{ + ec2.NetworkACLEntry{ + Protocol: aws.String("6"), + PortRange: &ec2.PortRange{ + From: aws.Integer(22), + To: aws.Integer(22), }, - RuleAction: "deny", - RuleNumber: 1, - CidrBlock: "0.0.0.0/0", - Egress: true, - IcmpCode: ec2.IcmpCode{Code: 0, Type: 0}, + RuleAction: aws.String("deny"), + RuleNumber: aws.Integer(1), + CIDRBlock: aws.String("0.0.0.0/0"), + Egress: aws.Boolean(true), }, - ec2.NetworkAclEntry{ - Protocol: 6, - PortRange: ec2.PortRange{ - From: 443, - To: 443, + ec2.NetworkACLEntry{ + Protocol: aws.String("6"), + PortRange: &ec2.PortRange{ + From: aws.Integer(443), + To: aws.Integer(443), }, - RuleAction: "deny", - RuleNumber: 2, - CidrBlock: "0.0.0.0/0", - Egress: true, - IcmpCode: ec2.IcmpCode{Code: 0, Type: 0}, + RuleAction: aws.String("deny"), + RuleNumber: aws.Integer(2), + CIDRBlock: aws.String("0.0.0.0/0"), + Egress: aws.Boolean(true), }, } @@ -64,28 +63,28 @@ func Test_expandNetworkAclEntry(t *testing.T) { } -func Test_flattenNetworkAclEntry(t *testing.T) { +func Test_flattenNetworkACLEntry(t *testing.T) { - apiInput := []ec2.NetworkAclEntry{ - ec2.NetworkAclEntry{ - Protocol: 6, - PortRange: ec2.PortRange{ - From: 22, - To: 22, + apiInput := []ec2.NetworkACLEntry{ + ec2.NetworkACLEntry{ + Protocol: aws.String("tcp"), + PortRange: &ec2.PortRange{ + From: aws.Integer(22), + To: aws.Integer(22), }, - RuleAction: "deny", - RuleNumber: 1, - CidrBlock: "0.0.0.0/0", + RuleAction: aws.String("deny"), + RuleNumber: aws.Integer(1), + CIDRBlock: aws.String("0.0.0.0/0"), }, - ec2.NetworkAclEntry{ - Protocol: 6, - PortRange: ec2.PortRange{ - From: 443, - To: 443, + ec2.NetworkACLEntry{ + Protocol: aws.String("tcp"), + PortRange: &ec2.PortRange{ + From: aws.Integer(443), + To: aws.Integer(443), }, - RuleAction: "deny", - RuleNumber: 2, - CidrBlock: "0.0.0.0/0", + RuleAction: aws.String("deny"), + RuleNumber: aws.Integer(2), + CIDRBlock: aws.String("0.0.0.0/0"), }, } flattened := flattenNetworkAclEntries(apiInput) diff --git a/builtin/providers/aws/resource_aws_db_instance.go b/builtin/providers/aws/resource_aws_db_instance.go index b57dc13f3..e99744a0f 100644 --- a/builtin/providers/aws/resource_aws_db_instance.go +++ b/builtin/providers/aws/resource_aws_db_instance.go @@ -156,7 +156,6 @@ func resourceAwsDbInstance() *schema.Resource { "final_snapshot_identifier": &schema.Schema{ Type: schema.TypeString, Optional: true, - ForceNew: true, }, "db_subnet_group_name": &schema.Schema{ diff --git a/builtin/providers/aws/resource_aws_db_parameter_group.go b/builtin/providers/aws/resource_aws_db_parameter_group.go index cf40d3b26..a5eda1a64 100644 --- a/builtin/providers/aws/resource_aws_db_parameter_group.go +++ b/builtin/providers/aws/resource_aws_db_parameter_group.go @@ -152,7 +152,7 @@ func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) os := o.(*schema.Set) ns := n.(*schema.Set) - // Expand the "parameter" set to goamz compat []rds.Parameter + // Expand the "parameter" set to aws-sdk-go compat []rds.Parameter parameters, err := expandParameters(ns.Difference(os).List()) if err != nil { return err diff --git a/builtin/providers/aws/resource_aws_eip.go b/builtin/providers/aws/resource_aws_eip.go index 103f9bc5a..c78fec4c9 100644 --- a/builtin/providers/aws/resource_aws_eip.go +++ b/builtin/providers/aws/resource_aws_eip.go @@ -60,7 +60,7 @@ func resourceAwsEip() *schema.Resource { } func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn // By default, we're not in a VPC domainOpt := "" @@ -97,7 +97,7 @@ func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn domain := resourceAwsEipDomain(d) id := d.Id() @@ -148,7 +148,7 @@ func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { } func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn domain := resourceAwsEipDomain(d) @@ -181,7 +181,7 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn if err := resourceAwsEipRead(d, meta); err != nil { return err diff --git a/builtin/providers/aws/resource_aws_eip_test.go b/builtin/providers/aws/resource_aws_eip_test.go index 79e88b8f3..b9944366f 100644 --- a/builtin/providers/aws/resource_aws_eip_test.go +++ b/builtin/providers/aws/resource_aws_eip_test.go @@ -58,7 +58,7 @@ func TestAccAWSEIP_instance(t *testing.T) { } func testAccCheckAWSEIPDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_eip" { @@ -113,7 +113,7 @@ func testAccCheckAWSEIPExists(n string, res *ec2.Address) resource.TestCheckFunc return fmt.Errorf("No EIP ID is set") } - conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn if strings.Contains(rs.Primary.ID, "eipalloc") { req := &ec2.DescribeAddressesRequest{ diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index e5ed9f3cf..7898f2120 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -161,7 +161,7 @@ func resourceAwsElb() *schema.Resource { func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { elbconn := meta.(*AWSClient).elbconn - // Expand the "listener" set to goamz compat []elb.Listener + // Expand the "listener" set to aws-sdk-go compat []elb.Listener listeners, err := expandListeners(d.Get("listener").(*schema.Set).List()) if err != nil { return err diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index ee62d305e..74c7b5845 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -3,17 +3,18 @@ package aws import ( "bytes" "crypto/sha1" + "encoding/base64" "encoding/hex" "fmt" "log" - "strconv" "strings" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsInstance() *schema.Resource { @@ -258,7 +259,28 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { // Figure out user data userData := "" if v := d.Get("user_data"); v != nil { - userData = v.(string) + userData = base64.StdEncoding.EncodeToString([]byte(v.(string))) + } + + placement := &ec2.Placement{ + AvailabilityZone: aws.String(d.Get("availability_zone").(string)), + Tenancy: aws.String(d.Get("tenancy").(string)), + } + + iam := &ec2.IAMInstanceProfileSpecification{ + Name: aws.String(d.Get("iam_instance_profile").(string)), + } + + // Build the creation struct + runOpts := &ec2.RunInstancesRequest{ + ImageID: aws.String(d.Get("ami").(string)), + Placement: placement, + InstanceType: aws.String(d.Get("instance_type").(string)), + MaxCount: aws.Integer(1), + MinCount: aws.Integer(1), + UserData: aws.String(userData), + EBSOptimized: aws.Boolean(d.Get("ebs_optimized").(bool)), + IAMInstanceProfile: iam, } associatePublicIPAddress := false @@ -266,36 +288,65 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { associatePublicIPAddress = v.(bool) } - // Build the creation struct - runOpts := &ec2.RunInstances{ - ImageId: d.Get("ami").(string), - AvailZone: d.Get("availability_zone").(string), - InstanceType: d.Get("instance_type").(string), - KeyName: d.Get("key_name").(string), - SubnetId: d.Get("subnet_id").(string), - PrivateIPAddress: d.Get("private_ip").(string), - AssociatePublicIpAddress: associatePublicIPAddress, - UserData: []byte(userData), - EbsOptimized: d.Get("ebs_optimized").(bool), - IamInstanceProfile: d.Get("iam_instance_profile").(string), - Tenancy: d.Get("tenancy").(string), - } + // check for non-default Subnet, and cast it to a String + var hasSubnet bool + subnet, hasSubnet := d.GetOk("subnet_id") + subnetID := subnet.(string) + var groups []string if v := d.Get("security_groups"); v != nil { + // Security group names. + // For a nondefault VPC, you must use security group IDs instead. + // See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html for _, v := range v.(*schema.Set).List() { str := v.(string) - - var g ec2.SecurityGroup - if runOpts.SubnetId != "" { - g.Id = str - } else { - g.Name = str - } - - runOpts.SecurityGroups = append(runOpts.SecurityGroups, g) + groups = append(groups, str) } } + if hasSubnet && associatePublicIPAddress { + // If we have a non-default VPC / Subnet specified, we can flag + // AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided. + // You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise + // you get: Network interfaces and an instance-level subnet ID may not be specified on the same request + // You also need to attach Security Groups to the NetworkInterface instead of the instance, + // to avoid: Network interfaces and an instance-level security groups may not be specified on + // the same request + ni := ec2.InstanceNetworkInterfaceSpecification{ + AssociatePublicIPAddress: aws.Boolean(associatePublicIPAddress), + DeviceIndex: aws.Integer(0), + SubnetID: aws.String(subnetID), + } + + if v, ok := d.GetOk("private_ip"); ok { + ni.PrivateIPAddress = aws.String(v.(string)) + } + + if len(groups) > 0 { + ni.Groups = groups + } + + runOpts.NetworkInterfaces = []ec2.InstanceNetworkInterfaceSpecification{ni} + } else { + if subnetID != "" { + runOpts.SubnetID = aws.String(subnetID) + } + + if v, ok := d.GetOk("private_ip"); ok { + runOpts.PrivateIPAddress = aws.String(v.(string)) + } + if runOpts.SubnetID != nil && + *runOpts.SubnetID != "" { + runOpts.SecurityGroupIDs = groups + } else { + runOpts.SecurityGroups = groups + } + } + + if v, ok := d.GetOk("key_name"); ok { + runOpts.KeyName = aws.String(v.(string)) + } + blockDevices := make([]interface{}, 0) if v := d.Get("block_device"); v != nil { @@ -311,24 +362,27 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { } if len(blockDevices) > 0 { - runOpts.BlockDevices = make([]ec2.BlockDeviceMapping, len(blockDevices)) + runOpts.BlockDeviceMappings = make([]ec2.BlockDeviceMapping, len(blockDevices)) for i, v := range blockDevices { bd := v.(map[string]interface{}) - runOpts.BlockDevices[i].DeviceName = bd["device_name"].(string) - runOpts.BlockDevices[i].VolumeType = bd["volume_type"].(string) - runOpts.BlockDevices[i].VolumeSize = int64(bd["volume_size"].(int)) - runOpts.BlockDevices[i].DeleteOnTermination = bd["delete_on_termination"].(bool) - if v, ok := bd["virtual_name"].(string); ok { - runOpts.BlockDevices[i].VirtualName = v + runOpts.BlockDeviceMappings[i].DeviceName = aws.String(bd["device_name"].(string)) + runOpts.BlockDeviceMappings[i].EBS = &ec2.EBSBlockDevice{ + VolumeType: aws.String(bd["volume_type"].(string)), + VolumeSize: aws.Integer(bd["volume_size"].(int)), + DeleteOnTermination: aws.Boolean(bd["delete_on_termination"].(bool)), } - if v, ok := bd["snapshot_id"].(string); ok { - runOpts.BlockDevices[i].SnapshotId = v + + if v, ok := bd["virtual_name"].(string); ok { + runOpts.BlockDeviceMappings[i].VirtualName = aws.String(v) + } + if v, ok := bd["snapshot_id"].(string); ok && v != "" { + runOpts.BlockDeviceMappings[i].EBS.SnapshotID = aws.String(v) } if v, ok := bd["encrypted"].(bool); ok { - runOpts.BlockDevices[i].Encrypted = v + runOpts.BlockDeviceMappings[i].EBS.Encrypted = aws.Boolean(v) } - if v, ok := bd["iops"].(int); ok { - runOpts.BlockDevices[i].IOPS = int64(v) + if v, ok := bd["iops"].(int); ok && v > 0 { + runOpts.BlockDeviceMappings[i].EBS.IOPS = aws.Integer(v) } } } @@ -341,21 +395,21 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { } instance := &runResp.Instances[0] - log.Printf("[INFO] Instance ID: %s", instance.InstanceId) + log.Printf("[INFO] Instance ID: %s", *instance.InstanceID) // Store the resulting ID so we can look this up later - d.SetId(instance.InstanceId) + d.SetId(*instance.InstanceID) // Wait for the instance to become running so we can get some attributes // that aren't available until later. log.Printf( "[DEBUG] Waiting for instance (%s) to become running", - instance.InstanceId) + *instance.InstanceID) stateConf := &resource.StateChangeConf{ Pending: []string{"pending"}, Target: "running", - Refresh: InstanceStateRefreshFunc(ec2conn, instance.InstanceId), + Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceID), Timeout: 10 * time.Minute, Delay: 10 * time.Second, MinTimeout: 3 * time.Second, @@ -365,16 +419,18 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf( "Error waiting for instance (%s) to become ready: %s", - instance.InstanceId, err) + *instance.InstanceID, err) } instance = instanceRaw.(*ec2.Instance) // Initialize the connection info - d.SetConnInfo(map[string]string{ - "type": "ssh", - "host": instance.PublicIpAddress, - }) + if instance.PublicIPAddress != nil { + d.SetConnInfo(map[string]string{ + "type": "ssh", + "host": *instance.PublicIPAddress, + }) + } // Set our attributes if err := resourceAwsInstanceRead(d, meta); err != nil { @@ -388,11 +444,13 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - resp, err := ec2conn.Instances([]string{d.Id()}, ec2.NewFilter()) + resp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesRequest{ + InstanceIDs: []string{d.Id()}, + }) if err != nil { // If the instance was not found, return nil so that we can show // that the instance is gone. - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" { + if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" { d.SetId("") return nil } @@ -410,28 +468,33 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { instance := &resp.Reservations[0].Instances[0] // If the instance is terminated, then it is gone - if instance.State.Name == "terminated" { + if *instance.State.Name == "terminated" { d.SetId("") return nil } - d.Set("availability_zone", instance.AvailZone) + d.Set("availability_zone", instance.Placement.AvailabilityZone) d.Set("key_name", instance.KeyName) - d.Set("public_dns", instance.DNSName) - d.Set("public_ip", instance.PublicIpAddress) + d.Set("public_dns", instance.PublicDNSName) + d.Set("public_ip", instance.PublicIPAddress) d.Set("private_dns", instance.PrivateDNSName) - d.Set("private_ip", instance.PrivateIpAddress) - d.Set("subnet_id", instance.SubnetId) - d.Set("ebs_optimized", instance.EbsOptimized) + d.Set("private_ip", instance.PrivateIPAddress) + d.Set("subnet_id", instance.SubnetID) + if len(instance.NetworkInterfaces) > 0 { + d.Set("subnet_id", instance.NetworkInterfaces[0].SubnetID) + } else { + d.Set("subnet_id", instance.SubnetID) + } + d.Set("ebs_optimized", instance.EBSOptimized) d.Set("tags", tagsToMap(instance.Tags)) - d.Set("tenancy", instance.Tenancy) + d.Set("tenancy", instance.Placement.Tenancy) // Determine whether we're referring to security groups with // IDs or names. We use a heuristic to figure this out. By default, // we use IDs if we're in a VPC. However, if we previously had an // all-name list of security groups, we use names. Or, if we had any // IDs, we use IDs. - useID := instance.SubnetId != "" + useID := *instance.SubnetID != "" if v := d.Get("security_groups"); v != nil { match := false for _, v := range v.(*schema.Set).List() { @@ -448,24 +511,26 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { sgs := make([]string, len(instance.SecurityGroups)) for i, sg := range instance.SecurityGroups { if useID { - sgs[i] = sg.Id + sgs[i] = *sg.GroupID } else { - sgs[i] = sg.Name + sgs[i] = *sg.GroupName } } d.Set("security_groups", sgs) - blockDevices := make(map[string]ec2.BlockDevice) - for _, bd := range instance.BlockDevices { - blockDevices[bd.VolumeId] = bd + blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping) + for _, bd := range instance.BlockDeviceMappings { + blockDevices[*bd.EBS.VolumeID] = bd } volIDs := make([]string, 0, len(blockDevices)) - for volID := range blockDevices { - volIDs = append(volIDs, volID) + for _, vol := range blockDevices { + volIDs = append(volIDs, *vol.EBS.VolumeID) } - volResp, err := ec2conn.Volumes(volIDs, ec2.NewFilter()) + volResp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesRequest{ + VolumeIDs: volIDs, + }) if err != nil { return err } @@ -473,28 +538,25 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { nonRootBlockDevices := make([]map[string]interface{}, 0) rootBlockDevice := make([]interface{}, 0, 1) for _, vol := range volResp.Volumes { - volSize, err := strconv.Atoi(vol.Size) - if err != nil { - return err - } - blockDevice := make(map[string]interface{}) - blockDevice["device_name"] = blockDevices[vol.VolumeId].DeviceName - blockDevice["volume_type"] = vol.VolumeType - blockDevice["volume_size"] = volSize + blockDevice["device_name"] = *blockDevices[*vol.VolumeID].DeviceName + blockDevice["volume_type"] = *vol.VolumeType + blockDevice["volume_size"] = *vol.Size + if vol.IOPS != nil { + blockDevice["iops"] = *vol.IOPS + } blockDevice["delete_on_termination"] = - blockDevices[vol.VolumeId].DeleteOnTermination + *blockDevices[*vol.VolumeID].EBS.DeleteOnTermination // If this is the root device, save it. We stop here since we // can't put invalid keys into this map. - if blockDevice["device_name"] == instance.RootDeviceName { + if blockDevice["device_name"] == *instance.RootDeviceName { rootBlockDevice = []interface{}{blockDevice} continue } - blockDevice["snapshot_id"] = vol.SnapshotId - blockDevice["encrypted"] = vol.Encrypted - blockDevice["iops"] = vol.IOPS + blockDevice["snapshot_id"] = *vol.SnapshotID + blockDevice["encrypted"] = *vol.Encrypted nonRootBlockDevices = append(nonRootBlockDevices, blockDevice) } d.Set("block_device", nonRootBlockDevices) @@ -505,13 +567,17 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - opts := new(ec2.ModifyInstance) - - opts.SetSourceDestCheck = true - opts.SourceDestCheck = d.Get("source_dest_check").(bool) + opts := new(ec2.ModifyInstanceAttributeRequest) log.Printf("[INFO] Modifying instance %s: %#v", d.Id(), opts) - if _, err := ec2conn.ModifyInstance(d.Id(), opts); err != nil { + err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeRequest{ + InstanceID: aws.String(d.Id()), + SourceDestCheck: &ec2.AttributeBooleanValue{ + Value: aws.Boolean(d.Get("source_dest_check").(bool)), + }, + }) + + if err != nil { return err } @@ -531,7 +597,10 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn log.Printf("[INFO] Terminating instance: %s", d.Id()) - if _, err := ec2conn.TerminateInstances([]string{d.Id()}); err != nil { + req := &ec2.TerminateInstancesRequest{ + InstanceIDs: []string{d.Id()}, + } + if _, err := ec2conn.TerminateInstances(req); err != nil { return fmt.Errorf("Error terminating instance: %s", err) } @@ -563,9 +632,11 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error { // an EC2 instance. func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.Instances([]string{instanceID}, ec2.NewFilter()) + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{ + InstanceIDs: []string{instanceID}, + }) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" { + if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" { // Set this to nil as if we didn't find anything. resp = nil } else { @@ -581,7 +652,7 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRe } i := &resp.Reservations[0].Instances[0] - return i, i.State.Name, nil + return i, *i.State.Name, nil } } diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index e25d23542..897143520 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -5,24 +5,25 @@ import ( "reflect" "testing" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestAccAWSInstance_normal(t *testing.T) { var v ec2.Instance testCheck := func(*terraform.State) error { - if v.AvailZone != "us-west-2a" { - return fmt.Errorf("bad availability zone: %#v", v.AvailZone) + if *v.Placement.AvailabilityZone != "us-west-2a" { + return fmt.Errorf("bad availability zone: %#v", *v.Placement.AvailabilityZone) } if len(v.SecurityGroups) == 0 { return fmt.Errorf("no security groups: %#v", v.SecurityGroups) } - if v.SecurityGroups[0].Name != "tf_test_foo" { + if *v.SecurityGroups[0].GroupName != "tf_test_foo" { return fmt.Errorf("no security groups: %#v", v.SecurityGroups) } @@ -73,9 +74,9 @@ func TestAccAWSInstance_blockDevices(t *testing.T) { return func(*terraform.State) error { // Map out the block devices by name, which should be unique. - blockDevices := make(map[string]ec2.BlockDevice) - for _, blockDevice := range v.BlockDevices { - blockDevices[blockDevice.DeviceName] = blockDevice + blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping) + for _, blockDevice := range v.BlockDeviceMappings { + blockDevices[*blockDevice.DeviceName] = blockDevice } // Check if the root block device exists. @@ -147,8 +148,8 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) { testCheck := func(enabled bool) resource.TestCheckFunc { return func(*terraform.State) error { - if v.SourceDestCheck != enabled { - return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck) + if *v.SourceDestCheck != enabled { + return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck) } return nil @@ -206,7 +207,26 @@ func TestAccAWSInstance_vpc(t *testing.T) { }) } -func TestAccInstance_tags(t *testing.T) { +func TestAccInstance_NetworkInstanceSecurityGroups(t *testing.T) { + var v ec2.Instance + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceNetworkInstanceSecurityGroups, + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists( + "aws_instance.foo_instance", &v), + ), + }, + }, + }) +} + +func TestAccAWSInstance_tags(t *testing.T) { var v ec2.Instance resource.Test(t, resource.TestCase{ @@ -236,13 +256,13 @@ func TestAccInstance_tags(t *testing.T) { }) } -func TestAccInstance_privateIP(t *testing.T) { +func TestAccAWSInstance_privateIP(t *testing.T) { var v ec2.Instance testCheckPrivateIP := func() resource.TestCheckFunc { return func(*terraform.State) error { - if v.PrivateIpAddress != "10.1.1.42" { - return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress) + if *v.PrivateIPAddress != "10.1.1.42" { + return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress) } return nil @@ -265,13 +285,13 @@ func TestAccInstance_privateIP(t *testing.T) { }) } -func TestAccInstance_associatePublicIPAndPrivateIP(t *testing.T) { +func TestAccAWSInstance_associatePublicIPAndPrivateIP(t *testing.T) { var v ec2.Instance testCheckPrivateIP := func() resource.TestCheckFunc { return func(*terraform.State) error { - if v.PrivateIpAddress != "10.1.1.42" { - return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress) + if *v.PrivateIPAddress != "10.1.1.42" { + return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress) } return nil @@ -303,8 +323,9 @@ func testAccCheckInstanceDestroy(s *terraform.State) error { } // Try to find the resource - resp, err := conn.Instances( - []string{rs.Primary.ID}, ec2.NewFilter()) + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{ + InstanceIDs: []string{rs.Primary.ID}, + }) if err == nil { if len(resp.Reservations) > 0 { return fmt.Errorf("still exist.") @@ -314,7 +335,7 @@ func testAccCheckInstanceDestroy(s *terraform.State) error { } // Verify the error is what we want - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -338,8 +359,9 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun } conn := testAccProvider.Meta().(*AWSClient).ec2conn - resp, err := conn.Instances( - []string{rs.Primary.ID}, ec2.NewFilter()) + resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{ + InstanceIDs: []string{rs.Primary.ID}, + }) if err != nil { return err } @@ -389,7 +411,7 @@ resource "aws_instance" "foo" { instance_type = "m1.small" security_groups = ["${aws_security_group.tf_test_foo.name}"] - user_data = "foo" + user_data = "foo:-with-character's" } ` @@ -530,3 +552,49 @@ resource "aws_instance" "foo" { private_ip = "10.1.1.42" } ` + +const testAccInstanceNetworkInstanceSecurityGroups = ` +resource "aws_internet_gateway" "gw" { + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" + tags { + Name = "tf-network-test" + } +} + +resource "aws_security_group" "tf_test_foo" { + name = "tf_test_foo" + description = "foo" + vpc_id="${aws_vpc.foo.id}" + + ingress { + protocol = "icmp" + from_port = -1 + to_port = -1 + cidr_blocks = ["0.0.0.0/0"] + } +} + +resource "aws_subnet" "foo" { + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_instance" "foo_instance" { + ami = "ami-21f78e11" + instance_type = "t1.micro" + security_groups = ["${aws_security_group.tf_test_foo.id}"] + subnet_id = "${aws_subnet.foo.id}" + associate_public_ip_address = true + depends_on = ["aws_internet_gateway.gw"] +} + +resource "aws_eip" "foo_eip" { + instance = "${aws_instance.foo_instance.id}" + vpc = true + depends_on = ["aws_internet_gateway.gw"] +} +` diff --git a/builtin/providers/aws/resource_aws_internet_gateway.go b/builtin/providers/aws/resource_aws_internet_gateway.go index 08f77a5c6..9546ffb5c 100644 --- a/builtin/providers/aws/resource_aws_internet_gateway.go +++ b/builtin/providers/aws/resource_aws_internet_gateway.go @@ -29,7 +29,7 @@ func resourceAwsInternetGateway() *schema.Resource { } func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn // Create the gateway log.Printf("[DEBUG] Creating internet gateway") @@ -43,12 +43,17 @@ func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) d.SetId(*ig.InternetGatewayID) log.Printf("[INFO] InternetGateway ID: %s", d.Id()) + err = setTags(ec2conn, d) + if err != nil { + return err + } + // Attach the new gateway to the correct vpc return resourceAwsInternetGatewayAttach(d, meta) } func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())() if err != nil { @@ -68,7 +73,7 @@ func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) er d.Set("vpc_id", ig.Attachments[0].VPCID) } - d.Set("tags", tagsToMapSDK(ig.Tags)) + d.Set("tags", tagsToMap(ig.Tags)) return nil } @@ -86,9 +91,9 @@ func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) } } - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn - if err := setTagsSDK(ec2conn, d); err != nil { + if err := setTags(ec2conn, d); err != nil { return err } @@ -98,7 +103,7 @@ func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) } func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn // Detach if it is attached if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { @@ -132,7 +137,7 @@ func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) } func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn if d.Get("vpc_id").(string) == "" { log.Printf( @@ -177,7 +182,7 @@ func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) } func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn // Get the old VPC ID to detach from vpcID, _ := d.GetChange("vpc_id") diff --git a/builtin/providers/aws/resource_aws_internet_gateway_test.go b/builtin/providers/aws/resource_aws_internet_gateway_test.go index a990342f9..a07d1abff 100644 --- a/builtin/providers/aws/resource_aws_internet_gateway_test.go +++ b/builtin/providers/aws/resource_aws_internet_gateway_test.go @@ -98,6 +98,7 @@ func TestAccInternetGateway_tags(t *testing.T) { Config: testAccCheckInternetGatewayConfigTags, Check: resource.ComposeTestCheckFunc( testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v), + testAccCheckTags(&v.Tags, "foo", "bar"), ), }, @@ -105,8 +106,8 @@ func TestAccInternetGateway_tags(t *testing.T) { Config: testAccCheckInternetGatewayConfigTagsUpdate, Check: resource.ComposeTestCheckFunc( testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v), - testAccCheckTagsSDK(&v.Tags, "foo", ""), - testAccCheckTagsSDK(&v.Tags, "bar", "baz"), + testAccCheckTags(&v.Tags, "foo", ""), + testAccCheckTags(&v.Tags, "bar", "baz"), ), }, }, @@ -114,7 +115,7 @@ func TestAccInternetGateway_tags(t *testing.T) { } func testAccCheckInternetGatewayDestroy(s *terraform.State) error { - ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_internet_gateway" { @@ -157,7 +158,7 @@ func testAccCheckInternetGatewayExists(n string, ig *ec2.InternetGateway) resour return fmt.Errorf("No ID is set") } - ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{ InternetGatewayIDs: []string{rs.Primary.ID}, }) diff --git a/builtin/providers/aws/resource_aws_key_pair.go b/builtin/providers/aws/resource_aws_key_pair.go index 573a93567..e96ecf620 100644 --- a/builtin/providers/aws/resource_aws_key_pair.go +++ b/builtin/providers/aws/resource_aws_key_pair.go @@ -37,7 +37,7 @@ func resourceAwsKeyPair() *schema.Resource { } func resourceAwsKeyPairCreate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn keyName := d.Get("key_name").(string) publicKey := d.Get("public_key").(string) @@ -55,7 +55,7 @@ func resourceAwsKeyPairCreate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsKeyPairRead(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn req := &ec2.DescribeKeyPairsRequest{ KeyNames: []string{d.Id()}, @@ -77,7 +77,7 @@ func resourceAwsKeyPairRead(d *schema.ResourceData, meta interface{}) error { } func resourceAwsKeyPairDelete(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairRequest{ KeyName: aws.String(d.Id()), diff --git a/builtin/providers/aws/resource_aws_key_pair_test.go b/builtin/providers/aws/resource_aws_key_pair_test.go index b601d479a..695da661f 100644 --- a/builtin/providers/aws/resource_aws_key_pair_test.go +++ b/builtin/providers/aws/resource_aws_key_pair_test.go @@ -30,7 +30,7 @@ func TestAccAWSKeyPair_normal(t *testing.T) { } func testAccCheckAWSKeyPairDestroy(s *terraform.State) error { - ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_key_pair" { @@ -81,7 +81,7 @@ func testAccCheckAWSKeyPairExists(n string, res *ec2.KeyPairInfo) resource.TestC return fmt.Errorf("No KeyPair name is set") } - ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := ec2conn.DescribeKeyPairs(&ec2.DescribeKeyPairsRequest{ KeyNames: []string{rs.Primary.ID}, diff --git a/builtin/providers/aws/resource_aws_main_route_table_association.go b/builtin/providers/aws/resource_aws_main_route_table_association.go index f656f3760..40303ab1e 100644 --- a/builtin/providers/aws/resource_aws_main_route_table_association.go +++ b/builtin/providers/aws/resource_aws_main_route_table_association.go @@ -4,8 +4,9 @@ import ( "fmt" "log" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsMainRouteTableAssociation() *schema.Resource { @@ -50,16 +51,16 @@ func resourceAwsMainRouteTableAssociationCreate(d *schema.ResourceData, meta int return err } - resp, err := ec2conn.ReassociateRouteTable( - mainAssociation.AssociationId, - routeTableId, - ) + resp, err := ec2conn.ReplaceRouteTableAssociation(&ec2.ReplaceRouteTableAssociationRequest{ + AssociationID: mainAssociation.RouteTableAssociationID, + RouteTableID: aws.String(routeTableId), + }) if err != nil { return err } - d.Set("original_route_table_id", mainAssociation.RouteTableId) - d.SetId(resp.AssociationId) + d.Set("original_route_table_id", mainAssociation.RouteTableID) + d.SetId(*resp.NewAssociationID) log.Printf("[INFO] New main route table association ID: %s", d.Id()) return nil @@ -75,7 +76,7 @@ func resourceAwsMainRouteTableAssociationRead(d *schema.ResourceData, meta inter return err } - if mainAssociation.AssociationId != d.Id() { + if *mainAssociation.RouteTableAssociationID != d.Id() { // It seems it doesn't exist anymore, so clear the ID d.SetId("") } @@ -93,12 +94,15 @@ func resourceAwsMainRouteTableAssociationUpdate(d *schema.ResourceData, meta int log.Printf("[INFO] Updating main route table association: %s => %s", vpcId, routeTableId) - resp, err := ec2conn.ReassociateRouteTable(d.Id(), routeTableId) + resp, err := ec2conn.ReplaceRouteTableAssociation(&ec2.ReplaceRouteTableAssociationRequest{ + AssociationID: aws.String(d.Id()), + RouteTableID: aws.String(routeTableId), + }) if err != nil { return err } - d.SetId(resp.AssociationId) + d.SetId(*resp.NewAssociationID) log.Printf("[INFO] New main route table association ID: %s", d.Id()) return nil @@ -113,12 +117,15 @@ func resourceAwsMainRouteTableAssociationDelete(d *schema.ResourceData, meta int vpcId, originalRouteTableId) - resp, err := ec2conn.ReassociateRouteTable(d.Id(), originalRouteTableId) + resp, err := ec2conn.ReplaceRouteTableAssociation(&ec2.ReplaceRouteTableAssociationRequest{ + AssociationID: aws.String(d.Id()), + RouteTableID: aws.String(originalRouteTableId), + }) if err != nil { return err } - log.Printf("[INFO] Resulting Association ID: %s", resp.AssociationId) + log.Printf("[INFO] Resulting Association ID: %s", *resp.NewAssociationID) return nil } @@ -130,7 +137,7 @@ func findMainRouteTableAssociation(ec2conn *ec2.EC2, vpcId string) (*ec2.RouteTa } for _, a := range mainRouteTable.Associations { - if a.Main { + if *a.Main { return &a, nil } } @@ -138,10 +145,17 @@ func findMainRouteTableAssociation(ec2conn *ec2.EC2, vpcId string) (*ec2.RouteTa } func findMainRouteTable(ec2conn *ec2.EC2, vpcId string) (*ec2.RouteTable, error) { - filter := ec2.NewFilter() - filter.Add("association.main", "true") - filter.Add("vpc-id", vpcId) - routeResp, err := ec2conn.DescribeRouteTables(nil, filter) + mainFilter := ec2.Filter{ + aws.String("association.main"), + []string{"true"}, + } + vpcFilter := ec2.Filter{ + aws.String("vpc-id"), + []string{vpcId}, + } + routeResp, err := ec2conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + Filters: []ec2.Filter{mainFilter, vpcFilter}, + }) if err != nil { return nil, err } else if len(routeResp.RouteTables) != 1 { diff --git a/builtin/providers/aws/resource_aws_main_route_table_association_test.go b/builtin/providers/aws/resource_aws_main_route_table_association_test.go index 937014cae..81f87a901 100644 --- a/builtin/providers/aws/resource_aws_main_route_table_association_test.go +++ b/builtin/providers/aws/resource_aws_main_route_table_association_test.go @@ -71,9 +71,9 @@ func testAccCheckMainRouteTableAssociation( return err } - if mainAssociation.AssociationId != rs.Primary.ID { + if *mainAssociation.RouteTableAssociationID != rs.Primary.ID { return fmt.Errorf("Found wrong main association: %s", - mainAssociation.AssociationId) + *mainAssociation.RouteTableAssociationID) } return nil diff --git a/builtin/providers/aws/resource_aws_network_acl.go b/builtin/providers/aws/resource_aws_network_acl.go index efafd7ffe..0d625ffe4 100644 --- a/builtin/providers/aws/resource_aws_network_acl.go +++ b/builtin/providers/aws/resource_aws_network_acl.go @@ -6,10 +6,11 @@ import ( "log" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsNetworkAcl() *schema.Resource { @@ -111,20 +112,20 @@ func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error ec2conn := meta.(*AWSClient).ec2conn // Create the Network Acl - createOpts := &ec2.CreateNetworkAcl{ - VpcId: d.Get("vpc_id").(string), + createOpts := &ec2.CreateNetworkACLRequest{ + VPCID: aws.String(d.Get("vpc_id").(string)), } log.Printf("[DEBUG] Network Acl create config: %#v", createOpts) - resp, err := ec2conn.CreateNetworkAcl(createOpts) + resp, err := ec2conn.CreateNetworkACL(createOpts) if err != nil { return fmt.Errorf("Error creating network acl: %s", err) } // Get the ID and store it - networkAcl := &resp.NetworkAcl - d.SetId(networkAcl.NetworkAclId) - log.Printf("[INFO] Network Acl ID: %s", networkAcl.NetworkAclId) + networkAcl := resp.NetworkACL + d.SetId(*networkAcl.NetworkACLID) + log.Printf("[INFO] Network Acl ID: %s", *networkAcl.NetworkACLID) // Update rules and subnet association once acl is created return resourceAwsNetworkAclUpdate(d, meta) @@ -133,7 +134,9 @@ func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - resp, err := ec2conn.NetworkAcls([]string{d.Id()}, ec2.NewFilter()) + resp, err := ec2conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{d.Id()}, + }) if err != nil { return err @@ -142,20 +145,20 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error { return nil } - networkAcl := &resp.NetworkAcls[0] - var ingressEntries []ec2.NetworkAclEntry - var egressEntries []ec2.NetworkAclEntry + networkAcl := &resp.NetworkACLs[0] + var ingressEntries []ec2.NetworkACLEntry + var egressEntries []ec2.NetworkACLEntry // separate the ingress and egress rules - for _, e := range networkAcl.EntrySet { - if e.Egress == true { + for _, e := range networkAcl.Entries { + if *e.Egress == true { egressEntries = append(egressEntries, e) } else { ingressEntries = append(ingressEntries, e) } } - d.Set("vpc_id", networkAcl.VpcId) + d.Set("vpc_id", networkAcl.VPCID) d.Set("ingress", ingressEntries) d.Set("egress", egressEntries) d.Set("tags", tagsToMap(networkAcl.Tags)) @@ -190,7 +193,10 @@ func resourceAwsNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error if err != nil { return fmt.Errorf("Failed to update acl %s with subnet %s: %s", d.Id(), newSubnet, err) } - _, err = ec2conn.ReplaceNetworkAclAssociation(association.NetworkAclAssociationId, d.Id()) + _, err = ec2conn.ReplaceNetworkACLAssociation(&ec2.ReplaceNetworkACLAssociationRequest{ + AssociationID: association.NetworkACLAssociationID, + NetworkACLID: aws.String(d.Id()), + }) if err != nil { return err } @@ -226,7 +232,11 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, ec2conn * } for _, remove := range toBeDeleted { // Delete old Acl - _, err := ec2conn.DeleteNetworkAclEntry(d.Id(), remove.RuleNumber, remove.Egress) + err := ec2conn.DeleteNetworkACLEntry(&ec2.DeleteNetworkACLEntryRequest{ + NetworkACLID: aws.String(d.Id()), + RuleNumber: remove.RuleNumber, + Egress: remove.Egress, + }) if err != nil { return fmt.Errorf("Error deleting %s entry: %s", entryType, err) } @@ -238,7 +248,15 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, ec2conn * } for _, add := range toBeCreated { // Add new Acl entry - _, err := ec2conn.CreateNetworkAclEntry(d.Id(), &add) + err := ec2conn.CreateNetworkACLEntry(&ec2.CreateNetworkACLEntryRequest{ + NetworkACLID: aws.String(d.Id()), + CIDRBlock: add.CIDRBlock, + Egress: add.Egress, + PortRange: add.PortRange, + Protocol: add.Protocol, + RuleAction: add.RuleAction, + RuleNumber: add.RuleNumber, + }) if err != nil { return fmt.Errorf("Error creating %s entry: %s", entryType, err) } @@ -251,8 +269,11 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error log.Printf("[INFO] Deleting Network Acl: %s", d.Id()) return resource.Retry(5*time.Minute, func() error { - if _, err := ec2conn.DeleteNetworkAcl(d.Id()); err != nil { - ec2err := err.(*ec2.Error) + err := ec2conn.DeleteNetworkACL(&ec2.DeleteNetworkACLRequest{ + NetworkACLID: aws.String(d.Id()), + }) + if err != nil { + ec2err := err.(aws.APIError) switch ec2err.Code { case "InvalidNetworkAclID.NotFound": return nil @@ -267,7 +288,10 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error if err != nil { return fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err) } - _, err = ec2conn.ReplaceNetworkAclAssociation(association.NetworkAclAssociationId, defaultAcl.NetworkAclId) + _, err = ec2conn.ReplaceNetworkACLAssociation(&ec2.ReplaceNetworkACLAssociationRequest{ + AssociationID: association.NetworkACLAssociationID, + NetworkACLID: defaultAcl.NetworkACLID, + }) return resource.RetryError{Err: err} default: // Any other error, we want to quit the retry loop immediately @@ -296,30 +320,43 @@ func resourceAwsNetworkAclEntryHash(v interface{}) int { return hashcode.String(buf.String()) } -func getDefaultNetworkAcl(vpc_id string, ec2conn *ec2.EC2) (defaultAcl *ec2.NetworkAcl, err error) { - filter := ec2.NewFilter() - filter.Add("default", "true") - filter.Add("vpc-id", vpc_id) - - resp, err := ec2conn.NetworkAcls([]string{}, filter) +func getDefaultNetworkAcl(vpc_id string, ec2conn *ec2.EC2) (defaultAcl *ec2.NetworkACL, err error) { + resp, err := ec2conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{}, + Filters: []ec2.Filter{ + ec2.Filter{ + Name: aws.String("default"), + Values: []string{"true"}, + }, + ec2.Filter{ + Name: aws.String("vpc-id"), + Values: []string{vpc_id}, + }, + }, + }) if err != nil { return nil, err } - return &resp.NetworkAcls[0], nil + return &resp.NetworkACLs[0], nil } -func findNetworkAclAssociation(subnetId string, ec2conn *ec2.EC2) (networkAclAssociation *ec2.NetworkAclAssociation, err error) { - filter := ec2.NewFilter() - filter.Add("association.subnet-id", subnetId) - - resp, err := ec2conn.NetworkAcls([]string{}, filter) +func findNetworkAclAssociation(subnetId string, ec2conn *ec2.EC2) (networkAclAssociation *ec2.NetworkACLAssociation, err error) { + resp, err := ec2conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{}, + Filters: []ec2.Filter{ + ec2.Filter{ + Name: aws.String("association.subnet-id"), + Values: []string{subnetId}, + }, + }, + }) if err != nil { return nil, err } - for _, association := range resp.NetworkAcls[0].AssociationSet { - if association.SubnetId == subnetId { + for _, association := range resp.NetworkACLs[0].Associations { + if *association.SubnetID == subnetId { return &association, nil } } diff --git a/builtin/providers/aws/resource_aws_network_acl_test.go b/builtin/providers/aws/resource_aws_network_acl_test.go index 939e8633e..23c60db44 100644 --- a/builtin/providers/aws/resource_aws_network_acl_test.go +++ b/builtin/providers/aws/resource_aws_network_acl_test.go @@ -4,15 +4,16 @@ import ( "fmt" "testing" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" // "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" // "github.com/hashicorp/terraform/helper/schema" ) func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) { - var networkAcl ec2.NetworkAcl + var networkAcl ec2.NetworkACL resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -24,29 +25,29 @@ func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSNetworkAclExists("aws_network_acl.bar", &networkAcl), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.protocol", "tcp"), + "aws_network_acl.bar", "ingress.3409203205.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.rule_no", "1"), + "aws_network_acl.bar", "ingress.3409203205.rule_no", "1"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.from_port", "80"), + "aws_network_acl.bar", "ingress.3409203205.from_port", "80"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.to_port", "80"), + "aws_network_acl.bar", "ingress.3409203205.to_port", "80"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.action", "allow"), + "aws_network_acl.bar", "ingress.3409203205.action", "allow"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "ingress.580214135.cidr_block", "10.3.10.3/18"), + "aws_network_acl.bar", "ingress.3409203205.cidr_block", "10.3.10.3/18"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.protocol", "tcp"), + "aws_network_acl.bar", "egress.2579689292.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.rule_no", "2"), + "aws_network_acl.bar", "egress.2579689292.rule_no", "2"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.from_port", "443"), + "aws_network_acl.bar", "egress.2579689292.from_port", "443"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.to_port", "443"), + "aws_network_acl.bar", "egress.2579689292.to_port", "443"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.cidr_block", "10.3.2.3/18"), + "aws_network_acl.bar", "egress.2579689292.cidr_block", "10.3.2.3/18"), resource.TestCheckResourceAttr( - "aws_network_acl.bar", "egress.1730430240.action", "allow"), + "aws_network_acl.bar", "egress.2579689292.action", "allow"), ), }, }, @@ -54,7 +55,7 @@ func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) { } func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) { - var networkAcl ec2.NetworkAcl + var networkAcl ec2.NetworkACL resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -67,17 +68,17 @@ func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) { testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), // testAccCheckSubnetAssociation("aws_network_acl.foos", "aws_subnet.blob"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), + "aws_network_acl.foos", "ingress.2750166237.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), + "aws_network_acl.foos", "ingress.2750166237.rule_no", "1"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.from_port", "0"), + "aws_network_acl.foos", "ingress.2750166237.from_port", "0"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.to_port", "22"), + "aws_network_acl.foos", "ingress.2750166237.to_port", "22"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.action", "deny"), + "aws_network_acl.foos", "ingress.2750166237.action", "deny"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"), + "aws_network_acl.foos", "ingress.2750166237.cidr_block", "10.2.2.3/18"), ), }, }, @@ -85,7 +86,7 @@ func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) { } func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) { - var networkAcl ec2.NetworkAcl + var networkAcl ec2.NetworkACL resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -98,21 +99,21 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) { testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testIngressRuleLength(&networkAcl, 2), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), + "aws_network_acl.foos", "ingress.37211640.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), + "aws_network_acl.foos", "ingress.37211640.rule_no", "1"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.from_port", "0"), + "aws_network_acl.foos", "ingress.37211640.from_port", "0"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.to_port", "22"), + "aws_network_acl.foos", "ingress.37211640.to_port", "22"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.action", "deny"), + "aws_network_acl.foos", "ingress.37211640.action", "deny"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"), + "aws_network_acl.foos", "ingress.37211640.cidr_block", "10.2.2.3/18"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.2438803013.from_port", "443"), + "aws_network_acl.foos", "ingress.2750166237.from_port", "443"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.2438803013.rule_no", "2"), + "aws_network_acl.foos", "ingress.2750166237.rule_no", "2"), ), }, resource.TestStep{ @@ -121,17 +122,17 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) { testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testIngressRuleLength(&networkAcl, 1), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), + "aws_network_acl.foos", "ingress.37211640.protocol", "tcp"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), + "aws_network_acl.foos", "ingress.37211640.rule_no", "1"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.from_port", "0"), + "aws_network_acl.foos", "ingress.37211640.from_port", "0"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.to_port", "22"), + "aws_network_acl.foos", "ingress.37211640.to_port", "22"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.action", "deny"), + "aws_network_acl.foos", "ingress.37211640.action", "deny"), resource.TestCheckResourceAttr( - "aws_network_acl.foos", "ingress.3697634361.cidr_block", "10.2.2.3/18"), + "aws_network_acl.foos", "ingress.37211640.cidr_block", "10.2.2.3/18"), ), }, }, @@ -139,7 +140,7 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) { } func TestAccAWSNetworkAclsOnlyEgressRules(t *testing.T) { - var networkAcl ec2.NetworkAcl + var networkAcl ec2.NetworkACL resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -157,7 +158,7 @@ func TestAccAWSNetworkAclsOnlyEgressRules(t *testing.T) { }) } -func TestAccNetworkAcl_SubnetChange(t *testing.T) { +func TestAccAWSNetworkAcl_SubnetChange(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -191,16 +192,18 @@ func testAccCheckAWSNetworkAclDestroy(s *terraform.State) error { } // Retrieve the network acl - resp, err := conn.NetworkAcls([]string{rs.Primary.ID}, ec2.NewFilter()) + resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{rs.Primary.ID}, + }) if err == nil { - if len(resp.NetworkAcls) > 0 && resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID { + if len(resp.NetworkACLs) > 0 && *resp.NetworkACLs[0].NetworkACLID == rs.Primary.ID { return fmt.Errorf("Network Acl (%s) still exists.", rs.Primary.ID) } return nil } - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -213,7 +216,7 @@ func testAccCheckAWSNetworkAclDestroy(s *terraform.State) error { return nil } -func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkAcl) resource.TestCheckFunc { +func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkACL) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -225,13 +228,15 @@ func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkAcl) resou } conn := testAccProvider.Meta().(*AWSClient).ec2conn - resp, err := conn.NetworkAcls([]string{rs.Primary.ID}, nil) + resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{rs.Primary.ID}, + }) if err != nil { return err } - if len(resp.NetworkAcls) > 0 && resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID { - *networkAcl = resp.NetworkAcls[0] + if len(resp.NetworkACLs) > 0 && *resp.NetworkACLs[0].NetworkACLID == rs.Primary.ID { + *networkAcl = resp.NetworkACLs[0] return nil } @@ -239,11 +244,11 @@ func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkAcl) resou } } -func testIngressRuleLength(networkAcl *ec2.NetworkAcl, length int) resource.TestCheckFunc { +func testIngressRuleLength(networkAcl *ec2.NetworkACL, length int) resource.TestCheckFunc { return func(s *terraform.State) error { - var ingressEntries []ec2.NetworkAclEntry - for _, e := range networkAcl.EntrySet { - if e.Egress == false { + var ingressEntries []ec2.NetworkACLEntry + for _, e := range networkAcl.Entries { + if *e.Egress == false { ingressEntries = append(ingressEntries, e) } } @@ -262,20 +267,25 @@ func testAccCheckSubnetIsAssociatedWithAcl(acl string, sub string) resource.Test subnet := s.RootModule().Resources[sub] conn := testAccProvider.Meta().(*AWSClient).ec2conn - filter := ec2.NewFilter() - filter.Add("association.subnet-id", subnet.Primary.ID) - resp, err := conn.NetworkAcls([]string{networkAcl.Primary.ID}, filter) - + resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{networkAcl.Primary.ID}, + Filters: []ec2.Filter{ + ec2.Filter{ + Name: aws.String("association.subnet-id"), + Values: []string{subnet.Primary.ID}, + }, + }, + }) if err != nil { return err } - if len(resp.NetworkAcls) > 0 { + if len(resp.NetworkACLs) > 0 { return nil } - r, _ := conn.NetworkAcls([]string{}, ec2.NewFilter()) - fmt.Printf("\n\nall acls\n %#v\n\n", r.NetworkAcls) - conn.NetworkAcls([]string{}, filter) + // r, _ := conn.NetworkACLs([]string{}, ec2.NewFilter()) + // fmt.Printf("\n\nall acls\n %#v\n\n", r.NetworkAcls) + // conn.NetworkAcls([]string{}, filter) return fmt.Errorf("Network Acl %s is not associated with subnet %s", acl, sub) } @@ -287,14 +297,20 @@ func testAccCheckSubnetIsNotAssociatedWithAcl(acl string, subnet string) resourc subnet := s.RootModule().Resources[subnet] conn := testAccProvider.Meta().(*AWSClient).ec2conn - filter := ec2.NewFilter() - filter.Add("association.subnet-id", subnet.Primary.ID) - resp, err := conn.NetworkAcls([]string{networkAcl.Primary.ID}, filter) + resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{ + NetworkACLIDs: []string{networkAcl.Primary.ID}, + Filters: []ec2.Filter{ + ec2.Filter{ + Name: aws.String("association.subnet-id"), + Values: []string{subnet.Primary.ID}, + }, + }, + }) if err != nil { return err } - if len(resp.NetworkAcls) > 0 { + if len(resp.NetworkACLs) > 0 { return fmt.Errorf("Network Acl %s is still associated with subnet %s", acl, subnet) } return nil diff --git a/builtin/providers/aws/resource_aws_route53_record.go b/builtin/providers/aws/resource_aws_route53_record.go index 96c7608fe..fcd781c61 100644 --- a/builtin/providers/aws/resource_aws_route53_record.go +++ b/builtin/providers/aws/resource_aws_route53_record.go @@ -138,7 +138,7 @@ func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) er Delay: 30 * time.Second, Pending: []string{"PENDING"}, Target: "INSYNC", - Timeout: 10 * time.Minute, + Timeout: 30 * time.Minute, MinTimeout: 5 * time.Second, Refresh: func() (result interface{}, state string, err error) { changeRequest := &route53.GetChangeRequest{ diff --git a/builtin/providers/aws/resource_aws_route_table.go b/builtin/providers/aws/resource_aws_route_table.go index 9d01218b0..6a4b1d3ca 100644 --- a/builtin/providers/aws/resource_aws_route_table.go +++ b/builtin/providers/aws/resource_aws_route_table.go @@ -6,10 +6,11 @@ import ( "log" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsRouteTable() *schema.Resource { @@ -64,8 +65,8 @@ func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error ec2conn := meta.(*AWSClient).ec2conn // Create the routing table - createOpts := &ec2.CreateRouteTable{ - VpcId: d.Get("vpc_id").(string), + createOpts := &ec2.CreateRouteTableRequest{ + VPCID: aws.String(d.Get("vpc_id").(string)), } log.Printf("[DEBUG] RouteTable create config: %#v", createOpts) @@ -75,8 +76,8 @@ func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error } // Get the ID and store it - rt := &resp.RouteTable - d.SetId(rt.RouteTableId) + rt := resp.RouteTable + d.SetId(*rt.RouteTableID) log.Printf("[INFO] Route Table ID: %s", d.Id()) // Wait for the route table to become available @@ -110,27 +111,35 @@ func resourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error { } rt := rtRaw.(*ec2.RouteTable) - d.Set("vpc_id", rt.VpcId) + d.Set("vpc_id", rt.VPCID) // Create an empty schema.Set to hold all routes route := &schema.Set{F: resourceAwsRouteTableHash} // Loop through the routes and add them to the set for _, r := range rt.Routes { - if r.GatewayId == "local" { + if r.GatewayID != nil && *r.GatewayID == "local" { continue } - if r.Origin == "EnableVgwRoutePropagation" { + if r.Origin != nil && *r.Origin == "EnableVgwRoutePropagation" { continue } m := make(map[string]interface{}) - m["cidr_block"] = r.DestinationCidrBlock - m["gateway_id"] = r.GatewayId - m["instance_id"] = r.InstanceId - m["vpc_peering_connection_id"] = r.VpcPeeringConnectionId + if r.DestinationCIDRBlock != nil { + m["cidr_block"] = *r.DestinationCIDRBlock + } + if r.GatewayID != nil { + m["gateway_id"] = *r.GatewayID + } + if r.InstanceID != nil { + m["instance_id"] = *r.InstanceID + } + if r.VPCPeeringConnectionID != nil { + m["vpc_peering_connection_id"] = *r.VPCPeeringConnectionID + } route.Add(m) } @@ -159,8 +168,10 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error log.Printf( "[INFO] Deleting route from %s: %s", d.Id(), m["cidr_block"].(string)) - _, err := ec2conn.DeleteRoute( - d.Id(), m["cidr_block"].(string)) + err := ec2conn.DeleteRoute(&ec2.DeleteRouteRequest{ + RouteTableID: aws.String(d.Id()), + DestinationCIDRBlock: aws.String(m["cidr_block"].(string)), + }) if err != nil { return err } @@ -174,17 +185,16 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error for _, route := range nrs.List() { m := route.(map[string]interface{}) - opts := ec2.CreateRoute{ - RouteTableId: d.Id(), - DestinationCidrBlock: m["cidr_block"].(string), - GatewayId: m["gateway_id"].(string), - InstanceId: m["instance_id"].(string), - VpcPeeringConnectionId: m["vpc_peering_connection_id"].(string), + opts := ec2.CreateRouteRequest{ + RouteTableID: aws.String(d.Id()), + DestinationCIDRBlock: aws.String(m["cidr_block"].(string)), + GatewayID: aws.String(m["gateway_id"].(string)), + InstanceID: aws.String(m["instance_id"].(string)), + VPCPeeringConnectionID: aws.String(m["vpc_peering_connection_id"].(string)), } log.Printf("[INFO] Creating route for %s: %#v", d.Id(), opts) - _, err := ec2conn.CreateRoute(&opts) - if err != nil { + if err := ec2conn.CreateRoute(&opts); err != nil { return err } @@ -218,16 +228,22 @@ func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error // Do all the disassociations for _, a := range rt.Associations { - log.Printf("[INFO] Disassociating association: %s", a.AssociationId) - if _, err := ec2conn.DisassociateRouteTable(a.AssociationId); err != nil { + log.Printf("[INFO] Disassociating association: %s", *a.RouteTableAssociationID) + err := ec2conn.DisassociateRouteTable(&ec2.DisassociateRouteTableRequest{ + AssociationID: a.RouteTableAssociationID, + }) + if err != nil { return err } } // Delete the route table log.Printf("[INFO] Deleting Route Table: %s", d.Id()) - if _, err := ec2conn.DeleteRouteTable(d.Id()); err != nil { - ec2err, ok := err.(*ec2.Error) + err = ec2conn.DeleteRouteTable(&ec2.DeleteRouteTableRequest{ + RouteTableID: aws.String(d.Id()), + }) + if err != nil { + ec2err, ok := err.(aws.APIError) if ok && ec2err.Code == "InvalidRouteTableID.NotFound" { return nil } @@ -279,9 +295,11 @@ func resourceAwsRouteTableHash(v interface{}) int { // a RouteTable. func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeRouteTables([]string{id}, ec2.NewFilter()) + resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + RouteTableIDs: []string{id}, + }) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidRouteTableID.NotFound" { + if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidRouteTableID.NotFound" { resp = nil } else { log.Printf("Error on RouteTableStateRefresh: %s", err) diff --git a/builtin/providers/aws/resource_aws_route_table_association.go b/builtin/providers/aws/resource_aws_route_table_association.go index 846836008..8fd324035 100644 --- a/builtin/providers/aws/resource_aws_route_table_association.go +++ b/builtin/providers/aws/resource_aws_route_table_association.go @@ -4,8 +4,9 @@ import ( "fmt" "log" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsRouteTableAssociation() *schema.Resource { @@ -38,16 +39,17 @@ func resourceAwsRouteTableAssociationCreate(d *schema.ResourceData, meta interfa d.Get("subnet_id").(string), d.Get("route_table_id").(string)) - resp, err := ec2conn.AssociateRouteTable( - d.Get("route_table_id").(string), - d.Get("subnet_id").(string)) + resp, err := ec2conn.AssociateRouteTable(&ec2.AssociateRouteTableRequest{ + RouteTableID: aws.String(d.Get("route_table_id").(string)), + SubnetID: aws.String(d.Get("subnet_id").(string)), + }) if err != nil { return err } // Set the ID and return - d.SetId(resp.AssociationId) + d.SetId(*resp.AssociationID) log.Printf("[INFO] Association ID: %s", d.Id()) return nil @@ -70,9 +72,9 @@ func resourceAwsRouteTableAssociationRead(d *schema.ResourceData, meta interface // Inspect that the association exists found := false for _, a := range rt.Associations { - if a.AssociationId == d.Id() { + if *a.RouteTableAssociationID == d.Id() { found = true - d.Set("subnet_id", a.SubnetId) + d.Set("subnet_id", *a.SubnetID) break } } @@ -93,12 +95,14 @@ func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interfa d.Get("subnet_id").(string), d.Get("route_table_id").(string)) - resp, err := ec2conn.ReassociateRouteTable( - d.Id(), - d.Get("route_table_id").(string)) + req := &ec2.ReplaceRouteTableAssociationRequest{ + AssociationID: aws.String(d.Id()), + RouteTableID: aws.String(d.Get("route_table_id").(string)), + } + resp, err := ec2conn.ReplaceRouteTableAssociation(req) if err != nil { - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if ok && ec2err.Code == "InvalidAssociationID.NotFound" { // Not found, so just create a new one return resourceAwsRouteTableAssociationCreate(d, meta) @@ -108,7 +112,7 @@ func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interfa } // Update the ID - d.SetId(resp.AssociationId) + d.SetId(*resp.NewAssociationID) log.Printf("[INFO] Association ID: %s", d.Id()) return nil @@ -118,8 +122,11 @@ func resourceAwsRouteTableAssociationDelete(d *schema.ResourceData, meta interfa ec2conn := meta.(*AWSClient).ec2conn log.Printf("[INFO] Deleting route table association: %s", d.Id()) - if _, err := ec2conn.DisassociateRouteTable(d.Id()); err != nil { - ec2err, ok := err.(*ec2.Error) + err := ec2conn.DisassociateRouteTable(&ec2.DisassociateRouteTableRequest{ + AssociationID: aws.String(d.Id()), + }) + if err != nil { + ec2err, ok := err.(aws.APIError) if ok && ec2err.Code == "InvalidAssociationID.NotFound" { return nil } diff --git a/builtin/providers/aws/resource_aws_route_table_association_test.go b/builtin/providers/aws/resource_aws_route_table_association_test.go index 079fb41f8..f5302258d 100644 --- a/builtin/providers/aws/resource_aws_route_table_association_test.go +++ b/builtin/providers/aws/resource_aws_route_table_association_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestAccAWSRouteTableAssociation(t *testing.T) { @@ -45,11 +46,12 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error { } // Try to find the resource - resp, err := conn.DescribeRouteTables( - []string{rs.Primary.Attributes["route_table_Id"]}, ec2.NewFilter()) + resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]}, + }) if err != nil { // Verify the error is what we want - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -62,7 +64,7 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error { rt := resp.RouteTables[0] if len(rt.Associations) > 0 { return fmt.Errorf( - "route table %s has associations", rt.RouteTableId) + "route table %s has associations", *rt.RouteTableID) } } @@ -82,8 +84,9 @@ func testAccCheckRouteTableAssociationExists(n string, v *ec2.RouteTable) resour } conn := testAccProvider.Meta().(*AWSClient).ec2conn - resp, err := conn.DescribeRouteTables( - []string{rs.Primary.Attributes["route_table_id"]}, ec2.NewFilter()) + resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]}, + }) if err != nil { return err } diff --git a/builtin/providers/aws/resource_aws_route_table_test.go b/builtin/providers/aws/resource_aws_route_table_test.go index 2f4dfab2e..b428b8786 100644 --- a/builtin/providers/aws/resource_aws_route_table_test.go +++ b/builtin/providers/aws/resource_aws_route_table_test.go @@ -4,9 +4,10 @@ import ( "fmt" "testing" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestAccAWSRouteTable_normal(t *testing.T) { @@ -19,7 +20,7 @@ func TestAccAWSRouteTable_normal(t *testing.T) { routes := make(map[string]ec2.Route) for _, r := range v.Routes { - routes[r.DestinationCidrBlock] = r + routes[*r.DestinationCIDRBlock] = r } if _, ok := routes["10.1.0.0/16"]; !ok { @@ -39,7 +40,7 @@ func TestAccAWSRouteTable_normal(t *testing.T) { routes := make(map[string]ec2.Route) for _, r := range v.Routes { - routes[r.DestinationCidrBlock] = r + routes[*r.DestinationCIDRBlock] = r } if _, ok := routes["10.1.0.0/16"]; !ok { @@ -91,7 +92,7 @@ func TestAccAWSRouteTable_instance(t *testing.T) { routes := make(map[string]ec2.Route) for _, r := range v.Routes { - routes[r.DestinationCidrBlock] = r + routes[*r.DestinationCIDRBlock] = r } if _, ok := routes["10.1.0.0/16"]; !ok { @@ -158,8 +159,9 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error { } // Try to find the resource - resp, err := conn.DescribeRouteTables( - []string{rs.Primary.ID}, ec2.NewFilter()) + resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + RouteTableIDs: []string{rs.Primary.ID}, + }) if err == nil { if len(resp.RouteTables) > 0 { return fmt.Errorf("still exist.") @@ -169,7 +171,7 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error { } // Verify the error is what we want - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -193,8 +195,9 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec } conn := testAccProvider.Meta().(*AWSClient).ec2conn - resp, err := conn.DescribeRouteTables( - []string{rs.Primary.ID}, ec2.NewFilter()) + resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{ + RouteTableIDs: []string{rs.Primary.ID}, + }) if err != nil { return err } @@ -208,7 +211,10 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec } } -func TestAccAWSRouteTable_vpcPeering(t *testing.T) { +// TODO: re-enable this test. +// VPC Peering connections are prefixed with pcx +// Right now there is no VPC Peering resource +func _TestAccAWSRouteTable_vpcPeering(t *testing.T) { var v ec2.RouteTable testCheck := func(*terraform.State) error { @@ -218,7 +224,7 @@ func TestAccAWSRouteTable_vpcPeering(t *testing.T) { routes := make(map[string]ec2.Route) for _, r := range v.Routes { - routes[r.DestinationCidrBlock] = r + routes[*r.DestinationCIDRBlock] = r } if _, ok := routes["10.1.0.0/16"]; !ok { @@ -345,6 +351,9 @@ resource "aws_route_table" "foo" { } ` +// TODO: re-enable this test. +// VPC Peering connections are prefixed with pcx +// Right now there is no VPC Peering resource const testAccRouteTableVpcPeeringConfig = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" @@ -359,7 +368,7 @@ resource "aws_route_table" "foo" { route { cidr_block = "10.2.0.0/16" - vpc_peering_connection_id = "vpc-12345" + vpc_peering_connection_id = "pcx-12345" } } ` diff --git a/builtin/providers/aws/resource_aws_security_group.go b/builtin/providers/aws/resource_aws_security_group.go index 451f1816f..c8051813f 100644 --- a/builtin/providers/aws/resource_aws_security_group.go +++ b/builtin/providers/aws/resource_aws_security_group.go @@ -7,10 +7,11 @@ import ( "sort" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsSecurityGroup() *schema.Resource { @@ -143,16 +144,16 @@ func resourceAwsSecurityGroup() *schema.Resource { func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - securityGroupOpts := ec2.SecurityGroup{ - Name: d.Get("name").(string), + securityGroupOpts := &ec2.CreateSecurityGroupRequest{ + GroupName: aws.String(d.Get("name").(string)), } if v := d.Get("vpc_id"); v != nil { - securityGroupOpts.VpcId = v.(string) + securityGroupOpts.VPCID = aws.String(v.(string)) } if v := d.Get("description"); v != nil { - securityGroupOpts.Description = v.(string) + securityGroupOpts.Description = aws.String(v.(string)) } log.Printf( @@ -162,7 +163,7 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Error creating Security Group: %s", err) } - d.SetId(createResp.Id) + d.SetId(*createResp.GroupID) log.Printf("[INFO] Security Group ID: %s", d.Id()) @@ -197,19 +198,18 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro return nil } - sg := sgRaw.(*ec2.SecurityGroupInfo) + sg := sgRaw.(ec2.SecurityGroup) - ingressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPerms) - egressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermsEgress) + ingressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermissions) + egressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermissionsEgress) d.Set("description", sg.Description) - d.Set("name", sg.Name) - d.Set("vpc_id", sg.VpcId) - d.Set("owner_id", sg.OwnerId) + d.Set("name", sg.GroupName) + d.Set("vpc_id", sg.VPCID) + d.Set("owner_id", sg.OwnerID) d.Set("ingress", ingressRules) d.Set("egress", egressRules) d.Set("tags", tagsToMap(sg.Tags)) - return nil } @@ -224,7 +224,8 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er d.SetId("") return nil } - group := sgRaw.(*ec2.SecurityGroupInfo).SecurityGroup + + group := sgRaw.(ec2.SecurityGroup) err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) if err != nil { @@ -253,9 +254,11 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) return resource.Retry(5*time.Minute, func() error { - _, err := ec2conn.DeleteSecurityGroup(ec2.SecurityGroup{Id: d.Id()}) + err := ec2conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupRequest{ + GroupID: aws.String(d.Id()), + }) if err != nil { - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -313,34 +316,45 @@ func resourceAwsSecurityGroupRuleHash(v interface{}) int { return hashcode.String(buf.String()) } -func resourceAwsSecurityGroupIPPermGather(d *schema.ResourceData, permissions []ec2.IPPerm) []map[string]interface{} { +func resourceAwsSecurityGroupIPPermGather(d *schema.ResourceData, permissions []ec2.IPPermission) []map[string]interface{} { ruleMap := make(map[string]map[string]interface{}) for _, perm := range permissions { - k := fmt.Sprintf("%s-%d-%d", perm.Protocol, perm.FromPort, perm.ToPort) + var fromPort, toPort int + if v := perm.FromPort; v != nil { + fromPort = *v + } + if v := perm.ToPort; v != nil { + toPort = *v + } + + k := fmt.Sprintf("%s-%d-%d", *perm.IPProtocol, fromPort, toPort) m, ok := ruleMap[k] if !ok { m = make(map[string]interface{}) ruleMap[k] = m } - m["from_port"] = perm.FromPort - m["to_port"] = perm.ToPort - m["protocol"] = perm.Protocol + m["from_port"] = fromPort + m["to_port"] = toPort + m["protocol"] = *perm.IPProtocol - if len(perm.SourceIPs) > 0 { + if len(perm.IPRanges) > 0 { raw, ok := m["cidr_blocks"] if !ok { - raw = make([]string, 0, len(perm.SourceIPs)) + raw = make([]string, 0, len(perm.IPRanges)) } list := raw.([]string) - list = append(list, perm.SourceIPs...) + for _, ip := range perm.IPRanges { + list = append(list, *ip.CIDRIP) + } + m["cidr_blocks"] = list } var groups []string - if len(perm.SourceGroups) > 0 { - groups = flattenSecurityGroups(perm.SourceGroups) + if len(perm.UserIDGroupPairs) > 0 { + groups = flattenSecurityGroups(perm.UserIDGroupPairs) } for i, id := range groups { if id == d.Id() { @@ -364,7 +378,6 @@ func resourceAwsSecurityGroupIPPermGather(d *schema.ResourceData, permissions [] for _, m := range ruleMap { rules = append(rules, m) } - return rules } @@ -398,32 +411,51 @@ func resourceAwsSecurityGroupUpdateRules( if len(remove) > 0 || len(add) > 0 { ec2conn := meta.(*AWSClient).ec2conn + var err error if len(remove) > 0 { - // Revoke the old rules - revoke := ec2conn.RevokeSecurityGroup + log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v", + group, ruleset, remove) + if ruleset == "egress" { - revoke = ec2conn.RevokeSecurityGroupEgress + req := &ec2.RevokeSecurityGroupEgressRequest{ + GroupID: group.GroupID, + IPPermissions: remove, + } + err = ec2conn.RevokeSecurityGroupEgress(req) + } else { + req := &ec2.RevokeSecurityGroupIngressRequest{ + GroupID: group.GroupID, + IPPermissions: remove, + } + err = ec2conn.RevokeSecurityGroupIngress(req) } - log.Printf("[DEBUG] Revoking security group %s %s rule: %#v", - group, ruleset, remove) - if _, err := revoke(group, remove); err != nil { + if err != nil { return fmt.Errorf( - "Error revoking security group %s rules: %s", + "Error authorizing security group %s rules: %s", ruleset, err) } } if len(add) > 0 { + log.Printf("[DEBUG] Authorizing security group %#v %s rule: %#v", + group, ruleset, add) // Authorize the new rules - authorize := ec2conn.AuthorizeSecurityGroup if ruleset == "egress" { - authorize = ec2conn.AuthorizeSecurityGroupEgress + req := &ec2.AuthorizeSecurityGroupEgressRequest{ + GroupID: group.GroupID, + IPPermissions: add, + } + err = ec2conn.AuthorizeSecurityGroupEgress(req) + } else { + req := &ec2.AuthorizeSecurityGroupIngressRequest{ + GroupID: group.GroupID, + IPPermissions: add, + } + err = ec2conn.AuthorizeSecurityGroupIngress(req) } - log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v", - group, ruleset, add) - if _, err := authorize(group, add); err != nil { + if err != nil { return fmt.Errorf( "Error authorizing security group %s rules: %s", ruleset, err) @@ -431,7 +463,6 @@ func resourceAwsSecurityGroupUpdateRules( } } } - return nil } @@ -439,10 +470,12 @@ func resourceAwsSecurityGroupUpdateRules( // a security group. func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - sgs := []ec2.SecurityGroup{ec2.SecurityGroup{Id: id}} - resp, err := conn.SecurityGroups(sgs, nil) + req := &ec2.DescribeSecurityGroupsRequest{ + GroupIDs: []string{id}, + } + resp, err := conn.DescribeSecurityGroups(req) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok { + if ec2err, ok := err.(aws.APIError); ok { if ec2err.Code == "InvalidSecurityGroupID.NotFound" || ec2err.Code == "InvalidGroup.NotFound" { resp = nil @@ -460,7 +493,7 @@ func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return nil, "", nil } - group := &resp.Groups[0] + group := resp.SecurityGroups[0] return group, "exists", nil } } diff --git a/builtin/providers/aws/resource_aws_security_group_test.go b/builtin/providers/aws/resource_aws_security_group_test.go index d31f9754b..b1e4e8c82 100644 --- a/builtin/providers/aws/resource_aws_security_group_test.go +++ b/builtin/providers/aws/resource_aws_security_group_test.go @@ -2,16 +2,18 @@ package aws import ( "fmt" + "log" "reflect" "testing" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestAccAWSSecurityGroup_normal(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -44,7 +46,7 @@ func TestAccAWSSecurityGroup_normal(t *testing.T) { } func TestAccAWSSecurityGroup_self(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup checkSelf := func(s *terraform.State) (err error) { defer func() { @@ -53,7 +55,7 @@ func TestAccAWSSecurityGroup_self(t *testing.T) { } }() - if group.IPPerms[0].SourceGroups[0].Id != group.Id { + if *group.IPPermissions[0].UserIDGroupPairs[0].GroupID != *group.GroupID { return fmt.Errorf("bad: %#v", group) } @@ -89,10 +91,10 @@ func TestAccAWSSecurityGroup_self(t *testing.T) { } func TestAccAWSSecurityGroup_vpc(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup testCheck := func(*terraform.State) error { - if group.VpcId == "" { + if *group.VPCID == "" { return fmt.Errorf("should have vpc ID") } @@ -141,7 +143,7 @@ func TestAccAWSSecurityGroup_vpc(t *testing.T) { } func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -159,7 +161,7 @@ func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) { } func TestAccAWSSecurityGroup_Change(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -191,23 +193,20 @@ func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error { continue } - sgs := []ec2.SecurityGroup{ - ec2.SecurityGroup{ - Id: rs.Primary.ID, - }, - } - // Retrieve our group - resp, err := conn.SecurityGroups(sgs, nil) + req := &ec2.DescribeSecurityGroupsRequest{ + GroupIDs: []string{rs.Primary.ID}, + } + resp, err := conn.DescribeSecurityGroups(req) if err == nil { - if len(resp.Groups) > 0 && resp.Groups[0].Id == rs.Primary.ID { + if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupID == rs.Primary.ID { return fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID) } return nil } - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(aws.APIError) if !ok { return err } @@ -220,7 +219,7 @@ func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error { return nil } -func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo) resource.TestCheckFunc { +func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -232,19 +231,18 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo) } conn := testAccProvider.Meta().(*AWSClient).ec2conn - sgs := []ec2.SecurityGroup{ - ec2.SecurityGroup{ - Id: rs.Primary.ID, - }, + req := &ec2.DescribeSecurityGroupsRequest{ + GroupIDs: []string{rs.Primary.ID}, } - resp, err := conn.SecurityGroups(sgs, nil) + resp, err := conn.DescribeSecurityGroups(req) if err != nil { return err } - if len(resp.Groups) > 0 && resp.Groups[0].Id == rs.Primary.ID { + if len(resp.SecurityGroups) > 0 && *resp.SecurityGroups[0].GroupID == rs.Primary.ID { - *group = resp.Groups[0] + log.Printf("\n==\n===\nfound group\n===\n==\n") + *group = resp.SecurityGroups[0] return nil } @@ -253,32 +251,32 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo) } } -func testAccCheckAWSSecurityGroupAttributes(group *ec2.SecurityGroupInfo) resource.TestCheckFunc { +func testAccCheckAWSSecurityGroupAttributes(group *ec2.SecurityGroup) resource.TestCheckFunc { return func(s *terraform.State) error { - p := ec2.IPPerm{ - FromPort: 80, - ToPort: 8000, - Protocol: "tcp", - SourceIPs: []string{"10.0.0.0/8"}, + p := ec2.IPPermission{ + FromPort: aws.Integer(80), + ToPort: aws.Integer(8000), + IPProtocol: aws.String("tcp"), + IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("10.0.0.0/8")}}, } - if group.Name != "terraform_acceptance_test_example" { - return fmt.Errorf("Bad name: %s", group.Name) + if *group.GroupName != "terraform_acceptance_test_example" { + return fmt.Errorf("Bad name: %s", *group.GroupName) } - if group.Description != "Used in the terraform acceptance tests" { - return fmt.Errorf("Bad description: %s", group.Description) + if *group.Description != "Used in the terraform acceptance tests" { + return fmt.Errorf("Bad description: %s", *group.Description) } - if len(group.IPPerms) == 0 { + if len(group.IPPermissions) == 0 { return fmt.Errorf("No IPPerms") } // Compare our ingress - if !reflect.DeepEqual(group.IPPerms[0], p) { + if !reflect.DeepEqual(group.IPPermissions[0], p) { return fmt.Errorf( "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - group.IPPerms[0], + group.IPPermissions[0], p) } @@ -287,7 +285,7 @@ func testAccCheckAWSSecurityGroupAttributes(group *ec2.SecurityGroupInfo) resour } func TestAccAWSSecurityGroup_tags(t *testing.T) { - var group ec2.SecurityGroupInfo + var group ec2.SecurityGroup resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -314,48 +312,48 @@ func TestAccAWSSecurityGroup_tags(t *testing.T) { }) } -func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroupInfo) resource.TestCheckFunc { +func testAccCheckAWSSecurityGroupAttributesChanged(group *ec2.SecurityGroup) resource.TestCheckFunc { return func(s *terraform.State) error { - p := []ec2.IPPerm{ - ec2.IPPerm{ - FromPort: 80, - ToPort: 9000, - Protocol: "tcp", - SourceIPs: []string{"10.0.0.0/8"}, + p := []ec2.IPPermission{ + ec2.IPPermission{ + FromPort: aws.Integer(80), + ToPort: aws.Integer(9000), + IPProtocol: aws.String("tcp"), + IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("10.0.0.0/8")}}, }, - ec2.IPPerm{ - FromPort: 80, - ToPort: 8000, - Protocol: "tcp", - SourceIPs: []string{"0.0.0.0/0", "10.0.0.0/8"}, + ec2.IPPermission{ + FromPort: aws.Integer(80), + ToPort: aws.Integer(8000), + IPProtocol: aws.String("tcp"), + IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("0.0.0.0/0")}, ec2.IPRange{aws.String("10.0.0.0/8")}}, }, } - if group.Name != "terraform_acceptance_test_example" { - return fmt.Errorf("Bad name: %s", group.Name) + if *group.GroupName != "terraform_acceptance_test_example" { + return fmt.Errorf("Bad name: %s", *group.GroupName) } - if group.Description != "Used in the terraform acceptance tests" { - return fmt.Errorf("Bad description: %s", group.Description) + if *group.Description != "Used in the terraform acceptance tests" { + return fmt.Errorf("Bad description: %s", *group.Description) } // Compare our ingress - if len(group.IPPerms) != 2 { + if len(group.IPPermissions) != 2 { return fmt.Errorf( "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - group.IPPerms, + group.IPPermissions, p) } - if group.IPPerms[0].ToPort == 8000 { - group.IPPerms[1], group.IPPerms[0] = - group.IPPerms[0], group.IPPerms[1] + if *group.IPPermissions[0].ToPort == 8000 { + group.IPPermissions[1], group.IPPermissions[0] = + group.IPPermissions[0], group.IPPermissions[1] } - if !reflect.DeepEqual(group.IPPerms, p) { + if !reflect.DeepEqual(group.IPPermissions, p) { return fmt.Errorf( "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - group.IPPerms, + group.IPPermissions, p) } diff --git a/builtin/providers/aws/resource_aws_subnet.go b/builtin/providers/aws/resource_aws_subnet.go index e09fb8bc4..d1db5aed9 100644 --- a/builtin/providers/aws/resource_aws_subnet.go +++ b/builtin/providers/aws/resource_aws_subnet.go @@ -51,7 +51,7 @@ func resourceAwsSubnet() *schema.Resource { } func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn createOpts := &ec2.CreateSubnetRequest{ AvailabilityZone: aws.String(d.Get("availability_zone").(string)), @@ -91,7 +91,7 @@ func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{ SubnetIDs: []string{d.Id()}, @@ -115,17 +115,17 @@ func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error { d.Set("availability_zone", subnet.AvailabilityZone) d.Set("cidr_block", subnet.CIDRBlock) d.Set("map_public_ip_on_launch", subnet.MapPublicIPOnLaunch) - d.Set("tags", tagsToMapSDK(subnet.Tags)) + d.Set("tags", tagsToMap(subnet.Tags)) return nil } func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn d.Partial(true) - if err := setTagsSDK(ec2conn, d); err != nil { + if err := setTags(ec2conn, d); err != nil { return err } else { d.SetPartial("tags") @@ -154,7 +154,7 @@ func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error { } func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error { - ec2conn := meta.(*AWSClient).awsEC2conn + ec2conn := meta.(*AWSClient).ec2conn log.Printf("[INFO] Deleting subnet: %s", d.Id()) diff --git a/builtin/providers/aws/resource_aws_subnet_test.go b/builtin/providers/aws/resource_aws_subnet_test.go index 77dfeccf0..ae7b28dee 100644 --- a/builtin/providers/aws/resource_aws_subnet_test.go +++ b/builtin/providers/aws/resource_aws_subnet_test.go @@ -43,7 +43,7 @@ func TestAccAWSSubnet(t *testing.T) { } func testAccCheckSubnetDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn for _, rs := range s.RootModule().Resources { if rs.Type != "aws_subnet" { @@ -86,7 +86,7 @@ func testAccCheckSubnetExists(n string, v *ec2.Subnet) resource.TestCheckFunc { return fmt.Errorf("No ID is set") } - conn := testAccProvider.Meta().(*AWSClient).awsEC2conn + conn := testAccProvider.Meta().(*AWSClient).ec2conn resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{ SubnetIDs: []string{rs.Primary.ID}, }) diff --git a/builtin/providers/aws/resource_aws_vpc.go b/builtin/providers/aws/resource_aws_vpc.go index f4ac2162e..0ef8aa570 100644 --- a/builtin/providers/aws/resource_aws_vpc.go +++ b/builtin/providers/aws/resource_aws_vpc.go @@ -5,9 +5,10 @@ import ( "log" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsVpc() *schema.Resource { @@ -64,22 +65,25 @@ func resourceAwsVpc() *schema.Resource { func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - - // Create the VPC - createOpts := &ec2.CreateVpc{ - CidrBlock: d.Get("cidr_block").(string), - InstanceTenancy: d.Get("instance_tenancy").(string), + instance_tenancy := "default" + if v, ok := d.GetOk("instance_tenancy"); ok { + instance_tenancy = v.(string) } - log.Printf("[DEBUG] VPC create config: %#v", createOpts) - vpcResp, err := ec2conn.CreateVpc(createOpts) + // Create the VPC + createOpts := &ec2.CreateVPCRequest{ + CIDRBlock: aws.String(d.Get("cidr_block").(string)), + InstanceTenancy: aws.String(instance_tenancy), + } + log.Printf("[DEBUG] VPC create config: %#v", *createOpts) + vpcResp, err := ec2conn.CreateVPC(createOpts) if err != nil { return fmt.Errorf("Error creating VPC: %s", err) } // Get the ID and store it - vpc := &vpcResp.VPC - log.Printf("[INFO] VPC ID: %s", vpc.VpcId) - d.SetId(vpc.VpcId) + vpc := vpcResp.VPC + d.SetId(*vpc.VPCID) + log.Printf("[INFO] VPC ID: %s", d.Id()) // Set partial mode and say that we setup the cidr block d.Partial(true) @@ -120,34 +124,53 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { // VPC stuff vpc := vpcRaw.(*ec2.VPC) - d.Set("cidr_block", vpc.CidrBlock) + vpcid := d.Id() + d.Set("cidr_block", vpc.CIDRBlock) // Tags d.Set("tags", tagsToMap(vpc.Tags)) // Attributes - resp, err := ec2conn.VpcAttribute(d.Id(), "enableDnsSupport") + attribute := "enableDnsSupport" + DescribeAttrOpts := &ec2.DescribeVPCAttributeRequest{ + Attribute: aws.String(attribute), + VPCID: aws.String(vpcid), + } + resp, err := ec2conn.DescribeVPCAttribute(DescribeAttrOpts) if err != nil { return err } - d.Set("enable_dns_support", resp.EnableDnsSupport) - - resp, err = ec2conn.VpcAttribute(d.Id(), "enableDnsHostnames") + d.Set("enable_dns_support", *resp.EnableDNSSupport) + attribute = "enableDnsHostnames" + DescribeAttrOpts = &ec2.DescribeVPCAttributeRequest{ + Attribute: &attribute, + VPCID: &vpcid, + } + resp, err = ec2conn.DescribeVPCAttribute(DescribeAttrOpts) if err != nil { return err } - d.Set("enable_dns_hostnames", resp.EnableDnsHostnames) + d.Set("enable_dns_hostnames", *resp.EnableDNSHostnames) // Get the main routing table for this VPC - filter := ec2.NewFilter() - filter.Add("association.main", "true") - filter.Add("vpc-id", d.Id()) - routeResp, err := ec2conn.DescribeRouteTables(nil, filter) + // Really Ugly need to make this better - rmenn + filter1 := &ec2.Filter{ + Name: aws.String("association.main"), + Values: []string{("true")}, + } + filter2 := &ec2.Filter{ + Name: aws.String("vpc-id"), + Values: []string{(d.Id())}, + } + DescribeRouteOpts := &ec2.DescribeRouteTablesRequest{ + Filters: []ec2.Filter{*filter1, *filter2}, + } + routeResp, err := ec2conn.DescribeRouteTables(DescribeRouteOpts) if err != nil { return err } if v := routeResp.RouteTables; len(v) > 0 { - d.Set("main_route_table_id", v[0].RouteTableId) + d.Set("main_route_table_id", *v[0].RouteTableID) } resourceAwsVpcSetDefaultNetworkAcl(ec2conn, d) @@ -161,16 +184,20 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { // Turn on partial mode d.Partial(true) - + vpcid := d.Id() + modifyOpts := &ec2.ModifyVPCAttributeRequest{ + VPCID: &vpcid, + } if d.HasChange("enable_dns_hostnames") { - options := new(ec2.ModifyVpcAttribute) - options.EnableDnsHostnames = d.Get("enable_dns_hostnames").(bool) - options.SetEnableDnsHostnames = true + val := d.Get("enable_dns_hostnames").(bool) + modifyOpts.EnableDNSHostnames = &ec2.AttributeBooleanValue{ + Value: &val, + } log.Printf( "[INFO] Modifying enable_dns_hostnames vpc attribute for %s: %#v", - d.Id(), options) - if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { + d.Id(), modifyOpts) + if err := ec2conn.ModifyVPCAttribute(modifyOpts); err != nil { return err } @@ -178,14 +205,15 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("enable_dns_support") { - options := new(ec2.ModifyVpcAttribute) - options.EnableDnsSupport = d.Get("enable_dns_support").(bool) - options.SetEnableDnsSupport = true + val := d.Get("enable_dns_hostnames").(bool) + modifyOpts.EnableDNSSupport = &ec2.AttributeBooleanValue{ + Value: &val, + } log.Printf( "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", - d.Id(), options) - if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { + d.Id(), modifyOpts) + if err := ec2conn.ModifyVPCAttribute(modifyOpts); err != nil { return err } @@ -204,10 +232,13 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - + vpcID := d.Id() + DeleteVpcOpts := &ec2.DeleteVPCRequest{ + VPCID: &vpcID, + } log.Printf("[INFO] Deleting VPC: %s", d.Id()) - if _, err := ec2conn.DeleteVpc(d.Id()); err != nil { - ec2err, ok := err.(*ec2.Error) + if err := ec2conn.DeleteVPC(DeleteVpcOpts); err != nil { + ec2err, ok := err.(*aws.APIError) if ok && ec2err.Code == "InvalidVpcID.NotFound" { return nil } @@ -222,9 +253,12 @@ func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { // a VPC. func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeVpcs([]string{id}, ec2.NewFilter()) + DescribeVpcOpts := &ec2.DescribeVPCsRequest{ + VPCIDs: []string{id}, + } + resp, err := conn.DescribeVPCs(DescribeVpcOpts) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidVpcID.NotFound" { + if ec2err, ok := err.(*aws.APIError); ok && ec2err.Code == "InvalidVpcID.NotFound" { resp = nil } else { log.Printf("Error on VPCStateRefresh: %s", err) @@ -239,37 +273,53 @@ func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { } vpc := &resp.VPCs[0] - return vpc, vpc.State, nil + return vpc, *vpc.State, nil } } func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { - filter := ec2.NewFilter() - filter.Add("default", "true") - filter.Add("vpc-id", d.Id()) - networkAclResp, err := conn.NetworkAcls(nil, filter) + filter1 := &ec2.Filter{ + Name: aws.String("default"), + Values: []string{("true")}, + } + filter2 := &ec2.Filter{ + Name: aws.String("vpc-id"), + Values: []string{(d.Id())}, + } + DescribeNetworkACLOpts := &ec2.DescribeNetworkACLsRequest{ + Filters: []ec2.Filter{*filter1, *filter2}, + } + networkAclResp, err := conn.DescribeNetworkACLs(DescribeNetworkACLOpts) if err != nil { return err } - if v := networkAclResp.NetworkAcls; len(v) > 0 { - d.Set("default_network_acl_id", v[0].NetworkAclId) + if v := networkAclResp.NetworkACLs; len(v) > 0 { + d.Set("default_network_acl_id", v[0].NetworkACLID) } return nil } func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { - filter := ec2.NewFilter() - filter.Add("group-name", "default") - filter.Add("vpc-id", d.Id()) - securityGroupResp, err := conn.SecurityGroups(nil, filter) + filter1 := &ec2.Filter{ + Name: aws.String("group-name"), + Values: []string{("default")}, + } + filter2 := &ec2.Filter{ + Name: aws.String("vpc-id"), + Values: []string{(d.Id())}, + } + DescribeSgOpts := &ec2.DescribeSecurityGroupsRequest{ + Filters: []ec2.Filter{*filter1, *filter2}, + } + securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) if err != nil { return err } - if v := securityGroupResp.Groups; len(v) > 0 { - d.Set("default_security_group_id", v[0].Id) + if v := securityGroupResp.SecurityGroups; len(v) > 0 { + d.Set("default_security_group_id", v[0].GroupID) } return nil diff --git a/builtin/providers/aws/resource_aws_vpc_peering_connection.go b/builtin/providers/aws/resource_aws_vpc_peering_connection.go index a8316c114..0f8c6185b 100644 --- a/builtin/providers/aws/resource_aws_vpc_peering_connection.go +++ b/builtin/providers/aws/resource_aws_vpc_peering_connection.go @@ -5,9 +5,10 @@ import ( "log" "time" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) func resourceAwsVpcPeeringConnection() *schema.Resource { @@ -19,9 +20,10 @@ func resourceAwsVpcPeeringConnection() *schema.Resource { Schema: map[string]*schema.Schema{ "peer_owner_id": &schema.Schema{ - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: schema.EnvDefaultFunc("AWS_ACCOUNT_ID", nil), }, "peer_vpc_id": &schema.Schema{ Type: schema.TypeString, @@ -42,20 +44,20 @@ func resourceAwsVpcPeeringCreate(d *schema.ResourceData, meta interface{}) error ec2conn := meta.(*AWSClient).ec2conn // Create the vpc peering connection - createOpts := &ec2.CreateVpcPeeringConnection{ - PeerOwnerId: d.Get("peer_owner_id").(string), - PeerVpcId: d.Get("peer_vpc_id").(string), - VpcId: d.Get("vpc_id").(string), + createOpts := &ec2.CreateVPCPeeringConnectionRequest{ + PeerOwnerID: aws.String(d.Get("peer_owner_id").(string)), + PeerVPCID: aws.String(d.Get("peer_vpc_id").(string)), + VPCID: aws.String(d.Get("vpc_id").(string)), } log.Printf("[DEBUG] VpcPeeringCreate create config: %#v", createOpts) - resp, err := ec2conn.CreateVpcPeeringConnection(createOpts) + resp, err := ec2conn.CreateVPCPeeringConnection(createOpts) if err != nil { return fmt.Errorf("Error creating vpc peering connection: %s", err) } // Get the ID and store it - rt := &resp.VpcPeeringConnection - d.SetId(rt.VpcPeeringConnectionId) + rt := resp.VPCPeeringConnection + d.SetId(*rt.VPCPeeringConnectionID) log.Printf("[INFO] Vpc Peering Connection ID: %s", d.Id()) // Wait for the vpc peering connection to become available @@ -88,11 +90,11 @@ func resourceAwsVpcPeeringRead(d *schema.ResourceData, meta interface{}) error { return nil } - pc := pcRaw.(*ec2.VpcPeeringConnection) + pc := pcRaw.(*ec2.VPCPeeringConnection) - d.Set("peer_owner_id", pc.AccepterVpcInfo.OwnerId) - d.Set("peer_vpc_id", pc.AccepterVpcInfo.VpcId) - d.Set("vpc_id", pc.RequesterVpcInfo.VpcId) + d.Set("peer_owner_id", pc.AccepterVPCInfo.OwnerID) + d.Set("peer_vpc_id", pc.AccepterVPCInfo.VPCID) + d.Set("vpc_id", pc.RequesterVPCInfo.VPCID) d.Set("tags", tagsToMap(pc.Tags)) return nil @@ -113,7 +115,10 @@ func resourceAwsVpcPeeringUpdate(d *schema.ResourceData, meta interface{}) error func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error { ec2conn := meta.(*AWSClient).ec2conn - _, err := ec2conn.DeleteVpcPeeringConnection(d.Id()) + _, err := ec2conn.DeleteVPCPeeringConnection( + &ec2.DeleteVPCPeeringConnectionRequest{ + VPCPeeringConnectionID: aws.String(d.Id()), + }) return err } @@ -122,9 +127,11 @@ func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error func resourceAwsVpcPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - resp, err := conn.DescribeVpcPeeringConnection([]string{id}, ec2.NewFilter()) + resp, err := conn.DescribeVPCPeeringConnections(&ec2.DescribeVPCPeeringConnectionsRequest{ + VPCPeeringConnectionIDs: []string{id}, + }) if err != nil { - if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidVpcPeeringConnectionID.NotFound" { + if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpcPeeringConnectionID.NotFound" { resp = nil } else { log.Printf("Error on VpcPeeringConnectionStateRefresh: %s", err) @@ -138,7 +145,7 @@ func resourceAwsVpcPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) r return nil, "", nil } - pc := &resp.VpcPeeringConnections[0] + pc := &resp.VPCPeeringConnections[0] return pc, "ready", nil } diff --git a/builtin/providers/aws/resource_aws_vpc_peering_connection_test.go b/builtin/providers/aws/resource_aws_vpc_peering_connection_test.go index 2b4b71e33..c374646b8 100644 --- a/builtin/providers/aws/resource_aws_vpc_peering_connection_test.go +++ b/builtin/providers/aws/resource_aws_vpc_peering_connection_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestAccAWSVPCPeeringConnection_normal(t *testing.T) { @@ -35,10 +35,13 @@ func testAccCheckAWSVpcPeeringConnectionDestroy(s *terraform.State) error { continue } - describe, err := conn.DescribeVpcPeeringConnection([]string{rs.Primary.ID}, ec2.NewFilter()) + describe, err := conn.DescribeVPCPeeringConnections( + &ec2.DescribeVPCPeeringConnectionsRequest{ + VPCPeeringConnectionIDs: []string{rs.Primary.ID}, + }) if err == nil { - if len(describe.VpcPeeringConnections) != 0 { + if len(describe.VPCPeeringConnections) != 0 { return fmt.Errorf("vpc peering connection still exists") } } @@ -68,11 +71,10 @@ resource "aws_vpc" "foo" { } resource "aws_vpc" "bar" { - cidr_block = "10.0.1.0/16" + cidr_block = "10.1.0.0/16" } resource "aws_vpc_peering_connection" "foo" { - peer_owner_id = "12345" vpc_id = "${aws_vpc.foo.id}" peer_vpc_id = "${aws_vpc.bar.id}" } diff --git a/builtin/providers/aws/resource_aws_vpc_test.go b/builtin/providers/aws/resource_aws_vpc_test.go index b555e0875..092f47806 100644 --- a/builtin/providers/aws/resource_aws_vpc_test.go +++ b/builtin/providers/aws/resource_aws_vpc_test.go @@ -2,11 +2,11 @@ package aws import ( "fmt" - "testing" - + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" + "testing" ) func TestAccVpc_basic(t *testing.T) { @@ -119,7 +119,10 @@ func testAccCheckVpcDestroy(s *terraform.State) error { } // Try to find the VPC - resp, err := conn.DescribeVpcs([]string{rs.Primary.ID}, ec2.NewFilter()) + DescribeVpcOpts := &ec2.DescribeVPCsRequest{ + VPCIDs: []string{rs.Primary.ID}, + } + resp, err := conn.DescribeVPCs(DescribeVpcOpts) if err == nil { if len(resp.VPCs) > 0 { return fmt.Errorf("VPCs still exist.") @@ -129,7 +132,7 @@ func testAccCheckVpcDestroy(s *terraform.State) error { } // Verify the error is what we want - ec2err, ok := err.(*ec2.Error) + ec2err, ok := err.(*aws.APIError) if !ok { return err } @@ -143,8 +146,9 @@ func testAccCheckVpcDestroy(s *terraform.State) error { func testAccCheckVpcCidr(vpc *ec2.VPC, expected string) resource.TestCheckFunc { return func(s *terraform.State) error { - if vpc.CidrBlock != expected { - return fmt.Errorf("Bad cidr: %s", vpc.CidrBlock) + CIDRBlock := vpc.CIDRBlock + if *CIDRBlock != expected { + return fmt.Errorf("Bad cidr: %s", *vpc.CIDRBlock) } return nil @@ -163,7 +167,10 @@ func testAccCheckVpcExists(n string, vpc *ec2.VPC) resource.TestCheckFunc { } conn := testAccProvider.Meta().(*AWSClient).ec2conn - resp, err := conn.DescribeVpcs([]string{rs.Primary.ID}, ec2.NewFilter()) + DescribeVpcOpts := &ec2.DescribeVPCsRequest{ + VPCIDs: []string{rs.Primary.ID}, + } + resp, err := conn.DescribeVPCs(DescribeVpcOpts) if err != nil { return err } diff --git a/builtin/providers/aws/structure.go b/builtin/providers/aws/structure.go index 7d4793d3d..617c2bbf9 100644 --- a/builtin/providers/aws/structure.go +++ b/builtin/providers/aws/structure.go @@ -4,10 +4,10 @@ import ( "strings" "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/aws-sdk-go/gen/elb" "github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) // Takes the result of flatmap.Expand for an array of listeners and @@ -16,7 +16,7 @@ func expandListeners(configured []interface{}) ([]elb.Listener, error) { listeners := make([]elb.Listener, 0, len(configured)) // Loop over our configured listeners and create - // an array of goamz compatabile objects + // an array of aws-sdk-go compatabile objects for _, lRaw := range configured { data := lRaw.(map[string]interface{}) @@ -39,15 +39,15 @@ func expandListeners(configured []interface{}) ([]elb.Listener, error) { // Takes the result of flatmap.Expand for an array of ingress/egress // security group rules and returns EC2 API compatible objects -func expandIPPerms(id string, configured []interface{}) []ec2.IPPerm { - perms := make([]ec2.IPPerm, len(configured)) +func expandIPPerms(id string, configured []interface{}) []ec2.IPPermission { + perms := make([]ec2.IPPermission, len(configured)) for i, mRaw := range configured { - var perm ec2.IPPerm + var perm ec2.IPPermission m := mRaw.(map[string]interface{}) - perm.FromPort = m["from_port"].(int) - perm.ToPort = m["to_port"].(int) - perm.Protocol = m["protocol"].(string) + perm.FromPort = aws.Integer(m["from_port"].(int)) + perm.ToPort = aws.Integer(m["to_port"].(int)) + perm.IPProtocol = aws.String(m["protocol"].(string)) var groups []string if raw, ok := m["security_groups"]; ok { @@ -61,25 +61,25 @@ func expandIPPerms(id string, configured []interface{}) []ec2.IPPerm { } if len(groups) > 0 { - perm.SourceGroups = make([]ec2.UserSecurityGroup, len(groups)) + perm.UserIDGroupPairs = make([]ec2.UserIDGroupPair, len(groups)) for i, name := range groups { ownerId, id := "", name if items := strings.Split(id, "/"); len(items) > 1 { ownerId, id = items[0], items[1] } - perm.SourceGroups[i] = ec2.UserSecurityGroup{ - Id: id, - OwnerId: ownerId, + perm.UserIDGroupPairs[i] = ec2.UserIDGroupPair{ + GroupID: aws.String(id), + UserID: aws.String(ownerId), } } } if raw, ok := m["cidr_blocks"]; ok { list := raw.([]interface{}) - perm.SourceIPs = make([]string, len(list)) + perm.IPRanges = make([]ec2.IPRange, len(list)) for i, v := range list { - perm.SourceIPs[i] = v.(string) + perm.IPRanges[i] = ec2.IPRange{aws.String(v.(string))} } } @@ -95,7 +95,7 @@ func expandParameters(configured []interface{}) ([]rds.Parameter, error) { parameters := make([]rds.Parameter, 0, len(configured)) // Loop over our configured parameters and create - // an array of goamz compatabile objects + // an array of aws-sdk-go compatabile objects for _, pRaw := range configured { data := pRaw.(map[string]interface{}) @@ -111,31 +111,6 @@ func expandParameters(configured []interface{}) ([]rds.Parameter, error) { return parameters, nil } -// Flattens an array of ipPerms into a list of primitives that -// flatmap.Flatten() can handle -func flattenIPPerms(list []ec2.IPPerm) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(list)) - - for _, perm := range list { - n := make(map[string]interface{}) - n["from_port"] = perm.FromPort - n["protocol"] = perm.Protocol - n["to_port"] = perm.ToPort - - if len(perm.SourceIPs) > 0 { - n["cidr_blocks"] = perm.SourceIPs - } - - if v := flattenSecurityGroups(perm.SourceGroups); len(v) > 0 { - n["security_groups"] = v - } - - result = append(result, n) - } - - return result -} - // Flattens a health check into something that flatmap.Flatten() // can handle func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { @@ -154,10 +129,10 @@ func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { } // Flattens an array of UserSecurityGroups into a []string -func flattenSecurityGroups(list []ec2.UserSecurityGroup) []string { +func flattenSecurityGroups(list []ec2.UserIDGroupPair) []string { result := make([]string, 0, len(list)) for _, g := range list { - result = append(result, g.Id) + result = append(result, *g.GroupID) } return result } diff --git a/builtin/providers/aws/structure_test.go b/builtin/providers/aws/structure_test.go index f3a8bcc72..b85adc51a 100644 --- a/builtin/providers/aws/structure_test.go +++ b/builtin/providers/aws/structure_test.go @@ -5,12 +5,12 @@ import ( "testing" "github.com/hashicorp/aws-sdk-go/aws" + ec2 "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/aws-sdk-go/gen/elb" "github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/terraform/flatmap" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) // Returns test configuration @@ -61,120 +61,58 @@ func TestExpandIPPerms(t *testing.T) { } perms := expandIPPerms("foo", expanded) - expected := []ec2.IPPerm{ - ec2.IPPerm{ - Protocol: "icmp", - FromPort: 1, - ToPort: -1, - SourceIPs: []string{"0.0.0.0/0"}, - SourceGroups: []ec2.UserSecurityGroup{ - ec2.UserSecurityGroup{ - OwnerId: "foo", - Id: "sg-22222", + expected := []ec2.IPPermission{ + ec2.IPPermission{ + IPProtocol: aws.String("icmp"), + FromPort: aws.Integer(1), + ToPort: aws.Integer(-1), + IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("0.0.0.0/0")}}, + UserIDGroupPairs: []ec2.UserIDGroupPair{ + ec2.UserIDGroupPair{ + UserID: aws.String("foo"), + GroupID: aws.String("sg-22222"), }, - ec2.UserSecurityGroup{ - Id: "sg-11111", + ec2.UserIDGroupPair{ + GroupID: aws.String("sg-22222"), }, }, }, - ec2.IPPerm{ - Protocol: "icmp", - FromPort: 1, - ToPort: -1, - SourceGroups: []ec2.UserSecurityGroup{ - ec2.UserSecurityGroup{ - Id: "foo", + ec2.IPPermission{ + IPProtocol: aws.String("icmp"), + FromPort: aws.Integer(1), + ToPort: aws.Integer(-1), + UserIDGroupPairs: []ec2.UserIDGroupPair{ + ec2.UserIDGroupPair{ + UserID: aws.String("foo"), }, }, }, } - if !reflect.DeepEqual(perms, expected) { + exp := expected[0] + perm := perms[0] + + if *exp.FromPort != *perm.FromPort { t.Fatalf( "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - perms[0], - expected) + *perm.FromPort, + *exp.FromPort) } -} - -func TestFlattenIPPerms(t *testing.T) { - cases := []struct { - Input []ec2.IPPerm - Output []map[string]interface{} - }{ - { - Input: []ec2.IPPerm{ - ec2.IPPerm{ - Protocol: "icmp", - FromPort: 1, - ToPort: -1, - SourceIPs: []string{"0.0.0.0/0"}, - SourceGroups: []ec2.UserSecurityGroup{ - ec2.UserSecurityGroup{ - Id: "sg-11111", - }, - }, - }, - }, - - Output: []map[string]interface{}{ - map[string]interface{}{ - "protocol": "icmp", - "from_port": 1, - "to_port": -1, - "cidr_blocks": []string{"0.0.0.0/0"}, - "security_groups": []string{"sg-11111"}, - }, - }, - }, - - { - Input: []ec2.IPPerm{ - ec2.IPPerm{ - Protocol: "icmp", - FromPort: 1, - ToPort: -1, - SourceIPs: []string{"0.0.0.0/0"}, - SourceGroups: nil, - }, - }, - - Output: []map[string]interface{}{ - map[string]interface{}{ - "protocol": "icmp", - "from_port": 1, - "to_port": -1, - "cidr_blocks": []string{"0.0.0.0/0"}, - }, - }, - }, - { - Input: []ec2.IPPerm{ - ec2.IPPerm{ - Protocol: "icmp", - FromPort: 1, - ToPort: -1, - SourceIPs: nil, - }, - }, - - Output: []map[string]interface{}{ - map[string]interface{}{ - "protocol": "icmp", - "from_port": 1, - "to_port": -1, - }, - }, - }, + if *exp.IPRanges[0].CIDRIP != *perm.IPRanges[0].CIDRIP { + t.Fatalf( + "Got:\n\n%#v\n\nExpected:\n\n%#v\n", + *perm.IPRanges[0].CIDRIP, + *exp.IPRanges[0].CIDRIP) } - for _, tc := range cases { - output := flattenIPPerms(tc.Input) - if !reflect.DeepEqual(output, tc.Output) { - t.Fatalf("Input:\n\n%#v\n\nOutput:\n\n%#v", tc.Input, output) - } + if *exp.UserIDGroupPairs[0].UserID != *perm.UserIDGroupPairs[0].UserID { + t.Fatalf( + "Got:\n\n%#v\n\nExpected:\n\n%#v\n", + *perm.UserIDGroupPairs[0].UserID, + *exp.UserIDGroupPairs[0].UserID) } + } func TestExpandListeners(t *testing.T) { diff --git a/builtin/providers/aws/tags.go b/builtin/providers/aws/tags.go index b45875c59..1c64b18b4 100644 --- a/builtin/providers/aws/tags.go +++ b/builtin/providers/aws/tags.go @@ -3,11 +3,13 @@ package aws import ( "log" + "github.com/hashicorp/aws-sdk-go/aws" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/schema" - "github.com/mitchellh/goamz/ec2" ) // tagsSchema returns the schema to use for tags. +// func tagsSchema() *schema.Schema { return &schema.Schema{ Type: schema.TypeMap, @@ -27,13 +29,21 @@ func setTags(conn *ec2.EC2, d *schema.ResourceData) error { // Set tags if len(remove) > 0 { log.Printf("[DEBUG] Removing tags: %#v", remove) - if _, err := conn.DeleteTags([]string{d.Id()}, remove); err != nil { + err := conn.DeleteTags(&ec2.DeleteTagsRequest{ + Resources: []string{d.Id()}, + Tags: remove, + }) + if err != nil { return err } } if len(create) > 0 { log.Printf("[DEBUG] Creating tags: %#v", create) - if _, err := conn.CreateTags([]string{d.Id()}, create); err != nil { + err := conn.CreateTags(&ec2.CreateTagsRequest{ + Resources: []string{d.Id()}, + Tags: create, + }) + if err != nil { return err } } @@ -49,14 +59,14 @@ func diffTags(oldTags, newTags []ec2.Tag) ([]ec2.Tag, []ec2.Tag) { // First, we're creating everything we have create := make(map[string]interface{}) for _, t := range newTags { - create[t.Key] = t.Value + create[*t.Key] = *t.Value } // Build the list of what to remove var remove []ec2.Tag for _, t := range oldTags { - old, ok := create[t.Key] - if !ok || old != t.Value { + old, ok := create[*t.Key] + if !ok || old != *t.Value { // Delete it! remove = append(remove, t) } @@ -70,8 +80,8 @@ func tagsFromMap(m map[string]interface{}) []ec2.Tag { result := make([]ec2.Tag, 0, len(m)) for k, v := range m { result = append(result, ec2.Tag{ - Key: k, - Value: v.(string), + Key: aws.String(k), + Value: aws.String(v.(string)), }) } @@ -82,7 +92,7 @@ func tagsFromMap(m map[string]interface{}) []ec2.Tag { func tagsToMap(ts []ec2.Tag) map[string]string { result := make(map[string]string) for _, t := range ts { - result[t.Key] = t.Value + result[*t.Key] = *t.Value } return result diff --git a/builtin/providers/aws/tags_sdk.go b/builtin/providers/aws/tags_sdk.go deleted file mode 100644 index 7e9690b78..000000000 --- a/builtin/providers/aws/tags_sdk.go +++ /dev/null @@ -1,106 +0,0 @@ -package aws - -// TODO: Clint: consolidate tags and tags_sdk -// tags_sdk and tags_sdk_test are used only for transition to aws-sdk-go -// and will replace tags and tags_test when the transition to aws-sdk-go/ec2 is -// complete - -import ( - "log" - - "github.com/hashicorp/aws-sdk-go/aws" - "github.com/hashicorp/aws-sdk-go/gen/ec2" - "github.com/hashicorp/terraform/helper/schema" -) - -// tagsSchema returns the schema to use for tags. -// -// TODO: uncomment this when we replace the original tags.go -// -// func tagsSchema() *schema.Schema { -// return &schema.Schema{ -// Type: schema.TypeMap, -// Optional: true, -// } -// } - -// setTags is a helper to set the tags for a resource. It expects the -// tags field to be named "tags" -func setTagsSDK(conn *ec2.EC2, 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 := diffTagsSDK(tagsFromMapSDK(o), tagsFromMapSDK(n)) - - // Set tags - if len(remove) > 0 { - log.Printf("[DEBUG] Removing tags: %#v", remove) - err := conn.DeleteTags(&ec2.DeleteTagsRequest{ - Resources: []string{d.Id()}, - Tags: remove, - }) - if err != nil { - return err - } - } - if len(create) > 0 { - log.Printf("[DEBUG] Creating tags: %#v", create) - err := conn.CreateTags(&ec2.CreateTagsRequest{ - Resources: []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 diffTagsSDK(oldTags, newTags []ec2.Tag) ([]ec2.Tag, []ec2.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 []ec2.Tag - for _, t := range oldTags { - old, ok := create[*t.Key] - if !ok || old != *t.Value { - // Delete it! - remove = append(remove, t) - } - } - - return tagsFromMapSDK(create), remove -} - -// tagsFromMap returns the tags for the given map of data. -func tagsFromMapSDK(m map[string]interface{}) []ec2.Tag { - result := make([]ec2.Tag, 0, len(m)) - for k, v := range m { - result = append(result, ec2.Tag{ - Key: aws.String(k), - Value: aws.String(v.(string)), - }) - } - - return result -} - -// tagsToMap turns the list of tags into a map. -func tagsToMapSDK(ts []ec2.Tag) map[string]string { - result := make(map[string]string) - for _, t := range ts { - result[*t.Key] = *t.Value - } - - return result -} diff --git a/builtin/providers/aws/tags_sdk_test.go b/builtin/providers/aws/tags_sdk_test.go deleted file mode 100644 index 5a5b0e600..000000000 --- a/builtin/providers/aws/tags_sdk_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package aws - -import ( - "fmt" - "reflect" - "testing" - - "github.com/hashicorp/aws-sdk-go/gen/ec2" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestDiffTagsSDK(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 := diffTagsSDK(tagsFromMapSDK(tc.Old), tagsFromMapSDK(tc.New)) - cm := tagsToMapSDK(c) - rm := tagsToMapSDK(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 testAccCheckTagsSDK( - ts *[]ec2.Tag, key string, value string) resource.TestCheckFunc { - return func(s *terraform.State) error { - m := tagsToMapSDK(*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 - } -} diff --git a/builtin/providers/aws/tags_test.go b/builtin/providers/aws/tags_test.go index 6e89492ca..16578ac1b 100644 --- a/builtin/providers/aws/tags_test.go +++ b/builtin/providers/aws/tags_test.go @@ -5,9 +5,9 @@ import ( "reflect" "testing" + "github.com/hashicorp/aws-sdk-go/gen/ec2" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/mitchellh/goamz/ec2" ) func TestDiffTags(t *testing.T) { diff --git a/builtin/providers/cloudstack/provider.go b/builtin/providers/cloudstack/provider.go index a9913f6e8..8bea67ef5 100644 --- a/builtin/providers/cloudstack/provider.go +++ b/builtin/providers/cloudstack/provider.go @@ -30,22 +30,25 @@ func Provider() terraform.ResourceProvider { "timeout": &schema.Schema{ Type: schema.TypeInt, Required: true, - DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_TIMEOUT", 180), + DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_TIMEOUT", 300), }, }, ResourcesMap: map[string]*schema.Resource{ - "cloudstack_disk": resourceCloudStackDisk(), - "cloudstack_egress_firewall": resourceCloudStackEgressFirewall(), - "cloudstack_firewall": resourceCloudStackFirewall(), - "cloudstack_instance": resourceCloudStackInstance(), - "cloudstack_ipaddress": resourceCloudStackIPAddress(), - "cloudstack_network": resourceCloudStackNetwork(), - "cloudstack_network_acl": resourceCloudStackNetworkACL(), - "cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(), - "cloudstack_nic": resourceCloudStackNIC(), - "cloudstack_port_forward": resourceCloudStackPortForward(), - "cloudstack_vpc": resourceCloudStackVPC(), + "cloudstack_disk": resourceCloudStackDisk(), + "cloudstack_egress_firewall": resourceCloudStackEgressFirewall(), + "cloudstack_firewall": resourceCloudStackFirewall(), + "cloudstack_instance": resourceCloudStackInstance(), + "cloudstack_ipaddress": resourceCloudStackIPAddress(), + "cloudstack_network": resourceCloudStackNetwork(), + "cloudstack_network_acl": resourceCloudStackNetworkACL(), + "cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(), + "cloudstack_nic": resourceCloudStackNIC(), + "cloudstack_port_forward": resourceCloudStackPortForward(), + "cloudstack_vpc": resourceCloudStackVPC(), + "cloudstack_vpn_connection": resourceCloudStackVPNConnection(), + "cloudstack_vpn_customer_gateway": resourceCloudStackVPNCustomerGateway(), + "cloudstack_vpn_gateway": resourceCloudStackVPNGateway(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/cloudstack/provider_test.go b/builtin/providers/cloudstack/provider_test.go index a13839177..537a2664f 100644 --- a/builtin/providers/cloudstack/provider_test.go +++ b/builtin/providers/cloudstack/provider_test.go @@ -51,7 +51,8 @@ var CLOUDSTACK_NETWORK_1_OFFERING = "" var CLOUDSTACK_NETWORK_1_IPADDRESS = "" var CLOUDSTACK_NETWORK_2 = "" var CLOUDSTACK_NETWORK_2_IPADDRESS = "" -var CLOUDSTACK_VPC_CIDR = "" +var CLOUDSTACK_VPC_CIDR_1 = "" +var CLOUDSTACK_VPC_CIDR_2 = "" var CLOUDSTACK_VPC_OFFERING = "" var CLOUDSTACK_VPC_NETWORK_CIDR = "" var CLOUDSTACK_VPC_NETWORK_OFFERING = "" diff --git a/builtin/providers/cloudstack/resource_cloudstack_instance.go b/builtin/providers/cloudstack/resource_cloudstack_instance.go index 600001a27..f0e52b588 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_instance.go +++ b/builtin/providers/cloudstack/resource_cloudstack_instance.go @@ -95,18 +95,18 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{}) return e.Error() } - // Retrieve the template UUID - templateid, e := retrieveUUID(cs, "template", d.Get("template").(string)) - if e != nil { - return e.Error() - } - // Retrieve the zone object zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string)) if err != nil { return err } + // Retrieve the template UUID + templateid, e := retrieveTemplateUUID(cs, zone.Id, d.Get("template").(string)) + if e != nil { + return e.Error() + } + // Create a new parameter struct p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id) diff --git a/builtin/providers/cloudstack/resource_cloudstack_ipaddress_test.go b/builtin/providers/cloudstack/resource_cloudstack_ipaddress_test.go index 5b1fc9a31..dfdeba209 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_ipaddress_test.go +++ b/builtin/providers/cloudstack/resource_cloudstack_ipaddress_test.go @@ -132,6 +132,6 @@ resource "cloudstack_vpc" "foobar" { resource "cloudstack_ipaddress" "foo" { vpc = "${cloudstack_vpc.foobar.name}" }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resource_cloudstack_network_acl_rule_test.go b/builtin/providers/cloudstack/resource_cloudstack_network_acl_rule_test.go index 037b9d10b..dbceb8d8d 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_network_acl_rule_test.go +++ b/builtin/providers/cloudstack/resource_cloudstack_network_acl_rule_test.go @@ -196,7 +196,7 @@ resource "cloudstack_network_acl_rule" "foo" { traffic_type = "ingress" } }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE) @@ -233,6 +233,6 @@ resource "cloudstack_network_acl_rule" "foo" { traffic_type = "egress" } }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resource_cloudstack_network_acl_test.go b/builtin/providers/cloudstack/resource_cloudstack_network_acl_test.go index e625d4c2d..9bf0bb0cf 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_network_acl_test.go +++ b/builtin/providers/cloudstack/resource_cloudstack_network_acl_test.go @@ -112,6 +112,6 @@ resource "cloudstack_network_acl" "foo" { description = "terraform-acl-text" vpc = "${cloudstack_vpc.foobar.name}" }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resource_cloudstack_network_test.go b/builtin/providers/cloudstack/resource_cloudstack_network_test.go index 750761f02..d936f8cb0 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_network_test.go +++ b/builtin/providers/cloudstack/resource_cloudstack_network_test.go @@ -186,7 +186,7 @@ resource "cloudstack_network" "foo" { aclid = "${cloudstack_network_acl.foo.id}" zone = "${cloudstack_vpc.foobar.zone}" }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE, CLOUDSTACK_VPC_NETWORK_CIDR, diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpc_test.go b/builtin/providers/cloudstack/resource_cloudstack_vpc_test.go index bf4e8f448..07861a091 100644 --- a/builtin/providers/cloudstack/resource_cloudstack_vpc_test.go +++ b/builtin/providers/cloudstack/resource_cloudstack_vpc_test.go @@ -72,8 +72,8 @@ func testAccCheckCloudStackVPCAttributes( return fmt.Errorf("Bad display text: %s", vpc.Displaytext) } - if vpc.Cidr != CLOUDSTACK_VPC_CIDR { - return fmt.Errorf("Bad VPC offering: %s", vpc.Cidr) + if vpc.Cidr != CLOUDSTACK_VPC_CIDR_1 { + return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr) } return nil @@ -113,6 +113,6 @@ resource "cloudstack_vpc" "foo" { vpc_offering = "%s" zone = "%s" }`, - CLOUDSTACK_VPC_CIDR, + CLOUDSTACK_VPC_CIDR_1, CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_connection.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_connection.go new file mode 100644 index 000000000..b036890a5 --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_connection.go @@ -0,0 +1,95 @@ +package cloudstack + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func resourceCloudStackVPNConnection() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackVPNConnectionCreate, + Read: resourceCloudStackVPNConnectionRead, + Delete: resourceCloudStackVPNConnectionDelete, + + Schema: map[string]*schema.Schema{ + "customergatewayid": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "vpngatewayid": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceCloudStackVPNConnectionCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewCreateVpnConnectionParams( + d.Get("customergatewayid").(string), + d.Get("vpngatewayid").(string), + ) + + // Create the new VPN Connection + v, err := cs.VPN.CreateVpnConnection(p) + if err != nil { + return fmt.Errorf("Error creating VPN Connection: %s", err) + } + + d.SetId(v.Id) + + return resourceCloudStackVPNConnectionRead(d, meta) +} + +func resourceCloudStackVPNConnectionRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Get the VPN Connection details + v, count, err := cs.VPN.GetVpnConnectionByID(d.Id()) + if err != nil { + if count == 0 { + log.Printf("[DEBUG] VPN Connection does no longer exist") + d.SetId("") + return nil + } + + return err + } + + d.Set("customergatewayid", v.S2scustomergatewayid) + d.Set("vpngatewayid", v.S2svpngatewayid) + + return nil +} + +func resourceCloudStackVPNConnectionDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewDeleteVpnConnectionParams(d.Id()) + + // Delete the VPN Connection + _, err := cs.VPN.DeleteVpnConnection(p) + if err != nil { + // This is a very poor way to be told the UUID does no longer exist :( + if strings.Contains(err.Error(), fmt.Sprintf( + "Invalid parameter id value=%s due to incorrect long value format, "+ + "or entity does not exist", d.Id())) { + return nil + } + + return fmt.Errorf("Error deleting VPN Connection: %s", err) + } + + return nil +} diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_connection_test.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_connection_test.go new file mode 100644 index 000000000..1b9d9920a --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_connection_test.go @@ -0,0 +1,142 @@ +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func TestAccCloudStackVPNConnection_basic(t *testing.T) { + var vpnConnection cloudstack.VpnConnection + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackVPNConnectionDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudStackVPNConnection_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackVPNConnectionExists( + "cloudstack_vpn_connection.foo-bar", &vpnConnection), + testAccCheckCloudStackVPNConnectionExists( + "cloudstack_vpn_connection.bar-foo", &vpnConnection), + ), + }, + }, + }) +} + +func testAccCheckCloudStackVPNConnectionExists( + n string, vpnConnection *cloudstack.VpnConnection) 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 VPN Connection ID is set") + } + + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + v, _, err := cs.VPN.GetVpnConnectionByID(rs.Primary.ID) + + if err != nil { + return err + } + + if v.Id != rs.Primary.ID { + return fmt.Errorf("VPN Connection not found") + } + + *vpnConnection = *v + + return nil + } +} + +func testAccCheckCloudStackVPNConnectionDestroy(s *terraform.State) error { + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudstack_vpn_connection" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No VPN Connection ID is set") + } + + p := cs.VPN.NewDeleteVpnConnectionParams(rs.Primary.ID) + _, err := cs.VPN.DeleteVpnConnection(p) + + if err != nil { + return fmt.Errorf( + "Error deleting VPN Connection (%s): %s", + rs.Primary.ID, err) + } + } + + return nil +} + +var testAccCloudStackVPNConnection_basic = fmt.Sprintf(` +resource "cloudstack_vpc" "foo" { + name = "terraform-vpc-foo" + cidr = "%s" + vpc_offering = "%s" + zone = "%s" +} + +resource "cloudstack_vpc" "bar" { + name = "terraform-vpc-bar" + cidr = "%s" + vpc_offering = "%s" + zone = "%s" +} + +resource "cloudstack_vpn_gateway" "foo" { + vpc = "${cloudstack_vpc.foo.name}" +} + +resource "cloudstack_vpn_gateway" "bar" { + vpc = "${cloudstack_vpc.bar.name}" +} + +resource "cloudstack_vpn_customer_gateway" "foo" { + name = "terraform-foo" + cidr = "${cloudstack_vpc.foo.cidr}" + esp_policy = "aes256-sha1" + gateway = "${cloudstack_vpn_gateway.foo.public_ip}" + ike_policy = "aes256-sha1" + ipsec_psk = "terraform" +} + +resource "cloudstack_vpn_customer_gateway" "bar" { + name = "terraform-bar" + cidr = "${cloudstack_vpc.bar.cidr}" + esp_policy = "aes256-sha1" + gateway = "${cloudstack_vpn_gateway.bar.public_ip}" + ike_policy = "aes256-sha1" + ipsec_psk = "terraform" +} + +resource "cloudstack_vpn_connection" "foo-bar" { + customergatewayid = "${cloudstack_vpn_customer_gateway.foo.id}" + vpngatewayid = "${cloudstack_vpn_gateway.bar.id}" +} + +resource "cloudstack_vpn_connection" "bar-foo" { + customergatewayid = "${cloudstack_vpn_customer_gateway.bar.id}" + vpngatewayid = "${cloudstack_vpn_gateway.foo.id}" +}`, + CLOUDSTACK_VPC_CIDR_1, + CLOUDSTACK_VPC_OFFERING, + CLOUDSTACK_ZONE, + CLOUDSTACK_VPC_CIDR_2, + CLOUDSTACK_VPC_OFFERING, + CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway.go new file mode 100644 index 000000000..90a03aeb0 --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway.go @@ -0,0 +1,193 @@ +package cloudstack + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func resourceCloudStackVPNCustomerGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackVPNCustomerGatewayCreate, + Read: resourceCloudStackVPNCustomerGatewayRead, + Update: resourceCloudStackVPNCustomerGatewayUpdate, + Delete: resourceCloudStackVPNCustomerGatewayDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "cidr": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "esp_policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "gateway": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "ike_policy": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "ipsec_psk": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "dpd": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + + "esp_lifetime": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "ike_lifetime": &schema.Schema{ + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceCloudStackVPNCustomerGatewayCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewCreateVpnCustomerGatewayParams( + d.Get("cidr").(string), + d.Get("esp_policy").(string), + d.Get("gateway").(string), + d.Get("ike_policy").(string), + d.Get("ipsec_psk").(string), + ) + + p.SetName(d.Get("name").(string)) + + if dpd, ok := d.GetOk("dpd"); ok { + p.SetDpd(dpd.(bool)) + } + + if esplifetime, ok := d.GetOk("esp_lifetime"); ok { + p.SetEsplifetime(esplifetime.(int)) + } + + if ikelifetime, ok := d.GetOk("ike_lifetime"); ok { + p.SetIkelifetime(ikelifetime.(int)) + } + + // Create the new VPN Customer Gateway + v, err := cs.VPN.CreateVpnCustomerGateway(p) + if err != nil { + return fmt.Errorf("Error creating VPN Customer Gateway %s: %s", d.Get("name").(string), err) + } + + d.SetId(v.Id) + + return resourceCloudStackVPNCustomerGatewayRead(d, meta) +} + +func resourceCloudStackVPNCustomerGatewayRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Get the VPN Customer Gateway details + v, count, err := cs.VPN.GetVpnCustomerGatewayByID(d.Id()) + if err != nil { + if count == 0 { + log.Printf( + "[DEBUG] VPN Customer Gateway %s does no longer exist", d.Get("name").(string)) + d.SetId("") + return nil + } + + return err + } + + d.Set("name", v.Name) + d.Set("cidr", v.Cidrlist) + d.Set("esp_policy", v.Esppolicy) + d.Set("gateway", v.Gateway) + d.Set("ike_policy", v.Ikepolicy) + d.Set("ipsec_psk", v.Ipsecpsk) + d.Set("dpd", v.Dpd) + d.Set("esp_lifetime", v.Esplifetime) + d.Set("ike_lifetime", v.Ikelifetime) + + return nil +} + +func resourceCloudStackVPNCustomerGatewayUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewUpdateVpnCustomerGatewayParams( + d.Get("cidr").(string), + d.Get("esp_policy").(string), + d.Get("gateway").(string), + d.Id(), + d.Get("ike_policy").(string), + d.Get("ipsec_psk").(string), + ) + + p.SetName(d.Get("name").(string)) + + if dpd, ok := d.GetOk("dpd"); ok { + p.SetDpd(dpd.(bool)) + } + + if esplifetime, ok := d.GetOk("esp_lifetime"); ok { + p.SetEsplifetime(esplifetime.(int)) + } + + if ikelifetime, ok := d.GetOk("ike_lifetime"); ok { + p.SetIkelifetime(ikelifetime.(int)) + } + + // Update the VPN Customer Gateway + _, err := cs.VPN.UpdateVpnCustomerGateway(p) + if err != nil { + return fmt.Errorf("Error updating VPN Customer Gateway %s: %s", d.Get("name").(string), err) + } + + return resourceCloudStackVPNCustomerGatewayRead(d, meta) +} + +func resourceCloudStackVPNCustomerGatewayDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewDeleteVpnCustomerGatewayParams(d.Id()) + + // Delete the VPN Customer Gateway + _, err := cs.VPN.DeleteVpnCustomerGateway(p) + if err != nil { + // This is a very poor way to be told the UUID does no longer exist :( + if strings.Contains(err.Error(), fmt.Sprintf( + "Invalid parameter id value=%s due to incorrect long value format, "+ + "or entity does not exist", d.Id())) { + return nil + } + + return fmt.Errorf("Error deleting VPN Customer Gateway %s: %s", d.Get("name").(string), err) + } + + return nil +} diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway_test.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway_test.go new file mode 100644 index 000000000..b468c76fe --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_customer_gateway_test.go @@ -0,0 +1,223 @@ +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func TestAccCloudStackVPNCustomerGateway_basic(t *testing.T) { + var vpnCustomerGateway cloudstack.VpnCustomerGateway + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackVPNCustomerGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudStackVPNCustomerGateway_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackVPNCustomerGatewayExists( + "cloudstack_vpn_customer_gateway.foo", &vpnCustomerGateway), + testAccCheckCloudStackVPNCustomerGatewayAttributes(&vpnCustomerGateway), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "name", "terraform-foo"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "name", "terraform-bar"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "ike_policy", "aes256-sha1"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "esp_policy", "aes256-sha1"), + ), + }, + }, + }) +} + +func TestAccCloudStackVPNCustomerGateway_update(t *testing.T) { + var vpnCustomerGateway cloudstack.VpnCustomerGateway + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackVPNCustomerGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudStackVPNCustomerGateway_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackVPNCustomerGatewayExists( + "cloudstack_vpn_customer_gateway.foo", &vpnCustomerGateway), + testAccCheckCloudStackVPNCustomerGatewayAttributes(&vpnCustomerGateway), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "name", "terraform-foo"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "name", "terraform-bar"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "ike_policy", "aes256-sha1"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "esp_policy", "aes256-sha1"), + ), + }, + + resource.TestStep{ + Config: testAccCloudStackVPNCustomerGateway_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackVPNCustomerGatewayExists( + "cloudstack_vpn_customer_gateway.foo", &vpnCustomerGateway), + testAccCheckCloudStackVPNCustomerGatewayAttributes(&vpnCustomerGateway), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "name", "terraform-foo-bar"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "name", "terraform-bar-foo"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.foo", "ike_policy", "3des-md5"), + resource.TestCheckResourceAttr( + "cloudstack_vpn_customer_gateway.bar", "esp_policy", "3des-md5"), + ), + }, + }, + }) +} + +func testAccCheckCloudStackVPNCustomerGatewayExists( + n string, vpnCustomerGateway *cloudstack.VpnCustomerGateway) 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 VPN CustomerGateway ID is set") + } + + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + v, _, err := cs.VPN.GetVpnCustomerGatewayByID(rs.Primary.ID) + + if err != nil { + return err + } + + if v.Id != rs.Primary.ID { + return fmt.Errorf("VPN CustomerGateway not found") + } + + *vpnCustomerGateway = *v + + return nil + } +} + +func testAccCheckCloudStackVPNCustomerGatewayAttributes( + vpnCustomerGateway *cloudstack.VpnCustomerGateway) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if vpnCustomerGateway.Esppolicy != "aes256-sha1" { + return fmt.Errorf("Bad ESP policy: %s", vpnCustomerGateway.Esppolicy) + } + + if vpnCustomerGateway.Ikepolicy != "aes256-sha1" { + return fmt.Errorf("Bad IKE policy: %s", vpnCustomerGateway.Ikepolicy) + } + + if vpnCustomerGateway.Ipsecpsk != "terraform" { + return fmt.Errorf("Bad IPSEC pre-shared key: %s", vpnCustomerGateway.Ipsecpsk) + } + + return nil + } +} + +func testAccCheckCloudStackVPNCustomerGatewayDestroy(s *terraform.State) error { + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudstack_vpn_customer_gateway" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No VPN Customer Gateway ID is set") + } + + p := cs.VPN.NewDeleteVpnCustomerGatewayParams(rs.Primary.ID) + _, err := cs.VPN.DeleteVpnCustomerGateway(p) + + if err != nil { + return fmt.Errorf( + "Error deleting VPN Customer Gateway (%s): %s", + rs.Primary.ID, err) + } + } + + return nil +} + +var testAccCloudStackVPNCustomerGateway_basic = fmt.Sprintf(` +resource "cloudstack_vpc" "foo" { + name = "terraform-vpc-foo" + cidr = "%s" + vpc_offering = "%s" + zone = "%s" +} + +resource "cloudstack_vpc" "bar" { + name = "terraform-vpc-bar" + cidr = "%s" + vpc_offering = "%s" + zone = "%s" +} + +resource "cloudstack_vpn_gateway" "foo" { + vpc = "${cloudstack_vpc.foo.name}" +} + +resource "cloudstack_vpn_gateway" "bar" { + vpc = "${cloudstack_vpc.bar.name}" +} + +resource "cloudstack_vpn_customer_gateway" "foo" { + name = "terraform-foo" + cidr = "${cloudstack_vpc.foo.cidr}" + esp_policy = "aes256-sha1" + gateway = "${cloudstack_vpn_gateway.foo.public_ip}" + ike_policy = "aes256-sha1" + ipsec_psk = "terraform" +} + +resource "cloudstack_vpn_customer_gateway" "bar" { + name = "terraform-bar" + cidr = "${cloudstack_vpc.bar.cidr}" + esp_policy = "aes256-sha1" + gateway = "${cloudstack_vpn_gateway.bar.public_ip}" + ike_policy = "aes256-sha1" + ipsec_psk = "terraform" +}`, + CLOUDSTACK_VPC_CIDR_1, + CLOUDSTACK_VPC_OFFERING, + CLOUDSTACK_ZONE, + CLOUDSTACK_VPC_CIDR_2, + CLOUDSTACK_VPC_OFFERING, + CLOUDSTACK_ZONE) + +var testAccCloudStackVPNCustomerGateway_update = fmt.Sprintf(` +resource "cloudstack_vpn_customer_gateway" "foo" { + name = "terraform-foo-bar" + cidr = "${cloudstack_vpc.foo.cidr}" + esp_policy = "3des-md5" + gateway = "${cloudstack_vpn_gateway.foo.public_ip}" + ike_policy = "3des-md5" + ipsec_psk = "terraform" +} + +resource "cloudstack_vpn_customer_gateway" "bar" { + name = "terraform-bar-foo" + cidr = "${cloudstack_vpc.bar.cidr}" + esp_policy = "3des-md5" + gateway = "${cloudstack_vpn_gateway.bar.public_ip}" + ike_policy = "3des-md5" + ipsec_psk = "terraform" +}`) diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway.go new file mode 100644 index 000000000..063c31777 --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway.go @@ -0,0 +1,97 @@ +package cloudstack + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func resourceCloudStackVPNGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudStackVPNGatewayCreate, + Read: resourceCloudStackVPNGatewayRead, + Delete: resourceCloudStackVPNGatewayDelete, + + Schema: map[string]*schema.Schema{ + "vpc": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "public_ip": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceCloudStackVPNGatewayCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Retrieve the VPC UUID + vpcid, e := retrieveUUID(cs, "vpc", d.Get("vpc").(string)) + if e != nil { + return e.Error() + } + + // Create a new parameter struct + p := cs.VPN.NewCreateVpnGatewayParams(vpcid) + + // Create the new VPN Gateway + v, err := cs.VPN.CreateVpnGateway(p) + if err != nil { + return fmt.Errorf("Error creating VPN Gateway for VPC %s: %s", d.Get("vpc").(string), err) + } + + d.SetId(v.Id) + + return resourceCloudStackVPNGatewayRead(d, meta) +} + +func resourceCloudStackVPNGatewayRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Get the VPN Gateway details + v, count, err := cs.VPN.GetVpnGatewayByID(d.Id()) + if err != nil { + if count == 0 { + log.Printf( + "[DEBUG] VPN Gateway for VPC %s does no longer exist", d.Get("vpc").(string)) + d.SetId("") + return nil + } + + return err + } + + d.Set("public_ip", v.Publicip) + + return nil +} + +func resourceCloudStackVPNGatewayDelete(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Create a new parameter struct + p := cs.VPN.NewDeleteVpnGatewayParams(d.Id()) + + // Delete the VPN Gateway + _, err := cs.VPN.DeleteVpnGateway(p) + if err != nil { + // This is a very poor way to be told the UUID does no longer exist :( + if strings.Contains(err.Error(), fmt.Sprintf( + "Invalid parameter id value=%s due to incorrect long value format, "+ + "or entity does not exist", d.Id())) { + return nil + } + + return fmt.Errorf("Error deleting VPN Gateway for VPC %s: %s", d.Get("vpc").(string), err) + } + + return nil +} diff --git a/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway_test.go b/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway_test.go new file mode 100644 index 000000000..db6c0085a --- /dev/null +++ b/builtin/providers/cloudstack/resource_cloudstack_vpn_gateway_test.go @@ -0,0 +1,101 @@ +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/xanzy/go-cloudstack/cloudstack" +) + +func TestAccCloudStackVPNGateway_basic(t *testing.T) { + var vpnGateway cloudstack.VpnGateway + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackVPNGatewayDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCloudStackVPNGateway_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackVPNGatewayExists( + "cloudstack_vpn_gateway.foo", &vpnGateway), + resource.TestCheckResourceAttr( + "cloudstack_vpn_gateway.foo", "vpc", "terraform-vpc"), + ), + }, + }, + }) +} + +func testAccCheckCloudStackVPNGatewayExists( + n string, vpnGateway *cloudstack.VpnGateway) 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 VPN Gateway ID is set") + } + + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + v, _, err := cs.VPN.GetVpnGatewayByID(rs.Primary.ID) + + if err != nil { + return err + } + + if v.Id != rs.Primary.ID { + return fmt.Errorf("VPN Gateway not found") + } + + *vpnGateway = *v + + return nil + } +} + +func testAccCheckCloudStackVPNGatewayDestroy(s *terraform.State) error { + cs := testAccProvider.Meta().(*cloudstack.CloudStackClient) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudstack_vpn_gateway" { + continue + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No VPN Gateway ID is set") + } + + p := cs.VPN.NewDeleteVpnGatewayParams(rs.Primary.ID) + _, err := cs.VPN.DeleteVpnGateway(p) + + if err != nil { + return fmt.Errorf( + "Error deleting VPN Gateway (%s): %s", + rs.Primary.ID, err) + } + } + + return nil +} + +var testAccCloudStackVPNGateway_basic = fmt.Sprintf(` +resource "cloudstack_vpc" "foo" { + name = "terraform-vpc" + display_text = "terraform-vpc-text" + cidr = "%s" + vpc_offering = "%s" + zone = "%s" +} + +resource "cloudstack_vpn_gateway" "foo" { + vpc = "${cloudstack_vpc.foo.name}" +}`, + CLOUDSTACK_VPC_CIDR_1, + CLOUDSTACK_VPC_OFFERING, + CLOUDSTACK_ZONE) diff --git a/builtin/providers/cloudstack/resources.go b/builtin/providers/cloudstack/resources.go index acef7b3da..76d38eb7c 100644 --- a/builtin/providers/cloudstack/resources.go +++ b/builtin/providers/cloudstack/resources.go @@ -40,8 +40,6 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str uuid, err = cs.VPC.GetVPCOfferingID(value) case "vpc": uuid, err = cs.VPC.GetVPCID(value) - case "template": - uuid, err = cs.Template.GetTemplateID(value, "executable") case "network": uuid, err = cs.Network.GetNetworkID(value) case "zone": @@ -71,6 +69,22 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str return uuid, nil } +func retrieveTemplateUUID(cs *cloudstack.CloudStackClient, zoneid, value string) (uuid string, e *retrieveError) { + // If the supplied value isn't a UUID, try to retrieve the UUID ourselves + if isUUID(value) { + return value, nil + } + + log.Printf("[DEBUG] Retrieving UUID of template: %s", value) + + uuid, err := cs.Template.GetTemplateID(value, "executable", zoneid) + if err != nil { + return uuid, &retrieveError{name: "template", value: value, err: err} + } + + return uuid, nil +} + func isUUID(s string) bool { re := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`) return re.MatchString(s) diff --git a/builtin/providers/digitalocean/provider.go b/builtin/providers/digitalocean/provider.go index ecc7d67b8..080716e2e 100644 --- a/builtin/providers/digitalocean/provider.go +++ b/builtin/providers/digitalocean/provider.go @@ -21,6 +21,7 @@ func Provider() terraform.ResourceProvider { "digitalocean_domain": resourceDigitalOceanDomain(), "digitalocean_droplet": resourceDigitalOceanDroplet(), "digitalocean_record": resourceDigitalOceanRecord(), + "digitalocean_ssh_key": resourceDigitalOceanSSHKey(), }, ConfigureFunc: providerConfigure, diff --git a/builtin/providers/digitalocean/resource_digitalocean_ssh_key.go b/builtin/providers/digitalocean/resource_digitalocean_ssh_key.go new file mode 100644 index 000000000..c509d4725 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_ssh_key.go @@ -0,0 +1,114 @@ +package digitalocean + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/pearkes/digitalocean" +) + +func resourceDigitalOceanSSHKey() *schema.Resource { + return &schema.Resource{ + Create: resourceDigitalOceanSSHKeyCreate, + Read: resourceDigitalOceanSSHKeyRead, + Update: resourceDigitalOceanSSHKeyUpdate, + Delete: resourceDigitalOceanSSHKeyDelete, + + Schema: map[string]*schema.Schema{ + "id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "public_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "fingerprint": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceDigitalOceanSSHKeyCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*digitalocean.Client) + + // Build up our creation options + opts := &digitalocean.CreateSSHKey{ + Name: d.Get("name").(string), + PublicKey: d.Get("public_key").(string), + } + + log.Printf("[DEBUG] SSH Key create configuration: %#v", opts) + id, err := client.CreateSSHKey(opts) + if err != nil { + return fmt.Errorf("Error creating SSH Key: %s", err) + } + + d.SetId(id) + log.Printf("[INFO] SSH Key: %s", id) + + return resourceDigitalOceanSSHKeyRead(d, meta) +} + +func resourceDigitalOceanSSHKeyRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*digitalocean.Client) + + key, err := client.RetrieveSSHKey(d.Id()) + if err != nil { + // If the key is somehow already destroyed, mark as + // succesfully gone + if strings.Contains(err.Error(), "404 Not Found") { + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving SSH key: %s", err) + } + + d.Set("name", key.Name) + d.Set("fingerprint", key.Fingerprint) + + return nil +} + +func resourceDigitalOceanSSHKeyUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*digitalocean.Client) + + var newName string + if v, ok := d.GetOk("name"); ok { + newName = v.(string) + } + + log.Printf("[DEBUG] SSH key update name: %#v", newName) + err := client.RenameSSHKey(d.Id(), newName) + if err != nil { + return fmt.Errorf("Failed to update SSH key: %s", err) + } + + return resourceDigitalOceanSSHKeyRead(d, meta) +} + +func resourceDigitalOceanSSHKeyDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*digitalocean.Client) + + log.Printf("[INFO] Deleting SSH key: %s", d.Id()) + err := client.DestroySSHKey(d.Id()) + if err != nil { + return fmt.Errorf("Error deleting SSH key: %s", err) + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go b/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go new file mode 100644 index 000000000..d5c50e6f8 --- /dev/null +++ b/builtin/providers/digitalocean/resource_digitalocean_ssh_key_test.go @@ -0,0 +1,99 @@ +package digitalocean + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/pearkes/digitalocean" +) + +func TestAccDigitalOceanSSHKey_Basic(t *testing.T) { + var key digitalocean.SSHKey + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDigitalOceanSSHKeyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckDigitalOceanSSHKeyConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckDigitalOceanSSHKeyExists("digitalocean_ssh_key.foobar", &key), + testAccCheckDigitalOceanSSHKeyAttributes(&key), + resource.TestCheckResourceAttr( + "digitalocean_ssh_key.foobar", "name", "foobar"), + resource.TestCheckResourceAttr( + "digitalocean_ssh_key.foobar", "public_key", "abcdef"), + ), + }, + }, + }) +} + +func testAccCheckDigitalOceanSSHKeyDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*digitalocean.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "digitalocean_ssh_key" { + continue + } + + // Try to find the key + _, err := client.RetrieveSSHKey(rs.Primary.ID) + + if err == nil { + fmt.Errorf("SSH key still exists") + } + } + + return nil +} + +func testAccCheckDigitalOceanSSHKeyAttributes(key *digitalocean.SSHKey) resource.TestCheckFunc { + return func(s *terraform.State) error { + + if key.Name != "foobar" { + return fmt.Errorf("Bad name: %s", key.Name) + } + + return nil + } +} + +func testAccCheckDigitalOceanSSHKeyExists(n string, key *digitalocean.SSHKey) 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 Record ID is set") + } + + client := testAccProvider.Meta().(*digitalocean.Client) + + foundKey, err := client.RetrieveSSHKey(rs.Primary.ID) + + if err != nil { + return err + } + + if foundKey.Name != rs.Primary.ID { + return fmt.Errorf("Record not found") + } + + *key = foundKey + + return nil + } +} + +const testAccCheckDigitalOceanSSHKeyConfig_basic = ` +resource "digitalocean_ssh_key" "foobar" { + name = "foobar" + public_key = "abcdef" +}` diff --git a/helper/schema/resource.go b/helper/schema/resource.go index f0e0515cd..a19912eed 100644 --- a/helper/schema/resource.go +++ b/helper/schema/resource.go @@ -3,6 +3,7 @@ package schema import ( "errors" "fmt" + "strconv" "github.com/hashicorp/terraform/terraform" ) @@ -24,6 +25,31 @@ type Resource struct { // resource. Schema map[string]*Schema + // SchemaVersion is the version number for this resource's Schema + // definition. The current SchemaVersion stored in the state for each + // resource. Provider authors can increment this version number + // when Schema semantics change. If the State's SchemaVersion is less than + // the current SchemaVersion, the InstanceState is yielded to the + // MigrateState callback, where the provider can make whatever changes it + // needs to update the state to be compatible to the latest version of the + // Schema. + // + // When unset, SchemaVersion defaults to 0, so provider authors can start + // their Versioning at any integer >= 1 + SchemaVersion int + + // MigrateState is responsible for updating an InstanceState with an old + // version to the format expected by the current version of the Schema. + // + // It is called during Refresh if the State's stored SchemaVersion is less + // than the current SchemaVersion of the Resource. + // + // The function is yielded the state's stored SchemaVersion and a pointer to + // the InstanceState that needs updating, as well as the configured + // provider's configured meta interface{}, in case the migration process + // needs to make any remote API calls. + MigrateState StateMigrateFunc + // The functions below are the CRUD operations for this resource. // // The only optional operation is Update. If Update is not implemented, @@ -69,6 +95,10 @@ type DeleteFunc func(*ResourceData, interface{}) error // See Resource documentation. type ExistsFunc func(*ResourceData, interface{}) (bool, error) +// See Resource documentation. +type StateMigrateFunc func( + int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) + // Apply creates, updates, and/or deletes a resource. func (r *Resource) Apply( s *terraform.InstanceState, @@ -158,6 +188,14 @@ func (r *Resource) Refresh( } } + needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) + if needsMigration && r.MigrateState != nil { + s, err := r.MigrateState(stateSchemaVersion, s, meta) + if err != nil { + return s, err + } + } + data, err := schemaMap(r.Schema).Data(s, nil) if err != nil { return s, err @@ -169,6 +207,13 @@ func (r *Resource) Refresh( state = nil } + if state != nil && r.SchemaVersion > 0 { + if state.Meta == nil { + state.Meta = make(map[string]string) + } + state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion) + } + return state, err } @@ -189,3 +234,10 @@ func (r *Resource) InternalValidate() error { return schemaMap(r.Schema).InternalValidate() } + +// Determines if a given InstanceState needs to be migrated by checking the +// stored version number with the current SchemaVersion +func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) { + stateSchemaVersion, _ := strconv.Atoi(is.Meta["schema_version"]) + return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion +} diff --git a/helper/schema/resource_test.go b/helper/schema/resource_test.go index 0c71abddf..b1c42721f 100644 --- a/helper/schema/resource_test.go +++ b/helper/schema/resource_test.go @@ -3,6 +3,7 @@ package schema import ( "fmt" "reflect" + "strconv" "testing" "github.com/hashicorp/terraform/terraform" @@ -478,3 +479,218 @@ func TestResourceRefresh_noExists(t *testing.T) { t.Fatalf("should have no state") } } + +func TestResourceRefresh_needsMigration(t *testing.T) { + // Schema v2 it deals only in newfoo, which tracks foo as an int + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + // Real state migration functions will probably switch on this value, + // but we'll just assert on it for now. + if v != 1 { + t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v) + } + + if meta != 42 { + t.Fatal("Expected meta to be passed through to the migration function") + } + + oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64) + if err != nil { + t.Fatalf("err: %#v", err) + } + s.Attributes["newfoo"] = strconv.Itoa((int(oldfoo * 10))) + delete(s.Attributes, "oldfoo") + + return s, nil + } + + // State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th + // the scale of newfoo + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "1.2", + }, + Meta: map[string]string{ + "schema_version": "1", + }, + } + + actual, err := r.Refresh(s, 42) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]string{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_noMigrationNeeded(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + t.Fatal("Migrate function shouldn't be called!") + return nil, nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "newfoo": "12", + }, + Meta: map[string]string{ + "schema_version": "2", + }, + } + + actual, err := r.Refresh(s, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]string{ + "schema_version": "2", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) { + r := &Resource{ + // Version 1 > Version 0 + SchemaVersion: 1, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + return d.Set("newfoo", d.Get("newfoo").(int)+1) + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + s.Attributes["newfoo"] = s.Attributes["oldfoo"] + return s, nil + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "12", + }, + } + + actual, err := r.Refresh(s, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + expected := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "id": "bar", + "newfoo": "13", + }, + Meta: map[string]string{ + "schema_version": "1", + }, + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual) + } +} + +func TestResourceRefresh_migrateStateErr(t *testing.T) { + r := &Resource{ + SchemaVersion: 2, + Schema: map[string]*Schema{ + "newfoo": &Schema{ + Type: TypeInt, + Optional: true, + }, + }, + } + + r.Read = func(d *ResourceData, m interface{}) error { + t.Fatal("Read should never be called!") + return nil + } + + r.MigrateState = func( + v int, + s *terraform.InstanceState, + meta interface{}) (*terraform.InstanceState, error) { + return s, fmt.Errorf("triggering an error") + } + + s := &terraform.InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "oldfoo": "12", + }, + } + + _, err := r.Refresh(s, nil) + if err == nil { + t.Fatal("expected error, but got none!") + } +} diff --git a/helper/ssh/communicator.go b/helper/ssh/communicator.go index 186fd4824..817f37368 100644 --- a/helper/ssh/communicator.go +++ b/helper/ssh/communicator.go @@ -14,7 +14,7 @@ import ( "sync" "time" - "code.google.com/p/go.crypto/ssh" + "golang.org/x/crypto/ssh" ) // RemoteCmd represents a remote command being prepared or run. diff --git a/helper/ssh/communicator_test.go b/helper/ssh/communicator_test.go index 2e16e1482..0e80c9415 100644 --- a/helper/ssh/communicator_test.go +++ b/helper/ssh/communicator_test.go @@ -4,7 +4,7 @@ package ssh import ( "bytes" - "code.google.com/p/go.crypto/ssh" + "golang.org/x/crypto/ssh" "fmt" "net" "testing" diff --git a/helper/ssh/password.go b/helper/ssh/password.go index 934bcd01f..8db6f82da 100644 --- a/helper/ssh/password.go +++ b/helper/ssh/password.go @@ -1,7 +1,7 @@ package ssh import ( - "code.google.com/p/go.crypto/ssh" + "golang.org/x/crypto/ssh" "log" ) diff --git a/helper/ssh/password_test.go b/helper/ssh/password_test.go index e74b46e06..6e3e0a257 100644 --- a/helper/ssh/password_test.go +++ b/helper/ssh/password_test.go @@ -1,7 +1,7 @@ package ssh import ( - "code.google.com/p/go.crypto/ssh" + "golang.org/x/crypto/ssh" "reflect" "testing" ) diff --git a/helper/ssh/provisioner.go b/helper/ssh/provisioner.go index baebbd9b6..2d60d8934 100644 --- a/helper/ssh/provisioner.go +++ b/helper/ssh/provisioner.go @@ -7,7 +7,7 @@ import ( "log" "time" - "code.google.com/p/go.crypto/ssh" + "golang.org/x/crypto/ssh" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" "github.com/mitchellh/mapstructure" diff --git a/scripts/build.sh b/scripts/build.sh index bf6074068..4433d8f9b 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -19,9 +19,11 @@ GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true) XC_ARCH=${XC_ARCH:-"386 amd64 arm"} XC_OS=${XC_OS:-linux darwin windows freebsd openbsd} -# Install dependencies -echo "==> Getting dependencies..." -go get ./... +# Install dependencies unless running in quick mode +if [ "${TF_QUICKDEV}x" == "x" ]; then + echo "==> Getting dependencies..." + go get ./... +fi # Delete the old dir echo "==> Removing old directory..." diff --git a/terraform/state.go b/terraform/state.go index ddf57cd7d..3dbad7ae6 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -832,6 +832,11 @@ type InstanceState struct { // that is necessary for the Terraform run to complete, but is not // persisted to a state file. Ephemeral EphemeralState `json:"-"` + + // Meta is a simple K/V map that is persisted to the State but otherwise + // ignored by Terraform core. It's meant to be used for accounting by + // external client code. + Meta map[string]string `json:"meta,omitempty"` } func (i *InstanceState) init() { diff --git a/website/Gemfile b/website/Gemfile index 936305b5e..850fc4b6c 100644 --- a/website/Gemfile +++ b/website/Gemfile @@ -1,3 +1,3 @@ source 'https://rubygems.org' -gem 'middleman-hashicorp', github: 'hashicorp/middleman-hashicorp' +gem 'middleman-hashicorp', git: 'https://github.com/hashicorp/middleman-hashicorp' diff --git a/website/Gemfile.lock b/website/Gemfile.lock index e956a6aa6..a55579e6d 100644 --- a/website/Gemfile.lock +++ b/website/Gemfile.lock @@ -1,19 +1,19 @@ GIT - remote: git://github.com/hashicorp/middleman-hashicorp.git - revision: 30c15f93fb501041cff97c490b60ddc96c8314c9 + remote: https://github.com/hashicorp/middleman-hashicorp + revision: 783fe9517dd02badb85e5ddfeda4d8e35bbd05a8 specs: middleman-hashicorp (0.1.0) - bootstrap-sass (~> 3.2) + bootstrap-sass (~> 3.3) builder (~> 3.2) less (~> 2.6) middleman (~> 3.3) - middleman-livereload (~> 3.3) + middleman-livereload (~> 3.4) middleman-minify-html (~> 3.4) middleman-syntax (~> 2.0) - rack-contrib (~> 1.1) + rack-contrib (~> 1.2) rack-rewrite (~> 1.5) rack-ssl-enforcer (~> 0.2) - redcarpet (~> 3.1) + redcarpet (~> 3.2) therubyracer (~> 0.12) thin (~> 1.6) @@ -26,7 +26,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.1) tzinfo (~> 1.1) - autoprefixer-rails (5.0.0.2) + autoprefixer-rails (5.1.7) execjs json bootstrap-sass (3.3.3) @@ -35,11 +35,11 @@ GEM builder (3.2.2) celluloid (0.16.0) timers (~> 4.0.0) - chunky_png (1.3.3) + chunky_png (1.3.4) coffee-script (2.3.0) coffee-script-source execjs - coffee-script-source (1.8.0) + coffee-script-source (1.9.1) commonjs (0.2.7) compass (1.0.3) chunky_png (~> 1.2) @@ -58,8 +58,8 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) erubis (2.7.0) - eventmachine (1.0.4) - execjs (2.2.2) + eventmachine (1.0.7) + execjs (2.4.0) ffi (1.9.6) haml (4.0.6) tilt @@ -69,9 +69,9 @@ GEM uber (~> 0.0.4) htmlcompressor (0.1.2) http_parser.rb (0.6.0) - i18n (0.6.11) + i18n (0.7.0) json (1.8.2) - kramdown (1.5.0) + kramdown (1.6.0) less (2.6.0) commonjs (~> 0.2.7) libv8 (3.16.14.7) @@ -79,23 +79,23 @@ GEM celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) - middleman (3.3.7) + middleman (3.3.10) coffee-script (~> 2.2) compass (>= 1.0.0, < 2.0.0) compass-import-once (= 1.0.5) execjs (~> 2.0) haml (>= 4.0.5) kramdown (~> 1.2) - middleman-core (= 3.3.7) + middleman-core (= 3.3.10) middleman-sprockets (>= 3.1.2) sass (>= 3.4.0, < 4.0) uglifier (~> 2.5) - middleman-core (3.3.7) + middleman-core (3.3.10) activesupport (~> 4.1.0) bundler (~> 1.1) erubis hooks (~> 0.3) - i18n (~> 0.6.9) + i18n (~> 0.7.0) listen (>= 2.7.9, < 3.0) padrino-helpers (~> 0.12.3) rack (>= 1.4.5, < 2.0) @@ -109,7 +109,7 @@ GEM middleman-minify-html (3.4.0) htmlcompressor (~> 0.1.0) middleman-core (>= 3.2) - middleman-sprockets (3.4.1) + middleman-sprockets (3.4.2) middleman-core (>= 3.3) sprockets (~> 2.12.1) sprockets-helpers (~> 1.1.0) @@ -118,12 +118,12 @@ GEM middleman-core (~> 3.2) rouge (~> 1.0) minitest (5.5.1) - multi_json (1.10.1) - padrino-helpers (0.12.4) + multi_json (1.11.0) + padrino-helpers (0.12.5) i18n (~> 0.6, >= 0.6.7) - padrino-support (= 0.12.4) + padrino-support (= 0.12.5) tilt (~> 1.4.1) - padrino-support (0.12.4) + padrino-support (0.12.5) activesupport (>= 3.1) rack (1.6.0) rack-contrib (1.2.0) @@ -139,8 +139,8 @@ GEM ffi (>= 0.5.0) redcarpet (3.2.2) ref (1.0.5) - rouge (1.7.7) - sass (3.4.10) + rouge (1.8.0) + sass (3.4.13) sprockets (2.12.3) hike (~> 1.2) multi_json (~> 1.0) @@ -166,7 +166,7 @@ GEM tzinfo (1.2.2) thread_safe (~> 0.1) uber (0.0.13) - uglifier (2.7.0) + uglifier (2.7.1) execjs (>= 0.3.0) json (>= 1.8.0) diff --git a/website/source/docs/commands/remote.html.markdown b/website/source/docs/commands/remote.html.markdown index 3bc96c802..22d341891 100644 --- a/website/source/docs/commands/remote.html.markdown +++ b/website/source/docs/commands/remote.html.markdown @@ -16,7 +16,7 @@ Terraform will automatically fetch the latest state from the remote server when necessary and if any updates are made, the newest state is persisted back to the remote server. In this mode, users do not need to durably store the state using version -control or shared storaged. +control or shared storage. ## Usage diff --git a/website/source/docs/providers/aws/r/db_instance.html.markdown b/website/source/docs/providers/aws/r/db_instance.html.markdown index 7727b9dd6..4b2253115 100644 --- a/website/source/docs/providers/aws/r/db_instance.html.markdown +++ b/website/source/docs/providers/aws/r/db_instance.html.markdown @@ -61,6 +61,7 @@ The following arguments are supported: Only used for [DB Instances on the _EC2-Classic_ Platform](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_VPC.html#USER_VPC.FindDefaultVPC). * `db_subnet_group_name` - (Optional) Name of DB subnet group * `parameter_group_name` - (Optional) Name of the DB parameter group to associate. +* `storage_encrypted` - (Optional) Specifies whether the DB instance is encrypted. The Default is `false` if not specified. ## Attributes Reference @@ -82,4 +83,5 @@ The following attributes are exported: * `port` - The database port * `status` - The RDS instance status * `username` - The master username for the database +* `storage_encrypted` - Specifies whether the DB instance is encrypted diff --git a/website/source/docs/providers/aws/r/internet_gateway.html.markdown b/website/source/docs/providers/aws/r/internet_gateway.html.markdown index ec79f922a..cefedc6ad 100644 --- a/website/source/docs/providers/aws/r/internet_gateway.html.markdown +++ b/website/source/docs/providers/aws/r/internet_gateway.html.markdown @@ -29,6 +29,18 @@ The following arguments are supported: * `vpc_id` - (Required) The VPC ID to create in. * `tags` - (Optional) A mapping of tags to assign to the resource. +-> **Note:** It's recommended to denote that the AWS Instance or Elastic IP depends on the Internet Gateway. For example: + + + resource "aws_internet_gateway" "gw" { + vpc_id = "${aws_vpc.main.id}" + } + + resource "aws_instance" "foo" { + depends_on = ["aws_internet_gateway.gw"] + } + + ## Attributes Reference The following attributes are exported: diff --git a/website/source/docs/providers/aws/r/vpc_peering.html.markdown b/website/source/docs/providers/aws/r/vpc_peering.html.markdown index 59af3c0ca..1d396a584 100644 --- a/website/source/docs/providers/aws/r/vpc_peering.html.markdown +++ b/website/source/docs/providers/aws/r/vpc_peering.html.markdown @@ -56,4 +56,4 @@ The following attributes are exported: ## Notes -You still have to accept the peering with the aws console, aws-cli or goamz +You still have to accept the peering with the aws console, aws-cli or aws-sdk-go. diff --git a/website/source/docs/providers/cloudstack/r/egress_firewall.html.markdown b/website/source/docs/providers/cloudstack/r/egress_firewall.html.markdown index 17fa20927..b905bc0e9 100644 --- a/website/source/docs/providers/cloudstack/r/egress_firewall.html.markdown +++ b/website/source/docs/providers/cloudstack/r/egress_firewall.html.markdown @@ -58,4 +58,4 @@ The `rule` block supports: The following attributes are exported: -* `ID` - The network ID for which the egress firewall rules are created. +* `id` - The network ID for which the egress firewall rules are created. diff --git a/website/source/docs/providers/cloudstack/r/firewall.html.markdown b/website/source/docs/providers/cloudstack/r/firewall.html.markdown index 1c659e6bf..8b8aa0089 100644 --- a/website/source/docs/providers/cloudstack/r/firewall.html.markdown +++ b/website/source/docs/providers/cloudstack/r/firewall.html.markdown @@ -58,4 +58,4 @@ The `rule` block supports: The following attributes are exported: -* `ID` - The IP address ID for which the firewall rules are created. +* `id` - The IP address ID for which the firewall rules are created. diff --git a/website/source/docs/providers/cloudstack/r/network_acl_rule.html.markdown b/website/source/docs/providers/cloudstack/r/network_acl_rule.html.markdown index fb6b0891f..f82b8f446 100644 --- a/website/source/docs/providers/cloudstack/r/network_acl_rule.html.markdown +++ b/website/source/docs/providers/cloudstack/r/network_acl_rule.html.markdown @@ -66,4 +66,4 @@ The `rule` block supports: The following attributes are exported: -* `ID` - The ACL ID for which the rules are created. +* `id` - The ACL ID for which the rules are created. diff --git a/website/source/docs/providers/cloudstack/r/vpn_connection.html.markdown b/website/source/docs/providers/cloudstack/r/vpn_connection.html.markdown new file mode 100644 index 000000000..3ecf17cbc --- /dev/null +++ b/website/source/docs/providers/cloudstack/r/vpn_connection.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_vpn_connection" +sidebar_current: "docs-cloudstack-resource-vpn-connection" +description: |- + Creates a site to site VPN connection. +--- + +# cloudstack\_vpn\_connection + +Creates a site to site VPN connection. + +## Example Usage + +Basic usage: + +``` +resource "cloudstack_vpn_connection" "default" { + customergatewayid = "xxx" + vpngatewayid = "xxx" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `customergatewayid` - (Required) The Customer Gateway ID to connect. + Changing this forces a new resource to be created. + +* `vpngatewayid` - (Required) The VPN Gateway ID to connect. + Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPN Connection. diff --git a/website/source/docs/providers/cloudstack/r/vpn_customer_gateway.html.markdown b/website/source/docs/providers/cloudstack/r/vpn_customer_gateway.html.markdown new file mode 100644 index 000000000..84183b8d6 --- /dev/null +++ b/website/source/docs/providers/cloudstack/r/vpn_customer_gateway.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_vpn_customer_gateway" +sidebar_current: "docs-cloudstack-resource-vpn-customer-gateway" +description: |- + Creates a site to site VPN local customer gateway. +--- + +# cloudstack\_vpn\_customer\_gateway + +Creates a site to site VPN local customer gateway. + +## Example Usage + +Basic usage: + +``` +resource "cloudstack_vpn_customer_gateway" "default" { + name = "test-vpc" + cidr = "10.0.0.0/8" + esp_policy = "aes256-sha1" + gateway = "192.168.0.1" + ike_policy = "aes256-sha1" + ipsec_psk = "terraform" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the VPN Customer Gateway. + +* `cidr` - (Required) The CIDR block that needs to be routed through this gateway. + +* `esp_policy` - (Required) The ESP policy to use for this VPN Customer Gateway. + +* `gateway` - (Required) The public IP address of the related VPN Gateway. + +* `ike_policy` - (Required) The IKE policy to use for this VPN Customer Gateway. + +* `ipsec_psk` - (Required) The IPSEC pre-shared key used for this gateway. + +* `dpd` - (Optional) If DPD is enabled for the related VPN connection (defaults false) + +* `esp_lifetime` - (Optional) The ESP lifetime of phase 2 VPN connection to this + VPN Customer Gateway in seconds (defaults 86400) + +* `ike_lifetime` - (Optional) The IKE lifetime of phase 2 VPN connection to this + VPN Customer Gateway in seconds (defaults 86400) + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPN Customer Gateway. +* `dpd` - Enable or disable DPD is enabled for the related VPN connection. +* `esp_lifetime` - The ESP lifetime of phase 2 VPN connection to this VPN Customer Gateway. +* `ike_lifetime` - The IKE lifetime of phase 2 VPN connection to this VPN Customer Gateway. diff --git a/website/source/docs/providers/cloudstack/r/vpn_gateway.html.markdown b/website/source/docs/providers/cloudstack/r/vpn_gateway.html.markdown new file mode 100644 index 000000000..10aabd796 --- /dev/null +++ b/website/source/docs/providers/cloudstack/r/vpn_gateway.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "cloudstack" +page_title: "CloudStack: cloudstack_vpn_gateway" +sidebar_current: "docs-cloudstack-resource-vpn-gateway" +description: |- + Creates a site to site VPN local gateway. +--- + +# cloudstack\_vpn\_gateway + +Creates a site to site VPN local gateway. + +## Example Usage + +Basic usage: + +``` +resource "cloudstack_vpn_gateway" "default" { + vpc = "test-vpc" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `vpc` - (Required) The name of the VPC for which to create the VPN Gateway. + Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the VPN Gateway. +* `public_ip` - The public IP address associated with the VPN Gateway. diff --git a/website/source/docs/providers/do/r/ssh_key.html.markdown b/website/source/docs/providers/do/r/ssh_key.html.markdown new file mode 100644 index 000000000..7a8033519 --- /dev/null +++ b/website/source/docs/providers/do/r/ssh_key.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "digitalocean" +page_title: "DigitalOcean: digitalocean_ssh_key" +sidebar_current: "docs-do-resource-ssh-key" +description: |- + Provides a DigitalOcean SSH key resource. +--- + +# digitalocean\_ssh_key + +Provides a DigitalOcean SSH key resource to allow you manage SSH +keys for Droplet access. Keys created with this resource +can be referenced in your droplet configuration via their ID or +fingerprint. + +## Example Usage + +``` +# Create a new SSH key +resource "digitalocean_ssh_key" "default" { + name = "Terraform Example" + public_key = "${file("/Users/terraform/.ssh/id_rsa.pub")}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the SSH key for identification +* `public_key` - (Required) The public key. If this is a file, it +can be read using the file interpolation function + +## Attributes Reference + +The following attributes are exported: + +* `id` - The unique ID of the key +* `name` - The name of the SSH key +* `public_key` - The text of the public key +* `fingerprint` - The fingerprint of the SSH key diff --git a/website/source/layouts/cloudstack.erb b/website/source/layouts/cloudstack.erb index a0e137aae..ee1ae6587 100644 --- a/website/source/layouts/cloudstack.erb +++ b/website/source/layouts/cloudstack.erb @@ -1,66 +1,78 @@ <% wrap_layout :inner do %> - <% content_for :sidebar do %> - + <% end %> + + <%= yield %> +<% end %> diff --git a/website/source/layouts/digitalocean.erb b/website/source/layouts/digitalocean.erb index cfffcd85f..c67d6395b 100644 --- a/website/source/layouts/digitalocean.erb +++ b/website/source/layouts/digitalocean.erb @@ -23,6 +23,10 @@ > digitalocean_record + + + > + digitalocean_ssh_key