Merge branch 'master' of github.com:hashicorp/terraform

This commit is contained in:
Peter Beams 2015-03-17 09:04:16 +00:00
commit c72918efb3
81 changed files with 2687 additions and 1091 deletions

View File

@ -30,6 +30,7 @@ IMPROVEMENTS:
info. [GH-1029] info. [GH-1029]
* **New config function: `split`** - Split a value based on a delimiter. * **New config function: `split`** - Split a value based on a delimiter.
This is useful for faking lists as parameters to modules. 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 * 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 change. This will lower the amount of state changing on things
like refresh. like refresh.
@ -48,7 +49,8 @@ BUG FIXES:
"resource.0" would ignore the latter completely. [GH-1086] "resource.0" would ignore the latter completely. [GH-1086]
* providers/aws: manually deleted VPC removes it from the state * providers/aws: manually deleted VPC removes it from the state
* providers/aws: `source_dest_check` regression fixed (now works). [GH-1020] * 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: Waits until droplet is ready to be destroyed [GH-1057]
* providers/digitalocean: More lenient about 404's while waiting [GH-1062] * providers/digitalocean: More lenient about 404's while waiting [GH-1062]
* providers/google: Network data in state was not being stored. [GH-1095] * providers/google: Network data in state was not being stored. [GH-1095]

View File

@ -12,6 +12,9 @@ bin: generate
dev: generate dev: generate
@TF_DEV=1 sh -c "'$(CURDIR)/scripts/build.sh'" @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 runs the unit tests and vets the code
test: generate test: generate
TF_ACC= go test $(TEST) $(TESTARGS) -timeout=30s -parallel=4 TF_ACC= go test $(TEST) $(TESTARGS) -timeout=30s -parallel=4

View File

@ -3,21 +3,16 @@ package aws
import ( import (
"fmt" "fmt"
"log" "log"
"strings"
"unicode"
"github.com/hashicorp/terraform/helper/multierror" "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/autoscaling"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/aws-sdk-go/gen/elb" "github.com/hashicorp/aws-sdk-go/gen/elb"
"github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/aws-sdk-go/gen/rds"
"github.com/hashicorp/aws-sdk-go/gen/route53" "github.com/hashicorp/aws-sdk-go/gen/route53"
"github.com/hashicorp/aws-sdk-go/gen/s3" "github.com/hashicorp/aws-sdk-go/gen/s3"
awsEC2 "github.com/hashicorp/aws-sdk-go/gen/ec2"
) )
type Config struct { type Config struct {
@ -29,7 +24,6 @@ type Config struct {
type AWSClient struct { type AWSClient struct {
ec2conn *ec2.EC2 ec2conn *ec2.EC2
awsEC2conn *awsEC2.EC2
elbconn *elb.ELB elbconn *elb.ELB
autoscalingconn *autoscaling.AutoScaling autoscalingconn *autoscaling.AutoScaling
s3conn *s3.S3 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 // Get the auth and region. This can fail if keys/regions were not
// specified and we're attempting to use the environment. // specified and we're attempting to use the environment.
var errs []error 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") log.Println("[INFO] Building AWS region structure")
region, err := c.AWSRegion() err := c.ValidateRegion()
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)
} }
@ -62,10 +51,9 @@ func (c *Config) Client() (interface{}, error) {
// bucket storage in S3 // bucket storage in S3
client.region = c.Region 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") log.Println("[INFO] Initializing ELB connection")
client.elbconn = elb.New(creds, c.Region, nil) client.elbconn = elb.New(creds, c.Region, nil)
log.Println("[INFO] Initializing AutoScaling connection") 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 // See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
log.Println("[INFO] Initializing Route53 connection") log.Println("[INFO] Initializing Route53 connection")
client.r53conn = route53.New(creds, "us-east-1", nil) client.r53conn = route53.New(creds, "us-east-1", nil)
log.Println("[INFO] Initializing AWS-GO EC2 Connection") log.Println("[INFO] Initializing EC2 Connection")
client.awsEC2conn = awsEC2.New(creds, c.Region, nil) client.ec2conn = ec2.New(creds, c.Region, nil)
} }
if len(errs) > 0 { if len(errs) > 0 {
@ -91,54 +79,17 @@ func (c *Config) Client() (interface{}, error) {
return &client, nil 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 // IsValidRegion returns true if the configured region is a valid AWS
// region and false if it's not // 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", 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", "eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
"sa-east-1", "cn-north-1", "us-gov-west-1"} "sa-east-1", "cn-north-1", "us-gov-west-1"}
for _, valid := range regions { for _, valid := range regions {
if c.Region == valid { if c.Region == valid {
return true return nil
} }
} }
return false return fmt.Errorf("Not a valid region: %s", c.Region)
}
// 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
} }

View File

@ -2,11 +2,14 @@ package aws
import ( import (
"fmt" "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) { func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.NetworkACLEntry, error) {
entries := make([]ec2.NetworkAclEntry, 0, len(configured)) entries := make([]ec2.NetworkACLEntry, 0, len(configured))
for _, eRaw := range configured { for _, eRaw := range configured {
data := eRaw.(map[string]interface{}) data := eRaw.(map[string]interface{})
protocol := data["protocol"].(string) 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) return nil, fmt.Errorf("Invalid Protocol %s for rule %#v", protocol, data)
} }
p := extractProtocolInteger(data["protocol"].(string)) p := extractProtocolInteger(data["protocol"].(string))
e := ec2.NetworkAclEntry{ e := ec2.NetworkACLEntry{
Protocol: p, Protocol: aws.String(strconv.Itoa(p)),
PortRange: ec2.PortRange{ PortRange: &ec2.PortRange{
From: data["from_port"].(int), From: aws.Integer(data["from_port"].(int)),
To: data["to_port"].(int), To: aws.Integer(data["to_port"].(int)),
}, },
Egress: (entryType == "egress"), Egress: aws.Boolean((entryType == "egress")),
RuleAction: data["action"].(string), RuleAction: aws.String(data["action"].(string)),
RuleNumber: data["rule_no"].(int), RuleNumber: aws.Integer(data["rule_no"].(int)),
CidrBlock: data["cidr_block"].(string), CIDRBlock: aws.String(data["cidr_block"].(string)),
} }
entries = append(entries, e) 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)) entries := make([]map[string]interface{}, 0, len(list))
for _, entry := range list { for _, entry := range list {
entries = append(entries, map[string]interface{}{ entries = append(entries, map[string]interface{}{
"from_port": entry.PortRange.From, "from_port": *entry.PortRange.From,
"to_port": entry.PortRange.To, "to_port": *entry.PortRange.To,
"action": entry.RuleAction, "action": *entry.RuleAction,
"rule_no": entry.RuleNumber, "rule_no": *entry.RuleNumber,
"protocol": extractProtocolString(entry.Protocol), "protocol": *entry.Protocol,
"cidr_block": entry.CidrBlock, "cidr_block": *entry.CIDRBlock,
}) })
} }
return entries return entries

View File

@ -4,10 +4,11 @@ import (
"reflect" "reflect"
"testing" "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{}{ input := []interface{}{
map[string]interface{}{ map[string]interface{}{
"protocol": "tcp", "protocol": "tcp",
@ -28,30 +29,28 @@ func Test_expandNetworkAclEntry(t *testing.T) {
} }
expanded, _ := expandNetworkAclEntries(input, "egress") expanded, _ := expandNetworkAclEntries(input, "egress")
expected := []ec2.NetworkAclEntry{ expected := []ec2.NetworkACLEntry{
ec2.NetworkAclEntry{ ec2.NetworkACLEntry{
Protocol: 6, Protocol: aws.String("6"),
PortRange: ec2.PortRange{ PortRange: &ec2.PortRange{
From: 22, From: aws.Integer(22),
To: 22, To: aws.Integer(22),
}, },
RuleAction: "deny", RuleAction: aws.String("deny"),
RuleNumber: 1, RuleNumber: aws.Integer(1),
CidrBlock: "0.0.0.0/0", CIDRBlock: aws.String("0.0.0.0/0"),
Egress: true, Egress: aws.Boolean(true),
IcmpCode: ec2.IcmpCode{Code: 0, Type: 0},
}, },
ec2.NetworkAclEntry{ ec2.NetworkACLEntry{
Protocol: 6, Protocol: aws.String("6"),
PortRange: ec2.PortRange{ PortRange: &ec2.PortRange{
From: 443, From: aws.Integer(443),
To: 443, To: aws.Integer(443),
}, },
RuleAction: "deny", RuleAction: aws.String("deny"),
RuleNumber: 2, RuleNumber: aws.Integer(2),
CidrBlock: "0.0.0.0/0", CIDRBlock: aws.String("0.0.0.0/0"),
Egress: true, Egress: aws.Boolean(true),
IcmpCode: ec2.IcmpCode{Code: 0, Type: 0},
}, },
} }
@ -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{ apiInput := []ec2.NetworkACLEntry{
ec2.NetworkAclEntry{ ec2.NetworkACLEntry{
Protocol: 6, Protocol: aws.String("tcp"),
PortRange: ec2.PortRange{ PortRange: &ec2.PortRange{
From: 22, From: aws.Integer(22),
To: 22, To: aws.Integer(22),
}, },
RuleAction: "deny", RuleAction: aws.String("deny"),
RuleNumber: 1, RuleNumber: aws.Integer(1),
CidrBlock: "0.0.0.0/0", CIDRBlock: aws.String("0.0.0.0/0"),
}, },
ec2.NetworkAclEntry{ ec2.NetworkACLEntry{
Protocol: 6, Protocol: aws.String("tcp"),
PortRange: ec2.PortRange{ PortRange: &ec2.PortRange{
From: 443, From: aws.Integer(443),
To: 443, To: aws.Integer(443),
}, },
RuleAction: "deny", RuleAction: aws.String("deny"),
RuleNumber: 2, RuleNumber: aws.Integer(2),
CidrBlock: "0.0.0.0/0", CIDRBlock: aws.String("0.0.0.0/0"),
}, },
} }
flattened := flattenNetworkAclEntries(apiInput) flattened := flattenNetworkAclEntries(apiInput)

View File

@ -156,7 +156,6 @@ func resourceAwsDbInstance() *schema.Resource {
"final_snapshot_identifier": &schema.Schema{ "final_snapshot_identifier": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
ForceNew: true,
}, },
"db_subnet_group_name": &schema.Schema{ "db_subnet_group_name": &schema.Schema{

View File

@ -152,7 +152,7 @@ func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{})
os := o.(*schema.Set) os := o.(*schema.Set)
ns := n.(*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()) parameters, err := expandParameters(ns.Difference(os).List())
if err != nil { if err != nil {
return err return err

View File

@ -60,7 +60,7 @@ func resourceAwsEip() *schema.Resource {
} }
func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
// By default, we're not in a VPC // By default, we're not in a VPC
domainOpt := "" domainOpt := ""
@ -97,7 +97,7 @@ func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error {
} }
func resourceAwsEipRead(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) domain := resourceAwsEipDomain(d)
id := d.Id() id := d.Id()
@ -148,7 +148,7 @@ func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error {
} }
func resourceAwsEipUpdate(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) domain := resourceAwsEipDomain(d)
@ -181,7 +181,7 @@ func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error {
} }
func resourceAwsEipDelete(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 { if err := resourceAwsEipRead(d, meta); err != nil {
return err return err

View File

@ -58,7 +58,7 @@ func TestAccAWSEIP_instance(t *testing.T) {
} }
func testAccCheckAWSEIPDestroy(s *terraform.State) error { func testAccCheckAWSEIPDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_eip" { 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") 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") { if strings.Contains(rs.Primary.ID, "eipalloc") {
req := &ec2.DescribeAddressesRequest{ req := &ec2.DescribeAddressesRequest{

View File

@ -161,7 +161,7 @@ func resourceAwsElb() *schema.Resource {
func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn 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()) listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
if err != nil { if err != nil {
return err return err

View File

@ -3,17 +3,18 @@ package aws
import ( import (
"bytes" "bytes"
"crypto/sha1" "crypto/sha1"
"encoding/base64"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"log" "log"
"strconv"
"strings" "strings"
"time" "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/hashcode"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsInstance() *schema.Resource { func resourceAwsInstance() *schema.Resource {
@ -258,7 +259,28 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
// Figure out user data // Figure out user data
userData := "" userData := ""
if v := d.Get("user_data"); v != nil { 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 associatePublicIPAddress := false
@ -266,36 +288,65 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
associatePublicIPAddress = v.(bool) associatePublicIPAddress = v.(bool)
} }
// Build the creation struct // check for non-default Subnet, and cast it to a String
runOpts := &ec2.RunInstances{ var hasSubnet bool
ImageId: d.Get("ami").(string), subnet, hasSubnet := d.GetOk("subnet_id")
AvailZone: d.Get("availability_zone").(string), subnetID := subnet.(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),
}
var groups []string
if v := d.Get("security_groups"); v != nil { 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() { for _, v := range v.(*schema.Set).List() {
str := v.(string) str := v.(string)
groups = append(groups, str)
var g ec2.SecurityGroup
if runOpts.SubnetId != "" {
g.Id = str
} else {
g.Name = str
}
runOpts.SecurityGroups = append(runOpts.SecurityGroups, g)
} }
} }
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) blockDevices := make([]interface{}, 0)
if v := d.Get("block_device"); v != nil { if v := d.Get("block_device"); v != nil {
@ -311,24 +362,27 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
} }
if len(blockDevices) > 0 { if len(blockDevices) > 0 {
runOpts.BlockDevices = make([]ec2.BlockDeviceMapping, len(blockDevices)) runOpts.BlockDeviceMappings = make([]ec2.BlockDeviceMapping, len(blockDevices))
for i, v := range blockDevices { for i, v := range blockDevices {
bd := v.(map[string]interface{}) bd := v.(map[string]interface{})
runOpts.BlockDevices[i].DeviceName = bd["device_name"].(string) runOpts.BlockDeviceMappings[i].DeviceName = aws.String(bd["device_name"].(string))
runOpts.BlockDevices[i].VolumeType = bd["volume_type"].(string) runOpts.BlockDeviceMappings[i].EBS = &ec2.EBSBlockDevice{
runOpts.BlockDevices[i].VolumeSize = int64(bd["volume_size"].(int)) VolumeType: aws.String(bd["volume_type"].(string)),
runOpts.BlockDevices[i].DeleteOnTermination = bd["delete_on_termination"].(bool) VolumeSize: aws.Integer(bd["volume_size"].(int)),
if v, ok := bd["virtual_name"].(string); ok { DeleteOnTermination: aws.Boolean(bd["delete_on_termination"].(bool)),
runOpts.BlockDevices[i].VirtualName = v
} }
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 { 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 { if v, ok := bd["iops"].(int); ok && v > 0 {
runOpts.BlockDevices[i].IOPS = int64(v) runOpts.BlockDeviceMappings[i].EBS.IOPS = aws.Integer(v)
} }
} }
} }
@ -341,21 +395,21 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
} }
instance := &runResp.Instances[0] 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 // 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 // Wait for the instance to become running so we can get some attributes
// that aren't available until later. // that aren't available until later.
log.Printf( log.Printf(
"[DEBUG] Waiting for instance (%s) to become running", "[DEBUG] Waiting for instance (%s) to become running",
instance.InstanceId) *instance.InstanceID)
stateConf := &resource.StateChangeConf{ stateConf := &resource.StateChangeConf{
Pending: []string{"pending"}, Pending: []string{"pending"},
Target: "running", Target: "running",
Refresh: InstanceStateRefreshFunc(ec2conn, instance.InstanceId), Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceID),
Timeout: 10 * time.Minute, Timeout: 10 * time.Minute,
Delay: 10 * time.Second, Delay: 10 * time.Second,
MinTimeout: 3 * time.Second, MinTimeout: 3 * time.Second,
@ -365,16 +419,18 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
if err != nil { if err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error waiting for instance (%s) to become ready: %s", "Error waiting for instance (%s) to become ready: %s",
instance.InstanceId, err) *instance.InstanceID, err)
} }
instance = instanceRaw.(*ec2.Instance) instance = instanceRaw.(*ec2.Instance)
// Initialize the connection info // Initialize the connection info
d.SetConnInfo(map[string]string{ if instance.PublicIPAddress != nil {
"type": "ssh", d.SetConnInfo(map[string]string{
"host": instance.PublicIpAddress, "type": "ssh",
}) "host": *instance.PublicIPAddress,
})
}
// Set our attributes // Set our attributes
if err := resourceAwsInstanceRead(d, meta); err != nil { 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 { func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn 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 err != nil {
// If the instance was not found, return nil so that we can show // If the instance was not found, return nil so that we can show
// that the instance is gone. // 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("") d.SetId("")
return nil return nil
} }
@ -410,28 +468,33 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
instance := &resp.Reservations[0].Instances[0] instance := &resp.Reservations[0].Instances[0]
// If the instance is terminated, then it is gone // If the instance is terminated, then it is gone
if instance.State.Name == "terminated" { if *instance.State.Name == "terminated" {
d.SetId("") d.SetId("")
return nil return nil
} }
d.Set("availability_zone", instance.AvailZone) d.Set("availability_zone", instance.Placement.AvailabilityZone)
d.Set("key_name", instance.KeyName) d.Set("key_name", instance.KeyName)
d.Set("public_dns", instance.DNSName) d.Set("public_dns", instance.PublicDNSName)
d.Set("public_ip", instance.PublicIpAddress) d.Set("public_ip", instance.PublicIPAddress)
d.Set("private_dns", instance.PrivateDNSName) d.Set("private_dns", instance.PrivateDNSName)
d.Set("private_ip", instance.PrivateIpAddress) d.Set("private_ip", instance.PrivateIPAddress)
d.Set("subnet_id", instance.SubnetId) d.Set("subnet_id", instance.SubnetID)
d.Set("ebs_optimized", instance.EbsOptimized) 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("tags", tagsToMap(instance.Tags))
d.Set("tenancy", instance.Tenancy) d.Set("tenancy", instance.Placement.Tenancy)
// Determine whether we're referring to security groups with // Determine whether we're referring to security groups with
// IDs or names. We use a heuristic to figure this out. By default, // 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 // 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 // all-name list of security groups, we use names. Or, if we had any
// IDs, we use IDs. // IDs, we use IDs.
useID := instance.SubnetId != "" useID := *instance.SubnetID != ""
if v := d.Get("security_groups"); v != nil { if v := d.Get("security_groups"); v != nil {
match := false match := false
for _, v := range v.(*schema.Set).List() { 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)) sgs := make([]string, len(instance.SecurityGroups))
for i, sg := range instance.SecurityGroups { for i, sg := range instance.SecurityGroups {
if useID { if useID {
sgs[i] = sg.Id sgs[i] = *sg.GroupID
} else { } else {
sgs[i] = sg.Name sgs[i] = *sg.GroupName
} }
} }
d.Set("security_groups", sgs) d.Set("security_groups", sgs)
blockDevices := make(map[string]ec2.BlockDevice) blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
for _, bd := range instance.BlockDevices { for _, bd := range instance.BlockDeviceMappings {
blockDevices[bd.VolumeId] = bd blockDevices[*bd.EBS.VolumeID] = bd
} }
volIDs := make([]string, 0, len(blockDevices)) volIDs := make([]string, 0, len(blockDevices))
for volID := range blockDevices { for _, vol := range blockDevices {
volIDs = append(volIDs, volID) volIDs = append(volIDs, *vol.EBS.VolumeID)
} }
volResp, err := ec2conn.Volumes(volIDs, ec2.NewFilter()) volResp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesRequest{
VolumeIDs: volIDs,
})
if err != nil { if err != nil {
return err return err
} }
@ -473,28 +538,25 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
nonRootBlockDevices := make([]map[string]interface{}, 0) nonRootBlockDevices := make([]map[string]interface{}, 0)
rootBlockDevice := make([]interface{}, 0, 1) rootBlockDevice := make([]interface{}, 0, 1)
for _, vol := range volResp.Volumes { for _, vol := range volResp.Volumes {
volSize, err := strconv.Atoi(vol.Size)
if err != nil {
return err
}
blockDevice := make(map[string]interface{}) blockDevice := make(map[string]interface{})
blockDevice["device_name"] = blockDevices[vol.VolumeId].DeviceName blockDevice["device_name"] = *blockDevices[*vol.VolumeID].DeviceName
blockDevice["volume_type"] = vol.VolumeType blockDevice["volume_type"] = *vol.VolumeType
blockDevice["volume_size"] = volSize blockDevice["volume_size"] = *vol.Size
if vol.IOPS != nil {
blockDevice["iops"] = *vol.IOPS
}
blockDevice["delete_on_termination"] = 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 // If this is the root device, save it. We stop here since we
// can't put invalid keys into this map. // can't put invalid keys into this map.
if blockDevice["device_name"] == instance.RootDeviceName { if blockDevice["device_name"] == *instance.RootDeviceName {
rootBlockDevice = []interface{}{blockDevice} rootBlockDevice = []interface{}{blockDevice}
continue continue
} }
blockDevice["snapshot_id"] = vol.SnapshotId blockDevice["snapshot_id"] = *vol.SnapshotID
blockDevice["encrypted"] = vol.Encrypted blockDevice["encrypted"] = *vol.Encrypted
blockDevice["iops"] = vol.IOPS
nonRootBlockDevices = append(nonRootBlockDevices, blockDevice) nonRootBlockDevices = append(nonRootBlockDevices, blockDevice)
} }
d.Set("block_device", nonRootBlockDevices) 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 { func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
opts := new(ec2.ModifyInstance) opts := new(ec2.ModifyInstanceAttributeRequest)
opts.SetSourceDestCheck = true
opts.SourceDestCheck = d.Get("source_dest_check").(bool)
log.Printf("[INFO] Modifying instance %s: %#v", d.Id(), opts) 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 return err
} }
@ -531,7 +597,10 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
log.Printf("[INFO] Terminating instance: %s", d.Id()) 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) return fmt.Errorf("Error terminating instance: %s", err)
} }
@ -563,9 +632,11 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
// an EC2 instance. // an EC2 instance.
func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc { func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { 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 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. // Set this to nil as if we didn't find anything.
resp = nil resp = nil
} else { } else {
@ -581,7 +652,7 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRe
} }
i := &resp.Reservations[0].Instances[0] i := &resp.Reservations[0].Instances[0]
return i, i.State.Name, nil return i, *i.State.Name, nil
} }
} }

View File

@ -5,24 +5,25 @@ import (
"reflect" "reflect"
"testing" "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/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestAccAWSInstance_normal(t *testing.T) { func TestAccAWSInstance_normal(t *testing.T) {
var v ec2.Instance var v ec2.Instance
testCheck := func(*terraform.State) error { testCheck := func(*terraform.State) error {
if v.AvailZone != "us-west-2a" { if *v.Placement.AvailabilityZone != "us-west-2a" {
return fmt.Errorf("bad availability zone: %#v", v.AvailZone) return fmt.Errorf("bad availability zone: %#v", *v.Placement.AvailabilityZone)
} }
if len(v.SecurityGroups) == 0 { if len(v.SecurityGroups) == 0 {
return fmt.Errorf("no security groups: %#v", v.SecurityGroups) 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) return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
} }
@ -73,9 +74,9 @@ func TestAccAWSInstance_blockDevices(t *testing.T) {
return func(*terraform.State) error { return func(*terraform.State) error {
// Map out the block devices by name, which should be unique. // Map out the block devices by name, which should be unique.
blockDevices := make(map[string]ec2.BlockDevice) blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
for _, blockDevice := range v.BlockDevices { for _, blockDevice := range v.BlockDeviceMappings {
blockDevices[blockDevice.DeviceName] = blockDevice blockDevices[*blockDevice.DeviceName] = blockDevice
} }
// Check if the root block device exists. // Check if the root block device exists.
@ -147,8 +148,8 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
testCheck := func(enabled bool) resource.TestCheckFunc { testCheck := func(enabled bool) resource.TestCheckFunc {
return func(*terraform.State) error { return func(*terraform.State) error {
if v.SourceDestCheck != enabled { if *v.SourceDestCheck != enabled {
return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck) return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
} }
return nil 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 var v ec2.Instance
resource.Test(t, resource.TestCase{ 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 var v ec2.Instance
testCheckPrivateIP := func() resource.TestCheckFunc { testCheckPrivateIP := func() resource.TestCheckFunc {
return func(*terraform.State) error { return func(*terraform.State) error {
if v.PrivateIpAddress != "10.1.1.42" { if *v.PrivateIPAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress) return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
} }
return nil 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 var v ec2.Instance
testCheckPrivateIP := func() resource.TestCheckFunc { testCheckPrivateIP := func() resource.TestCheckFunc {
return func(*terraform.State) error { return func(*terraform.State) error {
if v.PrivateIpAddress != "10.1.1.42" { if *v.PrivateIPAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress) return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
} }
return nil return nil
@ -303,8 +323,9 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
} }
// Try to find the resource // Try to find the resource
resp, err := conn.Instances( resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
[]string{rs.Primary.ID}, ec2.NewFilter()) InstanceIDs: []string{rs.Primary.ID},
})
if err == nil { if err == nil {
if len(resp.Reservations) > 0 { if len(resp.Reservations) > 0 {
return fmt.Errorf("still exist.") return fmt.Errorf("still exist.")
@ -314,7 +335,7 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
} }
// Verify the error is what we want // Verify the error is what we want
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -338,8 +359,9 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun
} }
conn := testAccProvider.Meta().(*AWSClient).ec2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.Instances( resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
[]string{rs.Primary.ID}, ec2.NewFilter()) InstanceIDs: []string{rs.Primary.ID},
})
if err != nil { if err != nil {
return err return err
} }
@ -389,7 +411,7 @@ resource "aws_instance" "foo" {
instance_type = "m1.small" instance_type = "m1.small"
security_groups = ["${aws_security_group.tf_test_foo.name}"] 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" 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"]
}
`

View File

@ -29,7 +29,7 @@ func resourceAwsInternetGateway() *schema.Resource {
} }
func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
// Create the gateway // Create the gateway
log.Printf("[DEBUG] Creating internet gateway") log.Printf("[DEBUG] Creating internet gateway")
@ -43,12 +43,17 @@ func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{})
d.SetId(*ig.InternetGatewayID) d.SetId(*ig.InternetGatewayID)
log.Printf("[INFO] InternetGateway ID: %s", d.Id()) 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 // Attach the new gateway to the correct vpc
return resourceAwsInternetGatewayAttach(d, meta) return resourceAwsInternetGatewayAttach(d, meta)
} }
func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())() igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())()
if err != nil { 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("vpc_id", ig.Attachments[0].VPCID)
} }
d.Set("tags", tagsToMapSDK(ig.Tags)) d.Set("tags", tagsToMap(ig.Tags))
return nil 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 return err
} }
@ -98,7 +103,7 @@ func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{})
} }
func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error { func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
// Detach if it is attached // Detach if it is attached
if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { 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 { func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
if d.Get("vpc_id").(string) == "" { if d.Get("vpc_id").(string) == "" {
log.Printf( log.Printf(
@ -177,7 +182,7 @@ func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{})
} }
func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error { func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
// Get the old VPC ID to detach from // Get the old VPC ID to detach from
vpcID, _ := d.GetChange("vpc_id") vpcID, _ := d.GetChange("vpc_id")

View File

@ -98,6 +98,7 @@ func TestAccInternetGateway_tags(t *testing.T) {
Config: testAccCheckInternetGatewayConfigTags, Config: testAccCheckInternetGatewayConfigTags,
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v), testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v),
testAccCheckTags(&v.Tags, "foo", "bar"),
), ),
}, },
@ -105,8 +106,8 @@ func TestAccInternetGateway_tags(t *testing.T) {
Config: testAccCheckInternetGatewayConfigTagsUpdate, Config: testAccCheckInternetGatewayConfigTagsUpdate,
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v), testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v),
testAccCheckTagsSDK(&v.Tags, "foo", ""), testAccCheckTags(&v.Tags, "foo", ""),
testAccCheckTagsSDK(&v.Tags, "bar", "baz"), testAccCheckTags(&v.Tags, "bar", "baz"),
), ),
}, },
}, },
@ -114,7 +115,7 @@ func TestAccInternetGateway_tags(t *testing.T) {
} }
func testAccCheckInternetGatewayDestroy(s *terraform.State) error { func testAccCheckInternetGatewayDestroy(s *terraform.State) error {
ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_internet_gateway" { 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") return fmt.Errorf("No ID is set")
} }
ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{ resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{
InternetGatewayIDs: []string{rs.Primary.ID}, InternetGatewayIDs: []string{rs.Primary.ID},
}) })

View File

@ -37,7 +37,7 @@ func resourceAwsKeyPair() *schema.Resource {
} }
func resourceAwsKeyPairCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsKeyPairCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
keyName := d.Get("key_name").(string) keyName := d.Get("key_name").(string)
publicKey := d.Get("public_key").(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 { func resourceAwsKeyPairRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
req := &ec2.DescribeKeyPairsRequest{ req := &ec2.DescribeKeyPairsRequest{
KeyNames: []string{d.Id()}, KeyNames: []string{d.Id()},
@ -77,7 +77,7 @@ func resourceAwsKeyPairRead(d *schema.ResourceData, meta interface{}) error {
} }
func resourceAwsKeyPairDelete(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{ err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairRequest{
KeyName: aws.String(d.Id()), KeyName: aws.String(d.Id()),

View File

@ -30,7 +30,7 @@ func TestAccAWSKeyPair_normal(t *testing.T) {
} }
func testAccCheckAWSKeyPairDestroy(s *terraform.State) error { func testAccCheckAWSKeyPairDestroy(s *terraform.State) error {
ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_key_pair" { 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") return fmt.Errorf("No KeyPair name is set")
} }
ec2conn := testAccProvider.Meta().(*AWSClient).awsEC2conn ec2conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := ec2conn.DescribeKeyPairs(&ec2.DescribeKeyPairsRequest{ resp, err := ec2conn.DescribeKeyPairs(&ec2.DescribeKeyPairsRequest{
KeyNames: []string{rs.Primary.ID}, KeyNames: []string{rs.Primary.ID},

View File

@ -4,8 +4,9 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsMainRouteTableAssociation() *schema.Resource { func resourceAwsMainRouteTableAssociation() *schema.Resource {
@ -50,16 +51,16 @@ func resourceAwsMainRouteTableAssociationCreate(d *schema.ResourceData, meta int
return err return err
} }
resp, err := ec2conn.ReassociateRouteTable( resp, err := ec2conn.ReplaceRouteTableAssociation(&ec2.ReplaceRouteTableAssociationRequest{
mainAssociation.AssociationId, AssociationID: mainAssociation.RouteTableAssociationID,
routeTableId, RouteTableID: aws.String(routeTableId),
) })
if err != nil { if err != nil {
return err return err
} }
d.Set("original_route_table_id", mainAssociation.RouteTableId) d.Set("original_route_table_id", mainAssociation.RouteTableID)
d.SetId(resp.AssociationId) d.SetId(*resp.NewAssociationID)
log.Printf("[INFO] New main route table association ID: %s", d.Id()) log.Printf("[INFO] New main route table association ID: %s", d.Id())
return nil return nil
@ -75,7 +76,7 @@ func resourceAwsMainRouteTableAssociationRead(d *schema.ResourceData, meta inter
return err return err
} }
if mainAssociation.AssociationId != d.Id() { if *mainAssociation.RouteTableAssociationID != d.Id() {
// It seems it doesn't exist anymore, so clear the ID // It seems it doesn't exist anymore, so clear the ID
d.SetId("") 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) 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 { if err != nil {
return err return err
} }
d.SetId(resp.AssociationId) d.SetId(*resp.NewAssociationID)
log.Printf("[INFO] New main route table association ID: %s", d.Id()) log.Printf("[INFO] New main route table association ID: %s", d.Id())
return nil return nil
@ -113,12 +117,15 @@ func resourceAwsMainRouteTableAssociationDelete(d *schema.ResourceData, meta int
vpcId, vpcId,
originalRouteTableId) 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 { if err != nil {
return err return err
} }
log.Printf("[INFO] Resulting Association ID: %s", resp.AssociationId) log.Printf("[INFO] Resulting Association ID: %s", *resp.NewAssociationID)
return nil return nil
} }
@ -130,7 +137,7 @@ func findMainRouteTableAssociation(ec2conn *ec2.EC2, vpcId string) (*ec2.RouteTa
} }
for _, a := range mainRouteTable.Associations { for _, a := range mainRouteTable.Associations {
if a.Main { if *a.Main {
return &a, nil 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) { func findMainRouteTable(ec2conn *ec2.EC2, vpcId string) (*ec2.RouteTable, error) {
filter := ec2.NewFilter() mainFilter := ec2.Filter{
filter.Add("association.main", "true") aws.String("association.main"),
filter.Add("vpc-id", vpcId) []string{"true"},
routeResp, err := ec2conn.DescribeRouteTables(nil, filter) }
vpcFilter := ec2.Filter{
aws.String("vpc-id"),
[]string{vpcId},
}
routeResp, err := ec2conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
Filters: []ec2.Filter{mainFilter, vpcFilter},
})
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(routeResp.RouteTables) != 1 { } else if len(routeResp.RouteTables) != 1 {

View File

@ -71,9 +71,9 @@ func testAccCheckMainRouteTableAssociation(
return err return err
} }
if mainAssociation.AssociationId != rs.Primary.ID { if *mainAssociation.RouteTableAssociationID != rs.Primary.ID {
return fmt.Errorf("Found wrong main association: %s", return fmt.Errorf("Found wrong main association: %s",
mainAssociation.AssociationId) *mainAssociation.RouteTableAssociationID)
} }
return nil return nil

View File

@ -6,10 +6,11 @@ import (
"log" "log"
"time" "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/hashcode"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsNetworkAcl() *schema.Resource { func resourceAwsNetworkAcl() *schema.Resource {
@ -111,20 +112,20 @@ func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
// Create the Network Acl // Create the Network Acl
createOpts := &ec2.CreateNetworkAcl{ createOpts := &ec2.CreateNetworkACLRequest{
VpcId: d.Get("vpc_id").(string), VPCID: aws.String(d.Get("vpc_id").(string)),
} }
log.Printf("[DEBUG] Network Acl create config: %#v", createOpts) log.Printf("[DEBUG] Network Acl create config: %#v", createOpts)
resp, err := ec2conn.CreateNetworkAcl(createOpts) resp, err := ec2conn.CreateNetworkACL(createOpts)
if err != nil { if err != nil {
return fmt.Errorf("Error creating network acl: %s", err) return fmt.Errorf("Error creating network acl: %s", err)
} }
// Get the ID and store it // Get the ID and store it
networkAcl := &resp.NetworkAcl networkAcl := resp.NetworkACL
d.SetId(networkAcl.NetworkAclId) d.SetId(*networkAcl.NetworkACLID)
log.Printf("[INFO] Network Acl ID: %s", networkAcl.NetworkAclId) log.Printf("[INFO] Network Acl ID: %s", *networkAcl.NetworkACLID)
// Update rules and subnet association once acl is created // Update rules and subnet association once acl is created
return resourceAwsNetworkAclUpdate(d, meta) return resourceAwsNetworkAclUpdate(d, meta)
@ -133,7 +134,9 @@ func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error
func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error { func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn 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 { if err != nil {
return err return err
@ -142,20 +145,20 @@ func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
return nil return nil
} }
networkAcl := &resp.NetworkAcls[0] networkAcl := &resp.NetworkACLs[0]
var ingressEntries []ec2.NetworkAclEntry var ingressEntries []ec2.NetworkACLEntry
var egressEntries []ec2.NetworkAclEntry var egressEntries []ec2.NetworkACLEntry
// separate the ingress and egress rules // separate the ingress and egress rules
for _, e := range networkAcl.EntrySet { for _, e := range networkAcl.Entries {
if e.Egress == true { if *e.Egress == true {
egressEntries = append(egressEntries, e) egressEntries = append(egressEntries, e)
} else { } else {
ingressEntries = append(ingressEntries, e) ingressEntries = append(ingressEntries, e)
} }
} }
d.Set("vpc_id", networkAcl.VpcId) d.Set("vpc_id", networkAcl.VPCID)
d.Set("ingress", ingressEntries) d.Set("ingress", ingressEntries)
d.Set("egress", egressEntries) d.Set("egress", egressEntries)
d.Set("tags", tagsToMap(networkAcl.Tags)) d.Set("tags", tagsToMap(networkAcl.Tags))
@ -190,7 +193,10 @@ func resourceAwsNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error
if err != nil { if err != nil {
return fmt.Errorf("Failed to update acl %s with subnet %s: %s", d.Id(), newSubnet, err) 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 { if err != nil {
return err return err
} }
@ -226,7 +232,11 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, ec2conn *
} }
for _, remove := range toBeDeleted { for _, remove := range toBeDeleted {
// Delete old Acl // 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 { if err != nil {
return fmt.Errorf("Error deleting %s entry: %s", entryType, err) 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 { for _, add := range toBeCreated {
// Add new Acl entry // 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 { if err != nil {
return fmt.Errorf("Error creating %s entry: %s", entryType, err) 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()) log.Printf("[INFO] Deleting Network Acl: %s", d.Id())
return resource.Retry(5*time.Minute, func() error { return resource.Retry(5*time.Minute, func() error {
if _, err := ec2conn.DeleteNetworkAcl(d.Id()); err != nil { err := ec2conn.DeleteNetworkACL(&ec2.DeleteNetworkACLRequest{
ec2err := err.(*ec2.Error) NetworkACLID: aws.String(d.Id()),
})
if err != nil {
ec2err := err.(aws.APIError)
switch ec2err.Code { switch ec2err.Code {
case "InvalidNetworkAclID.NotFound": case "InvalidNetworkAclID.NotFound":
return nil return nil
@ -267,7 +288,10 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error
if err != nil { if err != nil {
return fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err) 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} return resource.RetryError{Err: err}
default: default:
// Any other error, we want to quit the retry loop immediately // 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()) return hashcode.String(buf.String())
} }
func getDefaultNetworkAcl(vpc_id string, ec2conn *ec2.EC2) (defaultAcl *ec2.NetworkAcl, err error) { func getDefaultNetworkAcl(vpc_id string, ec2conn *ec2.EC2) (defaultAcl *ec2.NetworkACL, err error) {
filter := ec2.NewFilter() resp, err := ec2conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{
filter.Add("default", "true") NetworkACLIDs: []string{},
filter.Add("vpc-id", vpc_id) Filters: []ec2.Filter{
ec2.Filter{
resp, err := ec2conn.NetworkAcls([]string{}, filter) Name: aws.String("default"),
Values: []string{"true"},
},
ec2.Filter{
Name: aws.String("vpc-id"),
Values: []string{vpc_id},
},
},
})
if err != nil { if err != nil {
return nil, err 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) { func findNetworkAclAssociation(subnetId string, ec2conn *ec2.EC2) (networkAclAssociation *ec2.NetworkACLAssociation, err error) {
filter := ec2.NewFilter() resp, err := ec2conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{
filter.Add("association.subnet-id", subnetId) NetworkACLIDs: []string{},
Filters: []ec2.Filter{
resp, err := ec2conn.NetworkAcls([]string{}, filter) ec2.Filter{
Name: aws.String("association.subnet-id"),
Values: []string{subnetId},
},
},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, association := range resp.NetworkAcls[0].AssociationSet { for _, association := range resp.NetworkACLs[0].Associations {
if association.SubnetId == subnetId { if *association.SubnetID == subnetId {
return &association, nil return &association, nil
} }
} }

View File

@ -4,15 +4,16 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
// "github.com/hashicorp/terraform/helper/hashcode" // "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
// "github.com/hashicorp/terraform/helper/schema" // "github.com/hashicorp/terraform/helper/schema"
) )
func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) { func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) {
var networkAcl ec2.NetworkAcl var networkAcl ec2.NetworkACL
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -24,29 +25,29 @@ func TestAccAWSNetworkAclsWithEgressAndIngressRules(t *testing.T) {
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckAWSNetworkAclExists("aws_network_acl.bar", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.bar", &networkAcl),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.580214135.protocol", "tcp"), "aws_network_acl.bar", "ingress.3409203205.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.580214135.rule_no", "1"), "aws_network_acl.bar", "ingress.3409203205.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.580214135.from_port", "80"), "aws_network_acl.bar", "ingress.3409203205.from_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.580214135.to_port", "80"), "aws_network_acl.bar", "ingress.3409203205.to_port", "80"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "ingress.580214135.action", "allow"), "aws_network_acl.bar", "ingress.3409203205.action", "allow"),
resource.TestCheckResourceAttr( 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( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.1730430240.protocol", "tcp"), "aws_network_acl.bar", "egress.2579689292.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.1730430240.rule_no", "2"), "aws_network_acl.bar", "egress.2579689292.rule_no", "2"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.1730430240.from_port", "443"), "aws_network_acl.bar", "egress.2579689292.from_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.bar", "egress.1730430240.to_port", "443"), "aws_network_acl.bar", "egress.2579689292.to_port", "443"),
resource.TestCheckResourceAttr( 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( 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) { func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) {
var networkAcl ec2.NetworkAcl var networkAcl ec2.NetworkACL
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -67,17 +68,17 @@ func TestAccAWSNetworkAclsOnlyIngressRules(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
// testAccCheckSubnetAssociation("aws_network_acl.foos", "aws_subnet.blob"), // testAccCheckSubnetAssociation("aws_network_acl.foos", "aws_subnet.blob"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), "aws_network_acl.foos", "ingress.2750166237.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), "aws_network_acl.foos", "ingress.2750166237.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.from_port", "0"), "aws_network_acl.foos", "ingress.2750166237.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.to_port", "22"), "aws_network_acl.foos", "ingress.2750166237.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.action", "deny"), "aws_network_acl.foos", "ingress.2750166237.action", "deny"),
resource.TestCheckResourceAttr( 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) { func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) {
var networkAcl ec2.NetworkAcl var networkAcl ec2.NetworkACL
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -98,21 +99,21 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
testIngressRuleLength(&networkAcl, 2), testIngressRuleLength(&networkAcl, 2),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), "aws_network_acl.foos", "ingress.37211640.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), "aws_network_acl.foos", "ingress.37211640.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.from_port", "0"), "aws_network_acl.foos", "ingress.37211640.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.to_port", "22"), "aws_network_acl.foos", "ingress.37211640.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.action", "deny"), "aws_network_acl.foos", "ingress.37211640.action", "deny"),
resource.TestCheckResourceAttr( 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( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.2438803013.from_port", "443"), "aws_network_acl.foos", "ingress.2750166237.from_port", "443"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.2438803013.rule_no", "2"), "aws_network_acl.foos", "ingress.2750166237.rule_no", "2"),
), ),
}, },
resource.TestStep{ resource.TestStep{
@ -121,17 +122,17 @@ func TestAccAWSNetworkAclsOnlyIngressRulesChange(t *testing.T) {
testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl), testAccCheckAWSNetworkAclExists("aws_network_acl.foos", &networkAcl),
testIngressRuleLength(&networkAcl, 1), testIngressRuleLength(&networkAcl, 1),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.protocol", "tcp"), "aws_network_acl.foos", "ingress.37211640.protocol", "tcp"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.rule_no", "1"), "aws_network_acl.foos", "ingress.37211640.rule_no", "1"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.from_port", "0"), "aws_network_acl.foos", "ingress.37211640.from_port", "0"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.to_port", "22"), "aws_network_acl.foos", "ingress.37211640.to_port", "22"),
resource.TestCheckResourceAttr( resource.TestCheckResourceAttr(
"aws_network_acl.foos", "ingress.3697634361.action", "deny"), "aws_network_acl.foos", "ingress.37211640.action", "deny"),
resource.TestCheckResourceAttr( 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) { func TestAccAWSNetworkAclsOnlyEgressRules(t *testing.T) {
var networkAcl ec2.NetworkAcl var networkAcl ec2.NetworkACL
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, 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{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -191,16 +192,18 @@ func testAccCheckAWSNetworkAclDestroy(s *terraform.State) error {
} }
// Retrieve the network acl // 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 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 fmt.Errorf("Network Acl (%s) still exists.", rs.Primary.ID)
} }
return nil return nil
} }
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -213,7 +216,7 @@ func testAccCheckAWSNetworkAclDestroy(s *terraform.State) error {
return nil 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 { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n] rs, ok := s.RootModule().Resources[n]
if !ok { if !ok {
@ -225,13 +228,15 @@ func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkAcl) resou
} }
conn := testAccProvider.Meta().(*AWSClient).ec2conn 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 { if err != nil {
return err return err
} }
if len(resp.NetworkAcls) > 0 && resp.NetworkAcls[0].NetworkAclId == rs.Primary.ID { if len(resp.NetworkACLs) > 0 && *resp.NetworkACLs[0].NetworkACLID == rs.Primary.ID {
*networkAcl = resp.NetworkAcls[0] *networkAcl = resp.NetworkACLs[0]
return nil 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 { return func(s *terraform.State) error {
var ingressEntries []ec2.NetworkAclEntry var ingressEntries []ec2.NetworkACLEntry
for _, e := range networkAcl.EntrySet { for _, e := range networkAcl.Entries {
if e.Egress == false { if *e.Egress == false {
ingressEntries = append(ingressEntries, e) ingressEntries = append(ingressEntries, e)
} }
} }
@ -262,20 +267,25 @@ func testAccCheckSubnetIsAssociatedWithAcl(acl string, sub string) resource.Test
subnet := s.RootModule().Resources[sub] subnet := s.RootModule().Resources[sub]
conn := testAccProvider.Meta().(*AWSClient).ec2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
filter := ec2.NewFilter() resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{
filter.Add("association.subnet-id", subnet.Primary.ID) NetworkACLIDs: []string{networkAcl.Primary.ID},
resp, err := conn.NetworkAcls([]string{networkAcl.Primary.ID}, filter) Filters: []ec2.Filter{
ec2.Filter{
Name: aws.String("association.subnet-id"),
Values: []string{subnet.Primary.ID},
},
},
})
if err != nil { if err != nil {
return err return err
} }
if len(resp.NetworkAcls) > 0 { if len(resp.NetworkACLs) > 0 {
return nil return nil
} }
r, _ := conn.NetworkAcls([]string{}, ec2.NewFilter()) // r, _ := conn.NetworkACLs([]string{}, ec2.NewFilter())
fmt.Printf("\n\nall acls\n %#v\n\n", r.NetworkAcls) // fmt.Printf("\n\nall acls\n %#v\n\n", r.NetworkAcls)
conn.NetworkAcls([]string{}, filter) // conn.NetworkAcls([]string{}, filter)
return fmt.Errorf("Network Acl %s is not associated with subnet %s", acl, sub) 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] subnet := s.RootModule().Resources[subnet]
conn := testAccProvider.Meta().(*AWSClient).ec2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
filter := ec2.NewFilter() resp, err := conn.DescribeNetworkACLs(&ec2.DescribeNetworkACLsRequest{
filter.Add("association.subnet-id", subnet.Primary.ID) NetworkACLIDs: []string{networkAcl.Primary.ID},
resp, err := conn.NetworkAcls([]string{networkAcl.Primary.ID}, filter) Filters: []ec2.Filter{
ec2.Filter{
Name: aws.String("association.subnet-id"),
Values: []string{subnet.Primary.ID},
},
},
})
if err != nil { if err != nil {
return err 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 fmt.Errorf("Network Acl %s is still associated with subnet %s", acl, subnet)
} }
return nil return nil

View File

@ -138,7 +138,7 @@ func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) er
Delay: 30 * time.Second, Delay: 30 * time.Second,
Pending: []string{"PENDING"}, Pending: []string{"PENDING"},
Target: "INSYNC", Target: "INSYNC",
Timeout: 10 * time.Minute, Timeout: 30 * time.Minute,
MinTimeout: 5 * time.Second, MinTimeout: 5 * time.Second,
Refresh: func() (result interface{}, state string, err error) { Refresh: func() (result interface{}, state string, err error) {
changeRequest := &route53.GetChangeRequest{ changeRequest := &route53.GetChangeRequest{

View File

@ -6,10 +6,11 @@ import (
"log" "log"
"time" "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/hashcode"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsRouteTable() *schema.Resource { func resourceAwsRouteTable() *schema.Resource {
@ -64,8 +65,8 @@ func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
// Create the routing table // Create the routing table
createOpts := &ec2.CreateRouteTable{ createOpts := &ec2.CreateRouteTableRequest{
VpcId: d.Get("vpc_id").(string), VPCID: aws.String(d.Get("vpc_id").(string)),
} }
log.Printf("[DEBUG] RouteTable create config: %#v", createOpts) 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 // Get the ID and store it
rt := &resp.RouteTable rt := resp.RouteTable
d.SetId(rt.RouteTableId) d.SetId(*rt.RouteTableID)
log.Printf("[INFO] Route Table ID: %s", d.Id()) log.Printf("[INFO] Route Table ID: %s", d.Id())
// Wait for the route table to become available // Wait for the route table to become available
@ -110,27 +111,35 @@ func resourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error {
} }
rt := rtRaw.(*ec2.RouteTable) 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 // Create an empty schema.Set to hold all routes
route := &schema.Set{F: resourceAwsRouteTableHash} route := &schema.Set{F: resourceAwsRouteTableHash}
// Loop through the routes and add them to the set // Loop through the routes and add them to the set
for _, r := range rt.Routes { for _, r := range rt.Routes {
if r.GatewayId == "local" { if r.GatewayID != nil && *r.GatewayID == "local" {
continue continue
} }
if r.Origin == "EnableVgwRoutePropagation" { if r.Origin != nil && *r.Origin == "EnableVgwRoutePropagation" {
continue continue
} }
m := make(map[string]interface{}) m := make(map[string]interface{})
m["cidr_block"] = r.DestinationCidrBlock
m["gateway_id"] = r.GatewayId if r.DestinationCIDRBlock != nil {
m["instance_id"] = r.InstanceId m["cidr_block"] = *r.DestinationCIDRBlock
m["vpc_peering_connection_id"] = r.VpcPeeringConnectionId }
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) route.Add(m)
} }
@ -159,8 +168,10 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error
log.Printf( log.Printf(
"[INFO] Deleting route from %s: %s", "[INFO] Deleting route from %s: %s",
d.Id(), m["cidr_block"].(string)) d.Id(), m["cidr_block"].(string))
_, err := ec2conn.DeleteRoute( err := ec2conn.DeleteRoute(&ec2.DeleteRouteRequest{
d.Id(), m["cidr_block"].(string)) RouteTableID: aws.String(d.Id()),
DestinationCIDRBlock: aws.String(m["cidr_block"].(string)),
})
if err != nil { if err != nil {
return err return err
} }
@ -174,17 +185,16 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error
for _, route := range nrs.List() { for _, route := range nrs.List() {
m := route.(map[string]interface{}) m := route.(map[string]interface{})
opts := ec2.CreateRoute{ opts := ec2.CreateRouteRequest{
RouteTableId: d.Id(), RouteTableID: aws.String(d.Id()),
DestinationCidrBlock: m["cidr_block"].(string), DestinationCIDRBlock: aws.String(m["cidr_block"].(string)),
GatewayId: m["gateway_id"].(string), GatewayID: aws.String(m["gateway_id"].(string)),
InstanceId: m["instance_id"].(string), InstanceID: aws.String(m["instance_id"].(string)),
VpcPeeringConnectionId: m["vpc_peering_connection_id"].(string), VPCPeeringConnectionID: aws.String(m["vpc_peering_connection_id"].(string)),
} }
log.Printf("[INFO] Creating route for %s: %#v", d.Id(), opts) log.Printf("[INFO] Creating route for %s: %#v", d.Id(), opts)
_, err := ec2conn.CreateRoute(&opts) if err := ec2conn.CreateRoute(&opts); err != nil {
if err != nil {
return err return err
} }
@ -218,16 +228,22 @@ func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error
// Do all the disassociations // Do all the disassociations
for _, a := range rt.Associations { for _, a := range rt.Associations {
log.Printf("[INFO] Disassociating association: %s", a.AssociationId) log.Printf("[INFO] Disassociating association: %s", *a.RouteTableAssociationID)
if _, err := ec2conn.DisassociateRouteTable(a.AssociationId); err != nil { err := ec2conn.DisassociateRouteTable(&ec2.DisassociateRouteTableRequest{
AssociationID: a.RouteTableAssociationID,
})
if err != nil {
return err return err
} }
} }
// Delete the route table // Delete the route table
log.Printf("[INFO] Deleting Route Table: %s", d.Id()) log.Printf("[INFO] Deleting Route Table: %s", d.Id())
if _, err := ec2conn.DeleteRouteTable(d.Id()); err != nil { err = ec2conn.DeleteRouteTable(&ec2.DeleteRouteTableRequest{
ec2err, ok := err.(*ec2.Error) RouteTableID: aws.String(d.Id()),
})
if err != nil {
ec2err, ok := err.(aws.APIError)
if ok && ec2err.Code == "InvalidRouteTableID.NotFound" { if ok && ec2err.Code == "InvalidRouteTableID.NotFound" {
return nil return nil
} }
@ -279,9 +295,11 @@ func resourceAwsRouteTableHash(v interface{}) int {
// a RouteTable. // a RouteTable.
func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { func resourceAwsRouteTableStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { 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 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 resp = nil
} else { } else {
log.Printf("Error on RouteTableStateRefresh: %s", err) log.Printf("Error on RouteTableStateRefresh: %s", err)

View File

@ -4,8 +4,9 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsRouteTableAssociation() *schema.Resource { func resourceAwsRouteTableAssociation() *schema.Resource {
@ -38,16 +39,17 @@ func resourceAwsRouteTableAssociationCreate(d *schema.ResourceData, meta interfa
d.Get("subnet_id").(string), d.Get("subnet_id").(string),
d.Get("route_table_id").(string)) d.Get("route_table_id").(string))
resp, err := ec2conn.AssociateRouteTable( resp, err := ec2conn.AssociateRouteTable(&ec2.AssociateRouteTableRequest{
d.Get("route_table_id").(string), RouteTableID: aws.String(d.Get("route_table_id").(string)),
d.Get("subnet_id").(string)) SubnetID: aws.String(d.Get("subnet_id").(string)),
})
if err != nil { if err != nil {
return err return err
} }
// Set the ID and return // Set the ID and return
d.SetId(resp.AssociationId) d.SetId(*resp.AssociationID)
log.Printf("[INFO] Association ID: %s", d.Id()) log.Printf("[INFO] Association ID: %s", d.Id())
return nil return nil
@ -70,9 +72,9 @@ func resourceAwsRouteTableAssociationRead(d *schema.ResourceData, meta interface
// Inspect that the association exists // Inspect that the association exists
found := false found := false
for _, a := range rt.Associations { for _, a := range rt.Associations {
if a.AssociationId == d.Id() { if *a.RouteTableAssociationID == d.Id() {
found = true found = true
d.Set("subnet_id", a.SubnetId) d.Set("subnet_id", *a.SubnetID)
break break
} }
} }
@ -93,12 +95,14 @@ func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interfa
d.Get("subnet_id").(string), d.Get("subnet_id").(string),
d.Get("route_table_id").(string)) d.Get("route_table_id").(string))
resp, err := ec2conn.ReassociateRouteTable( req := &ec2.ReplaceRouteTableAssociationRequest{
d.Id(), AssociationID: aws.String(d.Id()),
d.Get("route_table_id").(string)) RouteTableID: aws.String(d.Get("route_table_id").(string)),
}
resp, err := ec2conn.ReplaceRouteTableAssociation(req)
if err != nil { if err != nil {
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if ok && ec2err.Code == "InvalidAssociationID.NotFound" { if ok && ec2err.Code == "InvalidAssociationID.NotFound" {
// Not found, so just create a new one // Not found, so just create a new one
return resourceAwsRouteTableAssociationCreate(d, meta) return resourceAwsRouteTableAssociationCreate(d, meta)
@ -108,7 +112,7 @@ func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interfa
} }
// Update the ID // Update the ID
d.SetId(resp.AssociationId) d.SetId(*resp.NewAssociationID)
log.Printf("[INFO] Association ID: %s", d.Id()) log.Printf("[INFO] Association ID: %s", d.Id())
return nil return nil
@ -118,8 +122,11 @@ func resourceAwsRouteTableAssociationDelete(d *schema.ResourceData, meta interfa
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
log.Printf("[INFO] Deleting route table association: %s", d.Id()) log.Printf("[INFO] Deleting route table association: %s", d.Id())
if _, err := ec2conn.DisassociateRouteTable(d.Id()); err != nil { err := ec2conn.DisassociateRouteTable(&ec2.DisassociateRouteTableRequest{
ec2err, ok := err.(*ec2.Error) AssociationID: aws.String(d.Id()),
})
if err != nil {
ec2err, ok := err.(aws.APIError)
if ok && ec2err.Code == "InvalidAssociationID.NotFound" { if ok && ec2err.Code == "InvalidAssociationID.NotFound" {
return nil return nil
} }

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"testing" "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/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestAccAWSRouteTableAssociation(t *testing.T) { func TestAccAWSRouteTableAssociation(t *testing.T) {
@ -45,11 +46,12 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error {
} }
// Try to find the resource // Try to find the resource
resp, err := conn.DescribeRouteTables( resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
[]string{rs.Primary.Attributes["route_table_Id"]}, ec2.NewFilter()) RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]},
})
if err != nil { if err != nil {
// Verify the error is what we want // Verify the error is what we want
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -62,7 +64,7 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error {
rt := resp.RouteTables[0] rt := resp.RouteTables[0]
if len(rt.Associations) > 0 { if len(rt.Associations) > 0 {
return fmt.Errorf( 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 conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables( resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
[]string{rs.Primary.Attributes["route_table_id"]}, ec2.NewFilter()) RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]},
})
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"testing" "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/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestAccAWSRouteTable_normal(t *testing.T) { func TestAccAWSRouteTable_normal(t *testing.T) {
@ -19,7 +20,7 @@ func TestAccAWSRouteTable_normal(t *testing.T) {
routes := make(map[string]ec2.Route) routes := make(map[string]ec2.Route)
for _, r := range v.Routes { for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r routes[*r.DestinationCIDRBlock] = r
} }
if _, ok := routes["10.1.0.0/16"]; !ok { 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) routes := make(map[string]ec2.Route)
for _, r := range v.Routes { for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r routes[*r.DestinationCIDRBlock] = r
} }
if _, ok := routes["10.1.0.0/16"]; !ok { 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) routes := make(map[string]ec2.Route)
for _, r := range v.Routes { for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r routes[*r.DestinationCIDRBlock] = r
} }
if _, ok := routes["10.1.0.0/16"]; !ok { if _, ok := routes["10.1.0.0/16"]; !ok {
@ -158,8 +159,9 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error {
} }
// Try to find the resource // Try to find the resource
resp, err := conn.DescribeRouteTables( resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
[]string{rs.Primary.ID}, ec2.NewFilter()) RouteTableIDs: []string{rs.Primary.ID},
})
if err == nil { if err == nil {
if len(resp.RouteTables) > 0 { if len(resp.RouteTables) > 0 {
return fmt.Errorf("still exist.") return fmt.Errorf("still exist.")
@ -169,7 +171,7 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error {
} }
// Verify the error is what we want // Verify the error is what we want
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -193,8 +195,9 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
} }
conn := testAccProvider.Meta().(*AWSClient).ec2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables( resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
[]string{rs.Primary.ID}, ec2.NewFilter()) RouteTableIDs: []string{rs.Primary.ID},
})
if err != nil { if err != nil {
return err 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 var v ec2.RouteTable
testCheck := func(*terraform.State) error { testCheck := func(*terraform.State) error {
@ -218,7 +224,7 @@ func TestAccAWSRouteTable_vpcPeering(t *testing.T) {
routes := make(map[string]ec2.Route) routes := make(map[string]ec2.Route)
for _, r := range v.Routes { for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r routes[*r.DestinationCIDRBlock] = r
} }
if _, ok := routes["10.1.0.0/16"]; !ok { 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 = ` const testAccRouteTableVpcPeeringConfig = `
resource "aws_vpc" "foo" { resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16" cidr_block = "10.1.0.0/16"
@ -359,7 +368,7 @@ resource "aws_route_table" "foo" {
route { route {
cidr_block = "10.2.0.0/16" cidr_block = "10.2.0.0/16"
vpc_peering_connection_id = "vpc-12345" vpc_peering_connection_id = "pcx-12345"
} }
} }
` `

View File

@ -7,10 +7,11 @@ import (
"sort" "sort"
"time" "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/hashcode"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsSecurityGroup() *schema.Resource { func resourceAwsSecurityGroup() *schema.Resource {
@ -143,16 +144,16 @@ func resourceAwsSecurityGroup() *schema.Resource {
func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
securityGroupOpts := ec2.SecurityGroup{ securityGroupOpts := &ec2.CreateSecurityGroupRequest{
Name: d.Get("name").(string), GroupName: aws.String(d.Get("name").(string)),
} }
if v := d.Get("vpc_id"); v != nil { 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 { if v := d.Get("description"); v != nil {
securityGroupOpts.Description = v.(string) securityGroupOpts.Description = aws.String(v.(string))
} }
log.Printf( log.Printf(
@ -162,7 +163,7 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error creating Security Group: %s", err) 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()) log.Printf("[INFO] Security Group ID: %s", d.Id())
@ -197,19 +198,18 @@ func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) erro
return nil return nil
} }
sg := sgRaw.(*ec2.SecurityGroupInfo) sg := sgRaw.(ec2.SecurityGroup)
ingressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPerms) ingressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermissions)
egressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermsEgress) egressRules := resourceAwsSecurityGroupIPPermGather(d, sg.IPPermissionsEgress)
d.Set("description", sg.Description) d.Set("description", sg.Description)
d.Set("name", sg.Name) d.Set("name", sg.GroupName)
d.Set("vpc_id", sg.VpcId) d.Set("vpc_id", sg.VPCID)
d.Set("owner_id", sg.OwnerId) d.Set("owner_id", sg.OwnerID)
d.Set("ingress", ingressRules) d.Set("ingress", ingressRules)
d.Set("egress", egressRules) d.Set("egress", egressRules)
d.Set("tags", tagsToMap(sg.Tags)) d.Set("tags", tagsToMap(sg.Tags))
return nil return nil
} }
@ -224,7 +224,8 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er
d.SetId("") d.SetId("")
return nil return nil
} }
group := sgRaw.(*ec2.SecurityGroupInfo).SecurityGroup
group := sgRaw.(ec2.SecurityGroup)
err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group) err = resourceAwsSecurityGroupUpdateRules(d, "ingress", meta, group)
if err != nil { if err != nil {
@ -253,9 +254,11 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er
log.Printf("[DEBUG] Security Group destroy: %v", d.Id()) log.Printf("[DEBUG] Security Group destroy: %v", d.Id())
return resource.Retry(5*time.Minute, func() error { 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 { if err != nil {
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -313,34 +316,45 @@ func resourceAwsSecurityGroupRuleHash(v interface{}) int {
return hashcode.String(buf.String()) 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{}) ruleMap := make(map[string]map[string]interface{})
for _, perm := range permissions { 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] m, ok := ruleMap[k]
if !ok { if !ok {
m = make(map[string]interface{}) m = make(map[string]interface{})
ruleMap[k] = m ruleMap[k] = m
} }
m["from_port"] = perm.FromPort m["from_port"] = fromPort
m["to_port"] = perm.ToPort m["to_port"] = toPort
m["protocol"] = perm.Protocol m["protocol"] = *perm.IPProtocol
if len(perm.SourceIPs) > 0 { if len(perm.IPRanges) > 0 {
raw, ok := m["cidr_blocks"] raw, ok := m["cidr_blocks"]
if !ok { if !ok {
raw = make([]string, 0, len(perm.SourceIPs)) raw = make([]string, 0, len(perm.IPRanges))
} }
list := raw.([]string) list := raw.([]string)
list = append(list, perm.SourceIPs...) for _, ip := range perm.IPRanges {
list = append(list, *ip.CIDRIP)
}
m["cidr_blocks"] = list m["cidr_blocks"] = list
} }
var groups []string var groups []string
if len(perm.SourceGroups) > 0 { if len(perm.UserIDGroupPairs) > 0 {
groups = flattenSecurityGroups(perm.SourceGroups) groups = flattenSecurityGroups(perm.UserIDGroupPairs)
} }
for i, id := range groups { for i, id := range groups {
if id == d.Id() { if id == d.Id() {
@ -364,7 +378,6 @@ func resourceAwsSecurityGroupIPPermGather(d *schema.ResourceData, permissions []
for _, m := range ruleMap { for _, m := range ruleMap {
rules = append(rules, m) rules = append(rules, m)
} }
return rules return rules
} }
@ -398,32 +411,51 @@ func resourceAwsSecurityGroupUpdateRules(
if len(remove) > 0 || len(add) > 0 { if len(remove) > 0 || len(add) > 0 {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
var err error
if len(remove) > 0 { if len(remove) > 0 {
// Revoke the old rules log.Printf("[DEBUG] Revoking security group %#v %s rule: %#v",
revoke := ec2conn.RevokeSecurityGroup group, ruleset, remove)
if ruleset == "egress" { 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", if err != nil {
group, ruleset, remove)
if _, err := revoke(group, remove); err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error revoking security group %s rules: %s", "Error authorizing security group %s rules: %s",
ruleset, err) ruleset, err)
} }
} }
if len(add) > 0 { if len(add) > 0 {
log.Printf("[DEBUG] Authorizing security group %#v %s rule: %#v",
group, ruleset, add)
// Authorize the new rules // Authorize the new rules
authorize := ec2conn.AuthorizeSecurityGroup
if ruleset == "egress" { 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", if err != nil {
group, ruleset, add)
if _, err := authorize(group, add); err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error authorizing security group %s rules: %s", "Error authorizing security group %s rules: %s",
ruleset, err) ruleset, err)
@ -431,7 +463,6 @@ func resourceAwsSecurityGroupUpdateRules(
} }
} }
} }
return nil return nil
} }
@ -439,10 +470,12 @@ func resourceAwsSecurityGroupUpdateRules(
// a security group. // a security group.
func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { return func() (interface{}, string, error) {
sgs := []ec2.SecurityGroup{ec2.SecurityGroup{Id: id}} req := &ec2.DescribeSecurityGroupsRequest{
resp, err := conn.SecurityGroups(sgs, nil) GroupIDs: []string{id},
}
resp, err := conn.DescribeSecurityGroups(req)
if err != nil { if err != nil {
if ec2err, ok := err.(*ec2.Error); ok { if ec2err, ok := err.(aws.APIError); ok {
if ec2err.Code == "InvalidSecurityGroupID.NotFound" || if ec2err.Code == "InvalidSecurityGroupID.NotFound" ||
ec2err.Code == "InvalidGroup.NotFound" { ec2err.Code == "InvalidGroup.NotFound" {
resp = nil resp = nil
@ -460,7 +493,7 @@ func SGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return nil, "", nil return nil, "", nil
} }
group := &resp.Groups[0] group := resp.SecurityGroups[0]
return group, "exists", nil return group, "exists", nil
} }
} }

View File

@ -2,16 +2,18 @@ package aws
import ( import (
"fmt" "fmt"
"log"
"reflect" "reflect"
"testing" "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/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestAccAWSSecurityGroup_normal(t *testing.T) { func TestAccAWSSecurityGroup_normal(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -44,7 +46,7 @@ func TestAccAWSSecurityGroup_normal(t *testing.T) {
} }
func TestAccAWSSecurityGroup_self(t *testing.T) { func TestAccAWSSecurityGroup_self(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
checkSelf := func(s *terraform.State) (err error) { checkSelf := func(s *terraform.State) (err error) {
defer func() { 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) return fmt.Errorf("bad: %#v", group)
} }
@ -89,10 +91,10 @@ func TestAccAWSSecurityGroup_self(t *testing.T) {
} }
func TestAccAWSSecurityGroup_vpc(t *testing.T) { func TestAccAWSSecurityGroup_vpc(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
testCheck := func(*terraform.State) error { testCheck := func(*terraform.State) error {
if group.VpcId == "" { if *group.VPCID == "" {
return fmt.Errorf("should have vpc ID") return fmt.Errorf("should have vpc ID")
} }
@ -141,7 +143,7 @@ func TestAccAWSSecurityGroup_vpc(t *testing.T) {
} }
func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) { func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -159,7 +161,7 @@ func TestAccAWSSecurityGroup_MultiIngress(t *testing.T) {
} }
func TestAccAWSSecurityGroup_Change(t *testing.T) { func TestAccAWSSecurityGroup_Change(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
@ -191,23 +193,20 @@ func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error {
continue continue
} }
sgs := []ec2.SecurityGroup{
ec2.SecurityGroup{
Id: rs.Primary.ID,
},
}
// Retrieve our group // 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 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 fmt.Errorf("Security Group (%s) still exists.", rs.Primary.ID)
} }
return nil return nil
} }
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -220,7 +219,7 @@ func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error {
return nil 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 { return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n] rs, ok := s.RootModule().Resources[n]
if !ok { if !ok {
@ -232,19 +231,18 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo)
} }
conn := testAccProvider.Meta().(*AWSClient).ec2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
sgs := []ec2.SecurityGroup{ req := &ec2.DescribeSecurityGroupsRequest{
ec2.SecurityGroup{ GroupIDs: []string{rs.Primary.ID},
Id: rs.Primary.ID,
},
} }
resp, err := conn.SecurityGroups(sgs, nil) resp, err := conn.DescribeSecurityGroups(req)
if err != nil { if err != nil {
return err 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 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 { return func(s *terraform.State) error {
p := ec2.IPPerm{ p := ec2.IPPermission{
FromPort: 80, FromPort: aws.Integer(80),
ToPort: 8000, ToPort: aws.Integer(8000),
Protocol: "tcp", IPProtocol: aws.String("tcp"),
SourceIPs: []string{"10.0.0.0/8"}, IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("10.0.0.0/8")}},
} }
if group.Name != "terraform_acceptance_test_example" { if *group.GroupName != "terraform_acceptance_test_example" {
return fmt.Errorf("Bad name: %s", group.Name) return fmt.Errorf("Bad name: %s", *group.GroupName)
} }
if group.Description != "Used in the terraform acceptance tests" { if *group.Description != "Used in the terraform acceptance tests" {
return fmt.Errorf("Bad description: %s", group.Description) return fmt.Errorf("Bad description: %s", *group.Description)
} }
if len(group.IPPerms) == 0 { if len(group.IPPermissions) == 0 {
return fmt.Errorf("No IPPerms") return fmt.Errorf("No IPPerms")
} }
// Compare our ingress // Compare our ingress
if !reflect.DeepEqual(group.IPPerms[0], p) { if !reflect.DeepEqual(group.IPPermissions[0], p) {
return fmt.Errorf( return fmt.Errorf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n", "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
group.IPPerms[0], group.IPPermissions[0],
p) p)
} }
@ -287,7 +285,7 @@ func testAccCheckAWSSecurityGroupAttributes(group *ec2.SecurityGroupInfo) resour
} }
func TestAccAWSSecurityGroup_tags(t *testing.T) { func TestAccAWSSecurityGroup_tags(t *testing.T) {
var group ec2.SecurityGroupInfo var group ec2.SecurityGroup
resource.Test(t, resource.TestCase{ resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) }, 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 { return func(s *terraform.State) error {
p := []ec2.IPPerm{ p := []ec2.IPPermission{
ec2.IPPerm{ ec2.IPPermission{
FromPort: 80, FromPort: aws.Integer(80),
ToPort: 9000, ToPort: aws.Integer(9000),
Protocol: "tcp", IPProtocol: aws.String("tcp"),
SourceIPs: []string{"10.0.0.0/8"}, IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("10.0.0.0/8")}},
}, },
ec2.IPPerm{ ec2.IPPermission{
FromPort: 80, FromPort: aws.Integer(80),
ToPort: 8000, ToPort: aws.Integer(8000),
Protocol: "tcp", IPProtocol: aws.String("tcp"),
SourceIPs: []string{"0.0.0.0/0", "10.0.0.0/8"}, 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" { if *group.GroupName != "terraform_acceptance_test_example" {
return fmt.Errorf("Bad name: %s", group.Name) return fmt.Errorf("Bad name: %s", *group.GroupName)
} }
if group.Description != "Used in the terraform acceptance tests" { if *group.Description != "Used in the terraform acceptance tests" {
return fmt.Errorf("Bad description: %s", group.Description) return fmt.Errorf("Bad description: %s", *group.Description)
} }
// Compare our ingress // Compare our ingress
if len(group.IPPerms) != 2 { if len(group.IPPermissions) != 2 {
return fmt.Errorf( return fmt.Errorf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n", "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
group.IPPerms, group.IPPermissions,
p) p)
} }
if group.IPPerms[0].ToPort == 8000 { if *group.IPPermissions[0].ToPort == 8000 {
group.IPPerms[1], group.IPPerms[0] = group.IPPermissions[1], group.IPPermissions[0] =
group.IPPerms[0], group.IPPerms[1] group.IPPermissions[0], group.IPPermissions[1]
} }
if !reflect.DeepEqual(group.IPPerms, p) { if !reflect.DeepEqual(group.IPPermissions, p) {
return fmt.Errorf( return fmt.Errorf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n", "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
group.IPPerms, group.IPPermissions,
p) p)
} }

View File

@ -51,7 +51,7 @@ func resourceAwsSubnet() *schema.Resource {
} }
func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
createOpts := &ec2.CreateSubnetRequest{ createOpts := &ec2.CreateSubnetRequest{
AvailabilityZone: aws.String(d.Get("availability_zone").(string)), 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 { func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{ resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{
SubnetIDs: []string{d.Id()}, SubnetIDs: []string{d.Id()},
@ -115,17 +115,17 @@ func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("availability_zone", subnet.AvailabilityZone) d.Set("availability_zone", subnet.AvailabilityZone)
d.Set("cidr_block", subnet.CIDRBlock) d.Set("cidr_block", subnet.CIDRBlock)
d.Set("map_public_ip_on_launch", subnet.MapPublicIPOnLaunch) d.Set("map_public_ip_on_launch", subnet.MapPublicIPOnLaunch)
d.Set("tags", tagsToMapSDK(subnet.Tags)) d.Set("tags", tagsToMap(subnet.Tags))
return nil return nil
} }
func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error { func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).awsEC2conn ec2conn := meta.(*AWSClient).ec2conn
d.Partial(true) d.Partial(true)
if err := setTagsSDK(ec2conn, d); err != nil { if err := setTags(ec2conn, d); err != nil {
return err return err
} else { } else {
d.SetPartial("tags") d.SetPartial("tags")
@ -154,7 +154,7 @@ func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
} }
func resourceAwsSubnetDelete(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()) log.Printf("[INFO] Deleting subnet: %s", d.Id())

View File

@ -43,7 +43,7 @@ func TestAccAWSSubnet(t *testing.T) {
} }
func testAccCheckSubnetDestroy(s *terraform.State) error { func testAccCheckSubnetDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources { for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_subnet" { 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") return fmt.Errorf("No ID is set")
} }
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{ resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsRequest{
SubnetIDs: []string{rs.Primary.ID}, SubnetIDs: []string{rs.Primary.ID},
}) })

View File

@ -5,9 +5,10 @@ import (
"log" "log"
"time" "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/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsVpc() *schema.Resource { func resourceAwsVpc() *schema.Resource {
@ -64,22 +65,25 @@ func resourceAwsVpc() *schema.Resource {
func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
instance_tenancy := "default"
// Create the VPC if v, ok := d.GetOk("instance_tenancy"); ok {
createOpts := &ec2.CreateVpc{ instance_tenancy = v.(string)
CidrBlock: d.Get("cidr_block").(string),
InstanceTenancy: d.Get("instance_tenancy").(string),
} }
log.Printf("[DEBUG] VPC create config: %#v", createOpts) // Create the VPC
vpcResp, err := ec2conn.CreateVpc(createOpts) 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 { if err != nil {
return fmt.Errorf("Error creating VPC: %s", err) return fmt.Errorf("Error creating VPC: %s", err)
} }
// Get the ID and store it // Get the ID and store it
vpc := &vpcResp.VPC vpc := vpcResp.VPC
log.Printf("[INFO] VPC ID: %s", vpc.VpcId) d.SetId(*vpc.VPCID)
d.SetId(vpc.VpcId) log.Printf("[INFO] VPC ID: %s", d.Id())
// Set partial mode and say that we setup the cidr block // Set partial mode and say that we setup the cidr block
d.Partial(true) d.Partial(true)
@ -120,34 +124,53 @@ func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error {
// VPC stuff // VPC stuff
vpc := vpcRaw.(*ec2.VPC) vpc := vpcRaw.(*ec2.VPC)
d.Set("cidr_block", vpc.CidrBlock) vpcid := d.Id()
d.Set("cidr_block", vpc.CIDRBlock)
// Tags // Tags
d.Set("tags", tagsToMap(vpc.Tags)) d.Set("tags", tagsToMap(vpc.Tags))
// Attributes // 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 { if err != nil {
return err return err
} }
d.Set("enable_dns_support", resp.EnableDnsSupport) d.Set("enable_dns_support", *resp.EnableDNSSupport)
attribute = "enableDnsHostnames"
resp, err = ec2conn.VpcAttribute(d.Id(), "enableDnsHostnames") DescribeAttrOpts = &ec2.DescribeVPCAttributeRequest{
Attribute: &attribute,
VPCID: &vpcid,
}
resp, err = ec2conn.DescribeVPCAttribute(DescribeAttrOpts)
if err != nil { if err != nil {
return err return err
} }
d.Set("enable_dns_hostnames", resp.EnableDnsHostnames) d.Set("enable_dns_hostnames", *resp.EnableDNSHostnames)
// Get the main routing table for this VPC // Get the main routing table for this VPC
filter := ec2.NewFilter() // Really Ugly need to make this better - rmenn
filter.Add("association.main", "true") filter1 := &ec2.Filter{
filter.Add("vpc-id", d.Id()) Name: aws.String("association.main"),
routeResp, err := ec2conn.DescribeRouteTables(nil, filter) 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 { if err != nil {
return err return err
} }
if v := routeResp.RouteTables; len(v) > 0 { 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) resourceAwsVpcSetDefaultNetworkAcl(ec2conn, d)
@ -161,16 +184,20 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error {
// Turn on partial mode // Turn on partial mode
d.Partial(true) d.Partial(true)
vpcid := d.Id()
modifyOpts := &ec2.ModifyVPCAttributeRequest{
VPCID: &vpcid,
}
if d.HasChange("enable_dns_hostnames") { if d.HasChange("enable_dns_hostnames") {
options := new(ec2.ModifyVpcAttribute) val := d.Get("enable_dns_hostnames").(bool)
options.EnableDnsHostnames = d.Get("enable_dns_hostnames").(bool) modifyOpts.EnableDNSHostnames = &ec2.AttributeBooleanValue{
options.SetEnableDnsHostnames = true Value: &val,
}
log.Printf( log.Printf(
"[INFO] Modifying enable_dns_hostnames vpc attribute for %s: %#v", "[INFO] Modifying enable_dns_hostnames vpc attribute for %s: %#v",
d.Id(), options) d.Id(), modifyOpts)
if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { if err := ec2conn.ModifyVPCAttribute(modifyOpts); err != nil {
return err return err
} }
@ -178,14 +205,15 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error {
} }
if d.HasChange("enable_dns_support") { if d.HasChange("enable_dns_support") {
options := new(ec2.ModifyVpcAttribute) val := d.Get("enable_dns_hostnames").(bool)
options.EnableDnsSupport = d.Get("enable_dns_support").(bool) modifyOpts.EnableDNSSupport = &ec2.AttributeBooleanValue{
options.SetEnableDnsSupport = true Value: &val,
}
log.Printf( log.Printf(
"[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v",
d.Id(), options) d.Id(), modifyOpts)
if _, err := ec2conn.ModifyVpcAttribute(d.Id(), options); err != nil { if err := ec2conn.ModifyVPCAttribute(modifyOpts); err != nil {
return err return err
} }
@ -204,10 +232,13 @@ func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error {
func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
vpcID := d.Id()
DeleteVpcOpts := &ec2.DeleteVPCRequest{
VPCID: &vpcID,
}
log.Printf("[INFO] Deleting VPC: %s", d.Id()) log.Printf("[INFO] Deleting VPC: %s", d.Id())
if _, err := ec2conn.DeleteVpc(d.Id()); err != nil { if err := ec2conn.DeleteVPC(DeleteVpcOpts); err != nil {
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(*aws.APIError)
if ok && ec2err.Code == "InvalidVpcID.NotFound" { if ok && ec2err.Code == "InvalidVpcID.NotFound" {
return nil return nil
} }
@ -222,9 +253,12 @@ func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error {
// a VPC. // a VPC.
func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { 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 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 resp = nil
} else { } else {
log.Printf("Error on VPCStateRefresh: %s", err) log.Printf("Error on VPCStateRefresh: %s", err)
@ -239,37 +273,53 @@ func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
} }
vpc := &resp.VPCs[0] vpc := &resp.VPCs[0]
return vpc, vpc.State, nil return vpc, *vpc.State, nil
} }
} }
func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error {
filter := ec2.NewFilter() filter1 := &ec2.Filter{
filter.Add("default", "true") Name: aws.String("default"),
filter.Add("vpc-id", d.Id()) Values: []string{("true")},
networkAclResp, err := conn.NetworkAcls(nil, filter) }
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 { if err != nil {
return err return err
} }
if v := networkAclResp.NetworkAcls; len(v) > 0 { if v := networkAclResp.NetworkACLs; len(v) > 0 {
d.Set("default_network_acl_id", v[0].NetworkAclId) d.Set("default_network_acl_id", v[0].NetworkACLID)
} }
return nil return nil
} }
func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error {
filter := ec2.NewFilter() filter1 := &ec2.Filter{
filter.Add("group-name", "default") Name: aws.String("group-name"),
filter.Add("vpc-id", d.Id()) Values: []string{("default")},
securityGroupResp, err := conn.SecurityGroups(nil, filter) }
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 { if err != nil {
return err return err
} }
if v := securityGroupResp.Groups; len(v) > 0 { if v := securityGroupResp.SecurityGroups; len(v) > 0 {
d.Set("default_security_group_id", v[0].Id) d.Set("default_security_group_id", v[0].GroupID)
} }
return nil return nil

View File

@ -5,9 +5,10 @@ import (
"log" "log"
"time" "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/resource"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
func resourceAwsVpcPeeringConnection() *schema.Resource { func resourceAwsVpcPeeringConnection() *schema.Resource {
@ -19,9 +20,10 @@ func resourceAwsVpcPeeringConnection() *schema.Resource {
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"peer_owner_id": &schema.Schema{ "peer_owner_id": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
ForceNew: true, ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("AWS_ACCOUNT_ID", nil),
}, },
"peer_vpc_id": &schema.Schema{ "peer_vpc_id": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
@ -42,20 +44,20 @@ func resourceAwsVpcPeeringCreate(d *schema.ResourceData, meta interface{}) error
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
// Create the vpc peering connection // Create the vpc peering connection
createOpts := &ec2.CreateVpcPeeringConnection{ createOpts := &ec2.CreateVPCPeeringConnectionRequest{
PeerOwnerId: d.Get("peer_owner_id").(string), PeerOwnerID: aws.String(d.Get("peer_owner_id").(string)),
PeerVpcId: d.Get("peer_vpc_id").(string), PeerVPCID: aws.String(d.Get("peer_vpc_id").(string)),
VpcId: d.Get("vpc_id").(string), VPCID: aws.String(d.Get("vpc_id").(string)),
} }
log.Printf("[DEBUG] VpcPeeringCreate create config: %#v", createOpts) log.Printf("[DEBUG] VpcPeeringCreate create config: %#v", createOpts)
resp, err := ec2conn.CreateVpcPeeringConnection(createOpts) resp, err := ec2conn.CreateVPCPeeringConnection(createOpts)
if err != nil { if err != nil {
return fmt.Errorf("Error creating vpc peering connection: %s", err) return fmt.Errorf("Error creating vpc peering connection: %s", err)
} }
// Get the ID and store it // Get the ID and store it
rt := &resp.VpcPeeringConnection rt := resp.VPCPeeringConnection
d.SetId(rt.VpcPeeringConnectionId) d.SetId(*rt.VPCPeeringConnectionID)
log.Printf("[INFO] Vpc Peering Connection ID: %s", d.Id()) log.Printf("[INFO] Vpc Peering Connection ID: %s", d.Id())
// Wait for the vpc peering connection to become available // Wait for the vpc peering connection to become available
@ -88,11 +90,11 @@ func resourceAwsVpcPeeringRead(d *schema.ResourceData, meta interface{}) error {
return nil return nil
} }
pc := pcRaw.(*ec2.VpcPeeringConnection) pc := pcRaw.(*ec2.VPCPeeringConnection)
d.Set("peer_owner_id", pc.AccepterVpcInfo.OwnerId) d.Set("peer_owner_id", pc.AccepterVPCInfo.OwnerID)
d.Set("peer_vpc_id", pc.AccepterVpcInfo.VpcId) d.Set("peer_vpc_id", pc.AccepterVPCInfo.VPCID)
d.Set("vpc_id", pc.RequesterVpcInfo.VpcId) d.Set("vpc_id", pc.RequesterVPCInfo.VPCID)
d.Set("tags", tagsToMap(pc.Tags)) d.Set("tags", tagsToMap(pc.Tags))
return nil return nil
@ -113,7 +115,10 @@ func resourceAwsVpcPeeringUpdate(d *schema.ResourceData, meta interface{}) error
func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error { func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn ec2conn := meta.(*AWSClient).ec2conn
_, err := ec2conn.DeleteVpcPeeringConnection(d.Id()) _, err := ec2conn.DeleteVPCPeeringConnection(
&ec2.DeleteVPCPeeringConnectionRequest{
VPCPeeringConnectionID: aws.String(d.Id()),
})
return err return err
} }
@ -122,9 +127,11 @@ func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error
func resourceAwsVpcPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { func resourceAwsVpcPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) { 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 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 resp = nil
} else { } else {
log.Printf("Error on VpcPeeringConnectionStateRefresh: %s", err) log.Printf("Error on VpcPeeringConnectionStateRefresh: %s", err)
@ -138,7 +145,7 @@ func resourceAwsVpcPeeringConnectionStateRefreshFunc(conn *ec2.EC2, id string) r
return nil, "", nil return nil, "", nil
} }
pc := &resp.VpcPeeringConnections[0] pc := &resp.VPCPeeringConnections[0]
return pc, "ready", nil return pc, "ready", nil
} }

View File

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestAccAWSVPCPeeringConnection_normal(t *testing.T) { func TestAccAWSVPCPeeringConnection_normal(t *testing.T) {
@ -35,10 +35,13 @@ func testAccCheckAWSVpcPeeringConnectionDestroy(s *terraform.State) error {
continue 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 err == nil {
if len(describe.VpcPeeringConnections) != 0 { if len(describe.VPCPeeringConnections) != 0 {
return fmt.Errorf("vpc peering connection still exists") return fmt.Errorf("vpc peering connection still exists")
} }
} }
@ -68,11 +71,10 @@ resource "aws_vpc" "foo" {
} }
resource "aws_vpc" "bar" { resource "aws_vpc" "bar" {
cidr_block = "10.0.1.0/16" cidr_block = "10.1.0.0/16"
} }
resource "aws_vpc_peering_connection" "foo" { resource "aws_vpc_peering_connection" "foo" {
peer_owner_id = "12345"
vpc_id = "${aws_vpc.foo.id}" vpc_id = "${aws_vpc.foo.id}"
peer_vpc_id = "${aws_vpc.bar.id}" peer_vpc_id = "${aws_vpc.bar.id}"
} }

View File

@ -2,11 +2,11 @@ package aws
import ( import (
"fmt" "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/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2" "testing"
) )
func TestAccVpc_basic(t *testing.T) { func TestAccVpc_basic(t *testing.T) {
@ -119,7 +119,10 @@ func testAccCheckVpcDestroy(s *terraform.State) error {
} }
// Try to find the VPC // 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 err == nil {
if len(resp.VPCs) > 0 { if len(resp.VPCs) > 0 {
return fmt.Errorf("VPCs still exist.") return fmt.Errorf("VPCs still exist.")
@ -129,7 +132,7 @@ func testAccCheckVpcDestroy(s *terraform.State) error {
} }
// Verify the error is what we want // Verify the error is what we want
ec2err, ok := err.(*ec2.Error) ec2err, ok := err.(*aws.APIError)
if !ok { if !ok {
return err return err
} }
@ -143,8 +146,9 @@ func testAccCheckVpcDestroy(s *terraform.State) error {
func testAccCheckVpcCidr(vpc *ec2.VPC, expected string) resource.TestCheckFunc { func testAccCheckVpcCidr(vpc *ec2.VPC, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error { return func(s *terraform.State) error {
if vpc.CidrBlock != expected { CIDRBlock := vpc.CIDRBlock
return fmt.Errorf("Bad cidr: %s", vpc.CidrBlock) if *CIDRBlock != expected {
return fmt.Errorf("Bad cidr: %s", *vpc.CIDRBlock)
} }
return nil return nil
@ -163,7 +167,10 @@ func testAccCheckVpcExists(n string, vpc *ec2.VPC) resource.TestCheckFunc {
} }
conn := testAccProvider.Meta().(*AWSClient).ec2conn 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 { if err != nil {
return err return err
} }

View File

@ -4,10 +4,10 @@ import (
"strings" "strings"
"github.com/hashicorp/aws-sdk-go/aws" "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/elb"
"github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/aws-sdk-go/gen/rds"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
// Takes the result of flatmap.Expand for an array of listeners and // 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)) listeners := make([]elb.Listener, 0, len(configured))
// Loop over our configured listeners and create // 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 { for _, lRaw := range configured {
data := lRaw.(map[string]interface{}) 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 // Takes the result of flatmap.Expand for an array of ingress/egress
// security group rules and returns EC2 API compatible objects // security group rules and returns EC2 API compatible objects
func expandIPPerms(id string, configured []interface{}) []ec2.IPPerm { func expandIPPerms(id string, configured []interface{}) []ec2.IPPermission {
perms := make([]ec2.IPPerm, len(configured)) perms := make([]ec2.IPPermission, len(configured))
for i, mRaw := range configured { for i, mRaw := range configured {
var perm ec2.IPPerm var perm ec2.IPPermission
m := mRaw.(map[string]interface{}) m := mRaw.(map[string]interface{})
perm.FromPort = m["from_port"].(int) perm.FromPort = aws.Integer(m["from_port"].(int))
perm.ToPort = m["to_port"].(int) perm.ToPort = aws.Integer(m["to_port"].(int))
perm.Protocol = m["protocol"].(string) perm.IPProtocol = aws.String(m["protocol"].(string))
var groups []string var groups []string
if raw, ok := m["security_groups"]; ok { if raw, ok := m["security_groups"]; ok {
@ -61,25 +61,25 @@ func expandIPPerms(id string, configured []interface{}) []ec2.IPPerm {
} }
if len(groups) > 0 { if len(groups) > 0 {
perm.SourceGroups = make([]ec2.UserSecurityGroup, len(groups)) perm.UserIDGroupPairs = make([]ec2.UserIDGroupPair, len(groups))
for i, name := range groups { for i, name := range groups {
ownerId, id := "", name ownerId, id := "", name
if items := strings.Split(id, "/"); len(items) > 1 { if items := strings.Split(id, "/"); len(items) > 1 {
ownerId, id = items[0], items[1] ownerId, id = items[0], items[1]
} }
perm.SourceGroups[i] = ec2.UserSecurityGroup{ perm.UserIDGroupPairs[i] = ec2.UserIDGroupPair{
Id: id, GroupID: aws.String(id),
OwnerId: ownerId, UserID: aws.String(ownerId),
} }
} }
} }
if raw, ok := m["cidr_blocks"]; ok { if raw, ok := m["cidr_blocks"]; ok {
list := raw.([]interface{}) list := raw.([]interface{})
perm.SourceIPs = make([]string, len(list)) perm.IPRanges = make([]ec2.IPRange, len(list))
for i, v := range 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)) parameters := make([]rds.Parameter, 0, len(configured))
// Loop over our configured parameters and create // 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 { for _, pRaw := range configured {
data := pRaw.(map[string]interface{}) data := pRaw.(map[string]interface{})
@ -111,31 +111,6 @@ func expandParameters(configured []interface{}) ([]rds.Parameter, error) {
return parameters, nil 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() // Flattens a health check into something that flatmap.Flatten()
// can handle // can handle
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { 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 // 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)) result := make([]string, 0, len(list))
for _, g := range list { for _, g := range list {
result = append(result, g.Id) result = append(result, *g.GroupID)
} }
return result return result
} }

View File

@ -5,12 +5,12 @@ import (
"testing" "testing"
"github.com/hashicorp/aws-sdk-go/aws" "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/elb"
"github.com/hashicorp/aws-sdk-go/gen/rds" "github.com/hashicorp/aws-sdk-go/gen/rds"
"github.com/hashicorp/terraform/flatmap" "github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
// Returns test configuration // Returns test configuration
@ -61,120 +61,58 @@ func TestExpandIPPerms(t *testing.T) {
} }
perms := expandIPPerms("foo", expanded) perms := expandIPPerms("foo", expanded)
expected := []ec2.IPPerm{ expected := []ec2.IPPermission{
ec2.IPPerm{ ec2.IPPermission{
Protocol: "icmp", IPProtocol: aws.String("icmp"),
FromPort: 1, FromPort: aws.Integer(1),
ToPort: -1, ToPort: aws.Integer(-1),
SourceIPs: []string{"0.0.0.0/0"}, IPRanges: []ec2.IPRange{ec2.IPRange{aws.String("0.0.0.0/0")}},
SourceGroups: []ec2.UserSecurityGroup{ UserIDGroupPairs: []ec2.UserIDGroupPair{
ec2.UserSecurityGroup{ ec2.UserIDGroupPair{
OwnerId: "foo", UserID: aws.String("foo"),
Id: "sg-22222", GroupID: aws.String("sg-22222"),
}, },
ec2.UserSecurityGroup{ ec2.UserIDGroupPair{
Id: "sg-11111", GroupID: aws.String("sg-22222"),
}, },
}, },
}, },
ec2.IPPerm{ ec2.IPPermission{
Protocol: "icmp", IPProtocol: aws.String("icmp"),
FromPort: 1, FromPort: aws.Integer(1),
ToPort: -1, ToPort: aws.Integer(-1),
SourceGroups: []ec2.UserSecurityGroup{ UserIDGroupPairs: []ec2.UserIDGroupPair{
ec2.UserSecurityGroup{ ec2.UserIDGroupPair{
Id: "foo", UserID: aws.String("foo"),
}, },
}, },
}, },
} }
if !reflect.DeepEqual(perms, expected) { exp := expected[0]
perm := perms[0]
if *exp.FromPort != *perm.FromPort {
t.Fatalf( t.Fatalf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n", "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
perms[0], *perm.FromPort,
expected) *exp.FromPort)
} }
} if *exp.IPRanges[0].CIDRIP != *perm.IPRanges[0].CIDRIP {
t.Fatalf(
func TestFlattenIPPerms(t *testing.T) { "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
cases := []struct { *perm.IPRanges[0].CIDRIP,
Input []ec2.IPPerm *exp.IPRanges[0].CIDRIP)
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,
},
},
},
} }
for _, tc := range cases { if *exp.UserIDGroupPairs[0].UserID != *perm.UserIDGroupPairs[0].UserID {
output := flattenIPPerms(tc.Input) t.Fatalf(
if !reflect.DeepEqual(output, tc.Output) { "Got:\n\n%#v\n\nExpected:\n\n%#v\n",
t.Fatalf("Input:\n\n%#v\n\nOutput:\n\n%#v", tc.Input, output) *perm.UserIDGroupPairs[0].UserID,
} *exp.UserIDGroupPairs[0].UserID)
} }
} }
func TestExpandListeners(t *testing.T) { func TestExpandListeners(t *testing.T) {

View File

@ -3,11 +3,13 @@ package aws
import ( import (
"log" "log"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
) )
// tagsSchema returns the schema to use for tags. // tagsSchema returns the schema to use for tags.
//
func tagsSchema() *schema.Schema { func tagsSchema() *schema.Schema {
return &schema.Schema{ return &schema.Schema{
Type: schema.TypeMap, Type: schema.TypeMap,
@ -27,13 +29,21 @@ func setTags(conn *ec2.EC2, d *schema.ResourceData) error {
// Set tags // Set tags
if len(remove) > 0 { if len(remove) > 0 {
log.Printf("[DEBUG] Removing tags: %#v", remove) log.Printf("[DEBUG] Removing tags: %#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 return err
} }
} }
if len(create) > 0 { if len(create) > 0 {
log.Printf("[DEBUG] Creating tags: %#v", create) 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 return err
} }
} }
@ -49,14 +59,14 @@ func diffTags(oldTags, newTags []ec2.Tag) ([]ec2.Tag, []ec2.Tag) {
// First, we're creating everything we have // First, we're creating everything we have
create := make(map[string]interface{}) create := make(map[string]interface{})
for _, t := range newTags { for _, t := range newTags {
create[t.Key] = t.Value create[*t.Key] = *t.Value
} }
// Build the list of what to remove // Build the list of what to remove
var remove []ec2.Tag var remove []ec2.Tag
for _, t := range oldTags { for _, t := range oldTags {
old, ok := create[t.Key] old, ok := create[*t.Key]
if !ok || old != t.Value { if !ok || old != *t.Value {
// Delete it! // Delete it!
remove = append(remove, t) remove = append(remove, t)
} }
@ -70,8 +80,8 @@ func tagsFromMap(m map[string]interface{}) []ec2.Tag {
result := make([]ec2.Tag, 0, len(m)) result := make([]ec2.Tag, 0, len(m))
for k, v := range m { for k, v := range m {
result = append(result, ec2.Tag{ result = append(result, ec2.Tag{
Key: k, Key: aws.String(k),
Value: v.(string), 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 { func tagsToMap(ts []ec2.Tag) map[string]string {
result := make(map[string]string) result := make(map[string]string)
for _, t := range ts { for _, t := range ts {
result[t.Key] = t.Value result[*t.Key] = *t.Value
} }
return result return result

View File

@ -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
}

View File

@ -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
}
}

View File

@ -5,9 +5,9 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
) )
func TestDiffTags(t *testing.T) { func TestDiffTags(t *testing.T) {

View File

@ -30,22 +30,25 @@ func Provider() terraform.ResourceProvider {
"timeout": &schema.Schema{ "timeout": &schema.Schema{
Type: schema.TypeInt, Type: schema.TypeInt,
Required: true, Required: true,
DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_TIMEOUT", 180), DefaultFunc: schema.EnvDefaultFunc("CLOUDSTACK_TIMEOUT", 300),
}, },
}, },
ResourcesMap: map[string]*schema.Resource{ ResourcesMap: map[string]*schema.Resource{
"cloudstack_disk": resourceCloudStackDisk(), "cloudstack_disk": resourceCloudStackDisk(),
"cloudstack_egress_firewall": resourceCloudStackEgressFirewall(), "cloudstack_egress_firewall": resourceCloudStackEgressFirewall(),
"cloudstack_firewall": resourceCloudStackFirewall(), "cloudstack_firewall": resourceCloudStackFirewall(),
"cloudstack_instance": resourceCloudStackInstance(), "cloudstack_instance": resourceCloudStackInstance(),
"cloudstack_ipaddress": resourceCloudStackIPAddress(), "cloudstack_ipaddress": resourceCloudStackIPAddress(),
"cloudstack_network": resourceCloudStackNetwork(), "cloudstack_network": resourceCloudStackNetwork(),
"cloudstack_network_acl": resourceCloudStackNetworkACL(), "cloudstack_network_acl": resourceCloudStackNetworkACL(),
"cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(), "cloudstack_network_acl_rule": resourceCloudStackNetworkACLRule(),
"cloudstack_nic": resourceCloudStackNIC(), "cloudstack_nic": resourceCloudStackNIC(),
"cloudstack_port_forward": resourceCloudStackPortForward(), "cloudstack_port_forward": resourceCloudStackPortForward(),
"cloudstack_vpc": resourceCloudStackVPC(), "cloudstack_vpc": resourceCloudStackVPC(),
"cloudstack_vpn_connection": resourceCloudStackVPNConnection(),
"cloudstack_vpn_customer_gateway": resourceCloudStackVPNCustomerGateway(),
"cloudstack_vpn_gateway": resourceCloudStackVPNGateway(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,

View File

@ -51,7 +51,8 @@ var CLOUDSTACK_NETWORK_1_OFFERING = ""
var CLOUDSTACK_NETWORK_1_IPADDRESS = "" var CLOUDSTACK_NETWORK_1_IPADDRESS = ""
var CLOUDSTACK_NETWORK_2 = "" var CLOUDSTACK_NETWORK_2 = ""
var CLOUDSTACK_NETWORK_2_IPADDRESS = "" 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_OFFERING = ""
var CLOUDSTACK_VPC_NETWORK_CIDR = "" var CLOUDSTACK_VPC_NETWORK_CIDR = ""
var CLOUDSTACK_VPC_NETWORK_OFFERING = "" var CLOUDSTACK_VPC_NETWORK_OFFERING = ""

View File

@ -95,18 +95,18 @@ func resourceCloudStackInstanceCreate(d *schema.ResourceData, meta interface{})
return e.Error() 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 // Retrieve the zone object
zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string)) zone, _, err := cs.Zone.GetZoneByName(d.Get("zone").(string))
if err != nil { if err != nil {
return err 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 // Create a new parameter struct
p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id) p := cs.VirtualMachine.NewDeployVirtualMachineParams(serviceofferingid, templateid, zone.Id)

View File

@ -132,6 +132,6 @@ resource "cloudstack_vpc" "foobar" {
resource "cloudstack_ipaddress" "foo" { resource "cloudstack_ipaddress" "foo" {
vpc = "${cloudstack_vpc.foobar.name}" vpc = "${cloudstack_vpc.foobar.name}"
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE) CLOUDSTACK_ZONE)

View File

@ -196,7 +196,7 @@ resource "cloudstack_network_acl_rule" "foo" {
traffic_type = "ingress" traffic_type = "ingress"
} }
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE) CLOUDSTACK_ZONE)
@ -233,6 +233,6 @@ resource "cloudstack_network_acl_rule" "foo" {
traffic_type = "egress" traffic_type = "egress"
} }
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE) CLOUDSTACK_ZONE)

View File

@ -112,6 +112,6 @@ resource "cloudstack_network_acl" "foo" {
description = "terraform-acl-text" description = "terraform-acl-text"
vpc = "${cloudstack_vpc.foobar.name}" vpc = "${cloudstack_vpc.foobar.name}"
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE) CLOUDSTACK_ZONE)

View File

@ -186,7 +186,7 @@ resource "cloudstack_network" "foo" {
aclid = "${cloudstack_network_acl.foo.id}" aclid = "${cloudstack_network_acl.foo.id}"
zone = "${cloudstack_vpc.foobar.zone}" zone = "${cloudstack_vpc.foobar.zone}"
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE, CLOUDSTACK_ZONE,
CLOUDSTACK_VPC_NETWORK_CIDR, CLOUDSTACK_VPC_NETWORK_CIDR,

View File

@ -72,8 +72,8 @@ func testAccCheckCloudStackVPCAttributes(
return fmt.Errorf("Bad display text: %s", vpc.Displaytext) return fmt.Errorf("Bad display text: %s", vpc.Displaytext)
} }
if vpc.Cidr != CLOUDSTACK_VPC_CIDR { if vpc.Cidr != CLOUDSTACK_VPC_CIDR_1 {
return fmt.Errorf("Bad VPC offering: %s", vpc.Cidr) return fmt.Errorf("Bad VPC CIDR: %s", vpc.Cidr)
} }
return nil return nil
@ -113,6 +113,6 @@ resource "cloudstack_vpc" "foo" {
vpc_offering = "%s" vpc_offering = "%s"
zone = "%s" zone = "%s"
}`, }`,
CLOUDSTACK_VPC_CIDR, CLOUDSTACK_VPC_CIDR_1,
CLOUDSTACK_VPC_OFFERING, CLOUDSTACK_VPC_OFFERING,
CLOUDSTACK_ZONE) CLOUDSTACK_ZONE)

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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"
}`)

View File

@ -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
}

View File

@ -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)

View File

@ -40,8 +40,6 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str
uuid, err = cs.VPC.GetVPCOfferingID(value) uuid, err = cs.VPC.GetVPCOfferingID(value)
case "vpc": case "vpc":
uuid, err = cs.VPC.GetVPCID(value) uuid, err = cs.VPC.GetVPCID(value)
case "template":
uuid, err = cs.Template.GetTemplateID(value, "executable")
case "network": case "network":
uuid, err = cs.Network.GetNetworkID(value) uuid, err = cs.Network.GetNetworkID(value)
case "zone": case "zone":
@ -71,6 +69,22 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str
return uuid, nil 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 { 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}$`) 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) return re.MatchString(s)

View File

@ -21,6 +21,7 @@ func Provider() terraform.ResourceProvider {
"digitalocean_domain": resourceDigitalOceanDomain(), "digitalocean_domain": resourceDigitalOceanDomain(),
"digitalocean_droplet": resourceDigitalOceanDroplet(), "digitalocean_droplet": resourceDigitalOceanDroplet(),
"digitalocean_record": resourceDigitalOceanRecord(), "digitalocean_record": resourceDigitalOceanRecord(),
"digitalocean_ssh_key": resourceDigitalOceanSSHKey(),
}, },
ConfigureFunc: providerConfigure, ConfigureFunc: providerConfigure,

View File

@ -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
}

View File

@ -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"
}`

View File

@ -3,6 +3,7 @@ package schema
import ( import (
"errors" "errors"
"fmt" "fmt"
"strconv"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
@ -24,6 +25,31 @@ type Resource struct {
// resource. // resource.
Schema map[string]*Schema 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 functions below are the CRUD operations for this resource.
// //
// The only optional operation is Update. If Update is not implemented, // The only optional operation is Update. If Update is not implemented,
@ -69,6 +95,10 @@ type DeleteFunc func(*ResourceData, interface{}) error
// See Resource documentation. // See Resource documentation.
type ExistsFunc func(*ResourceData, interface{}) (bool, error) 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. // Apply creates, updates, and/or deletes a resource.
func (r *Resource) Apply( func (r *Resource) Apply(
s *terraform.InstanceState, 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) data, err := schemaMap(r.Schema).Data(s, nil)
if err != nil { if err != nil {
return s, err return s, err
@ -169,6 +207,13 @@ func (r *Resource) Refresh(
state = nil 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 return state, err
} }
@ -189,3 +234,10 @@ func (r *Resource) InternalValidate() error {
return schemaMap(r.Schema).InternalValidate() 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
}

View File

@ -3,6 +3,7 @@ package schema
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"testing" "testing"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
@ -478,3 +479,218 @@ func TestResourceRefresh_noExists(t *testing.T) {
t.Fatalf("should have no state") 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!")
}
}

View File

@ -14,7 +14,7 @@ import (
"sync" "sync"
"time" "time"
"code.google.com/p/go.crypto/ssh" "golang.org/x/crypto/ssh"
) )
// RemoteCmd represents a remote command being prepared or run. // RemoteCmd represents a remote command being prepared or run.

View File

@ -4,7 +4,7 @@ package ssh
import ( import (
"bytes" "bytes"
"code.google.com/p/go.crypto/ssh" "golang.org/x/crypto/ssh"
"fmt" "fmt"
"net" "net"
"testing" "testing"

View File

@ -1,7 +1,7 @@
package ssh package ssh
import ( import (
"code.google.com/p/go.crypto/ssh" "golang.org/x/crypto/ssh"
"log" "log"
) )

View File

@ -1,7 +1,7 @@
package ssh package ssh
import ( import (
"code.google.com/p/go.crypto/ssh" "golang.org/x/crypto/ssh"
"reflect" "reflect"
"testing" "testing"
) )

View File

@ -7,7 +7,7 @@ import (
"log" "log"
"time" "time"
"code.google.com/p/go.crypto/ssh" "golang.org/x/crypto/ssh"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/go-homedir" "github.com/mitchellh/go-homedir"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"

View File

@ -19,9 +19,11 @@ GIT_DIRTY=$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)
XC_ARCH=${XC_ARCH:-"386 amd64 arm"} XC_ARCH=${XC_ARCH:-"386 amd64 arm"}
XC_OS=${XC_OS:-linux darwin windows freebsd openbsd} XC_OS=${XC_OS:-linux darwin windows freebsd openbsd}
# Install dependencies # Install dependencies unless running in quick mode
echo "==> Getting dependencies..." if [ "${TF_QUICKDEV}x" == "x" ]; then
go get ./... echo "==> Getting dependencies..."
go get ./...
fi
# Delete the old dir # Delete the old dir
echo "==> Removing old directory..." echo "==> Removing old directory..."

View File

@ -832,6 +832,11 @@ type InstanceState struct {
// that is necessary for the Terraform run to complete, but is not // that is necessary for the Terraform run to complete, but is not
// persisted to a state file. // persisted to a state file.
Ephemeral EphemeralState `json:"-"` 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() { func (i *InstanceState) init() {

View File

@ -1,3 +1,3 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'middleman-hashicorp', github: 'hashicorp/middleman-hashicorp' gem 'middleman-hashicorp', git: 'https://github.com/hashicorp/middleman-hashicorp'

View File

@ -1,19 +1,19 @@
GIT GIT
remote: git://github.com/hashicorp/middleman-hashicorp.git remote: https://github.com/hashicorp/middleman-hashicorp
revision: 30c15f93fb501041cff97c490b60ddc96c8314c9 revision: 783fe9517dd02badb85e5ddfeda4d8e35bbd05a8
specs: specs:
middleman-hashicorp (0.1.0) middleman-hashicorp (0.1.0)
bootstrap-sass (~> 3.2) bootstrap-sass (~> 3.3)
builder (~> 3.2) builder (~> 3.2)
less (~> 2.6) less (~> 2.6)
middleman (~> 3.3) middleman (~> 3.3)
middleman-livereload (~> 3.3) middleman-livereload (~> 3.4)
middleman-minify-html (~> 3.4) middleman-minify-html (~> 3.4)
middleman-syntax (~> 2.0) middleman-syntax (~> 2.0)
rack-contrib (~> 1.1) rack-contrib (~> 1.2)
rack-rewrite (~> 1.5) rack-rewrite (~> 1.5)
rack-ssl-enforcer (~> 0.2) rack-ssl-enforcer (~> 0.2)
redcarpet (~> 3.1) redcarpet (~> 3.2)
therubyracer (~> 0.12) therubyracer (~> 0.12)
thin (~> 1.6) thin (~> 1.6)
@ -26,7 +26,7 @@ GEM
minitest (~> 5.1) minitest (~> 5.1)
thread_safe (~> 0.1) thread_safe (~> 0.1)
tzinfo (~> 1.1) tzinfo (~> 1.1)
autoprefixer-rails (5.0.0.2) autoprefixer-rails (5.1.7)
execjs execjs
json json
bootstrap-sass (3.3.3) bootstrap-sass (3.3.3)
@ -35,11 +35,11 @@ GEM
builder (3.2.2) builder (3.2.2)
celluloid (0.16.0) celluloid (0.16.0)
timers (~> 4.0.0) timers (~> 4.0.0)
chunky_png (1.3.3) chunky_png (1.3.4)
coffee-script (2.3.0) coffee-script (2.3.0)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.8.0) coffee-script-source (1.9.1)
commonjs (0.2.7) commonjs (0.2.7)
compass (1.0.3) compass (1.0.3)
chunky_png (~> 1.2) chunky_png (~> 1.2)
@ -58,8 +58,8 @@ GEM
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0) http_parser.rb (~> 0.6.0)
erubis (2.7.0) erubis (2.7.0)
eventmachine (1.0.4) eventmachine (1.0.7)
execjs (2.2.2) execjs (2.4.0)
ffi (1.9.6) ffi (1.9.6)
haml (4.0.6) haml (4.0.6)
tilt tilt
@ -69,9 +69,9 @@ GEM
uber (~> 0.0.4) uber (~> 0.0.4)
htmlcompressor (0.1.2) htmlcompressor (0.1.2)
http_parser.rb (0.6.0) http_parser.rb (0.6.0)
i18n (0.6.11) i18n (0.7.0)
json (1.8.2) json (1.8.2)
kramdown (1.5.0) kramdown (1.6.0)
less (2.6.0) less (2.6.0)
commonjs (~> 0.2.7) commonjs (~> 0.2.7)
libv8 (3.16.14.7) libv8 (3.16.14.7)
@ -79,23 +79,23 @@ GEM
celluloid (>= 0.15.2) celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
middleman (3.3.7) middleman (3.3.10)
coffee-script (~> 2.2) coffee-script (~> 2.2)
compass (>= 1.0.0, < 2.0.0) compass (>= 1.0.0, < 2.0.0)
compass-import-once (= 1.0.5) compass-import-once (= 1.0.5)
execjs (~> 2.0) execjs (~> 2.0)
haml (>= 4.0.5) haml (>= 4.0.5)
kramdown (~> 1.2) kramdown (~> 1.2)
middleman-core (= 3.3.7) middleman-core (= 3.3.10)
middleman-sprockets (>= 3.1.2) middleman-sprockets (>= 3.1.2)
sass (>= 3.4.0, < 4.0) sass (>= 3.4.0, < 4.0)
uglifier (~> 2.5) uglifier (~> 2.5)
middleman-core (3.3.7) middleman-core (3.3.10)
activesupport (~> 4.1.0) activesupport (~> 4.1.0)
bundler (~> 1.1) bundler (~> 1.1)
erubis erubis
hooks (~> 0.3) hooks (~> 0.3)
i18n (~> 0.6.9) i18n (~> 0.7.0)
listen (>= 2.7.9, < 3.0) listen (>= 2.7.9, < 3.0)
padrino-helpers (~> 0.12.3) padrino-helpers (~> 0.12.3)
rack (>= 1.4.5, < 2.0) rack (>= 1.4.5, < 2.0)
@ -109,7 +109,7 @@ GEM
middleman-minify-html (3.4.0) middleman-minify-html (3.4.0)
htmlcompressor (~> 0.1.0) htmlcompressor (~> 0.1.0)
middleman-core (>= 3.2) middleman-core (>= 3.2)
middleman-sprockets (3.4.1) middleman-sprockets (3.4.2)
middleman-core (>= 3.3) middleman-core (>= 3.3)
sprockets (~> 2.12.1) sprockets (~> 2.12.1)
sprockets-helpers (~> 1.1.0) sprockets-helpers (~> 1.1.0)
@ -118,12 +118,12 @@ GEM
middleman-core (~> 3.2) middleman-core (~> 3.2)
rouge (~> 1.0) rouge (~> 1.0)
minitest (5.5.1) minitest (5.5.1)
multi_json (1.10.1) multi_json (1.11.0)
padrino-helpers (0.12.4) padrino-helpers (0.12.5)
i18n (~> 0.6, >= 0.6.7) i18n (~> 0.6, >= 0.6.7)
padrino-support (= 0.12.4) padrino-support (= 0.12.5)
tilt (~> 1.4.1) tilt (~> 1.4.1)
padrino-support (0.12.4) padrino-support (0.12.5)
activesupport (>= 3.1) activesupport (>= 3.1)
rack (1.6.0) rack (1.6.0)
rack-contrib (1.2.0) rack-contrib (1.2.0)
@ -139,8 +139,8 @@ GEM
ffi (>= 0.5.0) ffi (>= 0.5.0)
redcarpet (3.2.2) redcarpet (3.2.2)
ref (1.0.5) ref (1.0.5)
rouge (1.7.7) rouge (1.8.0)
sass (3.4.10) sass (3.4.13)
sprockets (2.12.3) sprockets (2.12.3)
hike (~> 1.2) hike (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
@ -166,7 +166,7 @@ GEM
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uber (0.0.13) uber (0.0.13)
uglifier (2.7.0) uglifier (2.7.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)

View File

@ -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 server when necessary and if any updates are made, the newest state
is persisted back to the remote server. is persisted back to the remote server.
In this mode, users do not need to durably store the state using version In this mode, users do not need to durably store the state using version
control or shared storaged. control or shared storage.
## Usage ## Usage

View File

@ -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). 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 * `db_subnet_group_name` - (Optional) Name of DB subnet group
* `parameter_group_name` - (Optional) Name of the DB parameter group to associate. * `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 ## Attributes Reference
@ -82,4 +83,5 @@ The following attributes are exported:
* `port` - The database port * `port` - The database port
* `status` - The RDS instance status * `status` - The RDS instance status
* `username` - The master username for the database * `username` - The master username for the database
* `storage_encrypted` - Specifies whether the DB instance is encrypted

View File

@ -29,6 +29,18 @@ The following arguments are supported:
* `vpc_id` - (Required) The VPC ID to create in. * `vpc_id` - (Required) The VPC ID to create in.
* `tags` - (Optional) A mapping of tags to assign to the resource. * `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 ## Attributes Reference
The following attributes are exported: The following attributes are exported:

View File

@ -56,4 +56,4 @@ The following attributes are exported:
## Notes ## 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.

View File

@ -58,4 +58,4 @@ The `rule` block supports:
The following attributes are exported: 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.

View File

@ -58,4 +58,4 @@ The `rule` block supports:
The following attributes are exported: 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.

View File

@ -66,4 +66,4 @@ The `rule` block supports:
The following attributes are exported: 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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -1,66 +1,78 @@
<% wrap_layout :inner do %> <% wrap_layout :inner do %>
<% content_for :sidebar do %> <% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary"> <div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav"> <ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>> <li<%= sidebar_current("docs-home") %>>
<a href="/docs/index.html">&laquo; Documentation Home</a> <a href="/docs/index.html">&laquo; Documentation Home</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-index") %>> <li<%= sidebar_current("docs-cloudstack-index") %>>
<a href="/docs/providers/cloudstack/index.html">CloudStack Provider</a> <a href="/docs/providers/cloudstack/index.html">CloudStack Provider</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource") %>> <li<%= sidebar_current("docs-cloudstack-resource") %>>
<a href="#">Resources</a> <a href="#">Resources</a>
<ul class="nav nav-visible"> <ul class="nav nav-visible">
<li<%= sidebar_current("docs-cloudstack-resource-disk") %>> <li<%= sidebar_current("docs-cloudstack-resource-disk") %>>
<a href="/docs/providers/cloudstack/r/disk.html">cloudstack_disk</a> <a href="/docs/providers/cloudstack/r/disk.html">cloudstack_disk</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-egress-firewall") %>> <li<%= sidebar_current("docs-cloudstack-resource-egress-firewall") %>>
<a href="/docs/providers/cloudstack/r/egress_firewall.html">cloudstack_egress_firewall</a> <a href="/docs/providers/cloudstack/r/egress_firewall.html">cloudstack_egress_firewall</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-firewall") %>> <li<%= sidebar_current("docs-cloudstack-resource-firewall") %>>
<a href="/docs/providers/cloudstack/r/firewall.html">cloudstack_firewall</a> <a href="/docs/providers/cloudstack/r/firewall.html">cloudstack_firewall</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-instance") %>> <li<%= sidebar_current("docs-cloudstack-resource-instance") %>>
<a href="/docs/providers/cloudstack/r/instance.html">cloudstack_instance</a> <a href="/docs/providers/cloudstack/r/instance.html">cloudstack_instance</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-ipaddress") %>> <li<%= sidebar_current("docs-cloudstack-resource-ipaddress") %>>
<a href="/docs/providers/cloudstack/r/ipaddress.html">cloudstack_ipaddress</a> <a href="/docs/providers/cloudstack/r/ipaddress.html">cloudstack_ipaddress</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-network") %>> <li<%= sidebar_current("docs-cloudstack-resource-network") %>>
<a href="/docs/providers/cloudstack/r/network.html">cloudstack_network</a> <a href="/docs/providers/cloudstack/r/network.html">cloudstack_network</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl") %>> <li<%= sidebar_current("docs-cloudstack-resource-network-acl") %>>
<a href="/docs/providers/cloudstack/r/network_acl.html">cloudstack_network_acl</a> <a href="/docs/providers/cloudstack/r/network_acl.html">cloudstack_network_acl</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl-rule") %>> <li<%= sidebar_current("docs-cloudstack-resource-network-acl-rule") %>>
<a href="/docs/providers/cloudstack/r/network_acl_rule.html">cloudstack_network_acl_rule</a> <a href="/docs/providers/cloudstack/r/network_acl_rule.html">cloudstack_network_acl_rule</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-nic") %>> <li<%= sidebar_current("docs-cloudstack-resource-nic") %>>
<a href="/docs/providers/cloudstack/r/nic.html">cloudstack_nic</a> <a href="/docs/providers/cloudstack/r/nic.html">cloudstack_nic</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-port-forward") %>> <li<%= sidebar_current("docs-cloudstack-resource-port-forward") %>>
<a href="/docs/providers/cloudstack/r/port_forward.html">cloudstack_port_forward</a> <a href="/docs/providers/cloudstack/r/port_forward.html">cloudstack_port_forward</a>
</li> </li>
<li<%= sidebar_current("docs-cloudstack-resource-vpc") %>> <li<%= sidebar_current("docs-cloudstack-resource-vpc") %>>
<a href="/docs/providers/cloudstack/r/vpc.html">cloudstack_vpc</a> <a href="/docs/providers/cloudstack/r/vpc.html">cloudstack_vpc</a>
</li> </li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %> <li<%= sidebar_current("docs-cloudstack-resource-vpn-gateway") %>>
<% end %> <a href="/docs/providers/cloudstack/r/vpn_gateway.html">cloudstack_vpn_gateway</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-vpn-customer-gateway") %>>
<a href="/docs/providers/cloudstack/r/vpn_customer_gateway.html">cloudstack_vpn_customer_gateway</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-vpn-connection") %>>
<a href="/docs/providers/cloudstack/r/vpn_connection.html">cloudstack_vpn_connection</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>

View File

@ -23,6 +23,10 @@
<li<%= sidebar_current("docs-do-resource-record") %>> <li<%= sidebar_current("docs-do-resource-record") %>>
<a href="/docs/providers/do/r/record.html">digitalocean_record</a> <a href="/docs/providers/do/r/record.html">digitalocean_record</a>
</li>
<li<%= sidebar_current("docs-do-resource-ssh-key") %>>
<a href="/docs/providers/do/r/ssh_key.html">digitalocean_ssh_key</a>
</li> </li>
</ul> </ul>
</li> </li>