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

View File

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

View File

@ -3,21 +3,16 @@ package aws
import (
"fmt"
"log"
"strings"
"unicode"
"github.com/hashicorp/terraform/helper/multierror"
"github.com/mitchellh/goamz/aws"
"github.com/mitchellh/goamz/ec2"
awsGo "github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/autoscaling"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/aws-sdk-go/gen/elb"
"github.com/hashicorp/aws-sdk-go/gen/rds"
"github.com/hashicorp/aws-sdk-go/gen/route53"
"github.com/hashicorp/aws-sdk-go/gen/s3"
awsEC2 "github.com/hashicorp/aws-sdk-go/gen/ec2"
)
type Config struct {
@ -29,7 +24,6 @@ type Config struct {
type AWSClient struct {
ec2conn *ec2.EC2
awsEC2conn *awsEC2.EC2
elbconn *elb.ELB
autoscalingconn *autoscaling.AutoScaling
s3conn *s3.S3
@ -45,14 +39,9 @@ func (c *Config) Client() (interface{}, error) {
// Get the auth and region. This can fail if keys/regions were not
// specified and we're attempting to use the environment.
var errs []error
log.Println("[INFO] Building AWS auth structure")
auth, err := c.AWSAuth()
if err != nil {
errs = append(errs, err)
}
log.Println("[INFO] Building AWS region structure")
region, err := c.AWSRegion()
err := c.ValidateRegion()
if err != nil {
errs = append(errs, err)
}
@ -62,10 +51,9 @@ func (c *Config) Client() (interface{}, error) {
// bucket storage in S3
client.region = c.Region
creds := awsGo.Creds(c.AccessKey, c.SecretKey, c.Token)
log.Println("[INFO] Building AWS auth structure")
creds := aws.Creds(c.AccessKey, c.SecretKey, c.Token)
log.Println("[INFO] Initializing EC2 connection")
client.ec2conn = ec2.New(auth, region)
log.Println("[INFO] Initializing ELB connection")
client.elbconn = elb.New(creds, c.Region, nil)
log.Println("[INFO] Initializing AutoScaling connection")
@ -80,8 +68,8 @@ func (c *Config) Client() (interface{}, error) {
// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
log.Println("[INFO] Initializing Route53 connection")
client.r53conn = route53.New(creds, "us-east-1", nil)
log.Println("[INFO] Initializing AWS-GO EC2 Connection")
client.awsEC2conn = awsEC2.New(creds, c.Region, nil)
log.Println("[INFO] Initializing EC2 Connection")
client.ec2conn = ec2.New(creds, c.Region, nil)
}
if len(errs) > 0 {
@ -91,54 +79,17 @@ func (c *Config) Client() (interface{}, error) {
return &client, nil
}
// AWSAuth returns a valid aws.Auth object for access to AWS services, or
// an error if the authentication couldn't be resolved.
//
// TODO(mitchellh): Test in some way.
func (c *Config) AWSAuth() (aws.Auth, error) {
auth, err := aws.GetAuth(c.AccessKey, c.SecretKey)
if err == nil {
// Store the accesskey and secret that we got...
c.AccessKey = auth.AccessKey
c.SecretKey = auth.SecretKey
c.Token = auth.Token
}
return auth, err
}
// IsValidRegion returns true if the configured region is a valid AWS
// region and false if it's not
func (c *Config) IsValidRegion() bool {
func (c *Config) ValidateRegion() error {
var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
"sa-east-1", "cn-north-1", "us-gov-west-1"}
for _, valid := range regions {
if c.Region == valid {
return true
return nil
}
}
return false
}
// AWSRegion returns the configured region.
//
// TODO(mitchellh): Test in some way.
func (c *Config) AWSRegion() (aws.Region, error) {
if c.Region != "" {
if c.IsValidRegion() {
return aws.Regions[c.Region], nil
} else {
return aws.Region{}, fmt.Errorf("Not a valid region: %s", c.Region)
}
}
md, err := aws.GetMetaData("placement/availability-zone")
if err != nil {
return aws.Region{}, err
}
region := strings.TrimRightFunc(string(md), unicode.IsLetter)
return aws.Regions[region], nil
return fmt.Errorf("Not a valid region: %s", c.Region)
}

View File

@ -2,11 +2,14 @@ package aws
import (
"fmt"
"github.com/mitchellh/goamz/ec2"
"strconv"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
)
func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.NetworkAclEntry, error) {
entries := make([]ec2.NetworkAclEntry, 0, len(configured))
func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.NetworkACLEntry, error) {
entries := make([]ec2.NetworkACLEntry, 0, len(configured))
for _, eRaw := range configured {
data := eRaw.(map[string]interface{})
protocol := data["protocol"].(string)
@ -15,16 +18,16 @@ func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.
return nil, fmt.Errorf("Invalid Protocol %s for rule %#v", protocol, data)
}
p := extractProtocolInteger(data["protocol"].(string))
e := ec2.NetworkAclEntry{
Protocol: p,
PortRange: ec2.PortRange{
From: data["from_port"].(int),
To: data["to_port"].(int),
e := ec2.NetworkACLEntry{
Protocol: aws.String(strconv.Itoa(p)),
PortRange: &ec2.PortRange{
From: aws.Integer(data["from_port"].(int)),
To: aws.Integer(data["to_port"].(int)),
},
Egress: (entryType == "egress"),
RuleAction: data["action"].(string),
RuleNumber: data["rule_no"].(int),
CidrBlock: data["cidr_block"].(string),
Egress: aws.Boolean((entryType == "egress")),
RuleAction: aws.String(data["action"].(string)),
RuleNumber: aws.Integer(data["rule_no"].(int)),
CIDRBlock: aws.String(data["cidr_block"].(string)),
}
entries = append(entries, e)
}
@ -33,17 +36,17 @@ func expandNetworkAclEntries(configured []interface{}, entryType string) ([]ec2.
}
func flattenNetworkAclEntries(list []ec2.NetworkAclEntry) []map[string]interface{} {
func flattenNetworkAclEntries(list []ec2.NetworkACLEntry) []map[string]interface{} {
entries := make([]map[string]interface{}, 0, len(list))
for _, entry := range list {
entries = append(entries, map[string]interface{}{
"from_port": entry.PortRange.From,
"to_port": entry.PortRange.To,
"action": entry.RuleAction,
"rule_no": entry.RuleNumber,
"protocol": extractProtocolString(entry.Protocol),
"cidr_block": entry.CidrBlock,
"from_port": *entry.PortRange.From,
"to_port": *entry.PortRange.To,
"action": *entry.RuleAction,
"rule_no": *entry.RuleNumber,
"protocol": *entry.Protocol,
"cidr_block": *entry.CIDRBlock,
})
}
return entries

View File

@ -4,10 +4,11 @@ import (
"reflect"
"testing"
"github.com/mitchellh/goamz/ec2"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
)
func Test_expandNetworkAclEntry(t *testing.T) {
func Test_expandNetworkACLEntry(t *testing.T) {
input := []interface{}{
map[string]interface{}{
"protocol": "tcp",
@ -28,30 +29,28 @@ func Test_expandNetworkAclEntry(t *testing.T) {
}
expanded, _ := expandNetworkAclEntries(input, "egress")
expected := []ec2.NetworkAclEntry{
ec2.NetworkAclEntry{
Protocol: 6,
PortRange: ec2.PortRange{
From: 22,
To: 22,
expected := []ec2.NetworkACLEntry{
ec2.NetworkACLEntry{
Protocol: aws.String("6"),
PortRange: &ec2.PortRange{
From: aws.Integer(22),
To: aws.Integer(22),
},
RuleAction: "deny",
RuleNumber: 1,
CidrBlock: "0.0.0.0/0",
Egress: true,
IcmpCode: ec2.IcmpCode{Code: 0, Type: 0},
RuleAction: aws.String("deny"),
RuleNumber: aws.Integer(1),
CIDRBlock: aws.String("0.0.0.0/0"),
Egress: aws.Boolean(true),
},
ec2.NetworkAclEntry{
Protocol: 6,
PortRange: ec2.PortRange{
From: 443,
To: 443,
ec2.NetworkACLEntry{
Protocol: aws.String("6"),
PortRange: &ec2.PortRange{
From: aws.Integer(443),
To: aws.Integer(443),
},
RuleAction: "deny",
RuleNumber: 2,
CidrBlock: "0.0.0.0/0",
Egress: true,
IcmpCode: ec2.IcmpCode{Code: 0, Type: 0},
RuleAction: aws.String("deny"),
RuleNumber: aws.Integer(2),
CIDRBlock: aws.String("0.0.0.0/0"),
Egress: aws.Boolean(true),
},
}
@ -64,28 +63,28 @@ func Test_expandNetworkAclEntry(t *testing.T) {
}
func Test_flattenNetworkAclEntry(t *testing.T) {
func Test_flattenNetworkACLEntry(t *testing.T) {
apiInput := []ec2.NetworkAclEntry{
ec2.NetworkAclEntry{
Protocol: 6,
PortRange: ec2.PortRange{
From: 22,
To: 22,
apiInput := []ec2.NetworkACLEntry{
ec2.NetworkACLEntry{
Protocol: aws.String("tcp"),
PortRange: &ec2.PortRange{
From: aws.Integer(22),
To: aws.Integer(22),
},
RuleAction: "deny",
RuleNumber: 1,
CidrBlock: "0.0.0.0/0",
RuleAction: aws.String("deny"),
RuleNumber: aws.Integer(1),
CIDRBlock: aws.String("0.0.0.0/0"),
},
ec2.NetworkAclEntry{
Protocol: 6,
PortRange: ec2.PortRange{
From: 443,
To: 443,
ec2.NetworkACLEntry{
Protocol: aws.String("tcp"),
PortRange: &ec2.PortRange{
From: aws.Integer(443),
To: aws.Integer(443),
},
RuleAction: "deny",
RuleNumber: 2,
CidrBlock: "0.0.0.0/0",
RuleAction: aws.String("deny"),
RuleNumber: aws.Integer(2),
CIDRBlock: aws.String("0.0.0.0/0"),
},
}
flattened := flattenNetworkAclEntries(apiInput)

View File

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

View File

@ -152,7 +152,7 @@ func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{})
os := o.(*schema.Set)
ns := n.(*schema.Set)
// Expand the "parameter" set to goamz compat []rds.Parameter
// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
parameters, err := expandParameters(ns.Difference(os).List())
if err != nil {
return err

View File

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

View File

@ -58,7 +58,7 @@ func TestAccAWSEIP_instance(t *testing.T) {
}
func testAccCheckAWSEIPDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
conn := testAccProvider.Meta().(*AWSClient).ec2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_eip" {
@ -113,7 +113,7 @@ func testAccCheckAWSEIPExists(n string, res *ec2.Address) resource.TestCheckFunc
return fmt.Errorf("No EIP ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
conn := testAccProvider.Meta().(*AWSClient).ec2conn
if strings.Contains(rs.Primary.ID, "eipalloc") {
req := &ec2.DescribeAddressesRequest{

View File

@ -161,7 +161,7 @@ func resourceAwsElb() *schema.Resource {
func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn
// Expand the "listener" set to goamz compat []elb.Listener
// Expand the "listener" set to aws-sdk-go compat []elb.Listener
listeners, err := expandListeners(d.Get("listener").(*schema.Set).List())
if err != nil {
return err

View File

@ -3,17 +3,18 @@ package aws
import (
"bytes"
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/goamz/ec2"
)
func resourceAwsInstance() *schema.Resource {
@ -258,7 +259,28 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
// Figure out user data
userData := ""
if v := d.Get("user_data"); v != nil {
userData = v.(string)
userData = base64.StdEncoding.EncodeToString([]byte(v.(string)))
}
placement := &ec2.Placement{
AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
Tenancy: aws.String(d.Get("tenancy").(string)),
}
iam := &ec2.IAMInstanceProfileSpecification{
Name: aws.String(d.Get("iam_instance_profile").(string)),
}
// Build the creation struct
runOpts := &ec2.RunInstancesRequest{
ImageID: aws.String(d.Get("ami").(string)),
Placement: placement,
InstanceType: aws.String(d.Get("instance_type").(string)),
MaxCount: aws.Integer(1),
MinCount: aws.Integer(1),
UserData: aws.String(userData),
EBSOptimized: aws.Boolean(d.Get("ebs_optimized").(bool)),
IAMInstanceProfile: iam,
}
associatePublicIPAddress := false
@ -266,36 +288,65 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
associatePublicIPAddress = v.(bool)
}
// Build the creation struct
runOpts := &ec2.RunInstances{
ImageId: d.Get("ami").(string),
AvailZone: d.Get("availability_zone").(string),
InstanceType: d.Get("instance_type").(string),
KeyName: d.Get("key_name").(string),
SubnetId: d.Get("subnet_id").(string),
PrivateIPAddress: d.Get("private_ip").(string),
AssociatePublicIpAddress: associatePublicIPAddress,
UserData: []byte(userData),
EbsOptimized: d.Get("ebs_optimized").(bool),
IamInstanceProfile: d.Get("iam_instance_profile").(string),
Tenancy: d.Get("tenancy").(string),
}
// check for non-default Subnet, and cast it to a String
var hasSubnet bool
subnet, hasSubnet := d.GetOk("subnet_id")
subnetID := subnet.(string)
var groups []string
if v := d.Get("security_groups"); v != nil {
// Security group names.
// For a nondefault VPC, you must use security group IDs instead.
// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html
for _, v := range v.(*schema.Set).List() {
str := v.(string)
var g ec2.SecurityGroup
if runOpts.SubnetId != "" {
g.Id = str
} else {
g.Name = str
}
runOpts.SecurityGroups = append(runOpts.SecurityGroups, g)
groups = append(groups, str)
}
}
if hasSubnet && associatePublicIPAddress {
// If we have a non-default VPC / Subnet specified, we can flag
// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
// You also need to attach Security Groups to the NetworkInterface instead of the instance,
// to avoid: Network interfaces and an instance-level security groups may not be specified on
// the same request
ni := ec2.InstanceNetworkInterfaceSpecification{
AssociatePublicIPAddress: aws.Boolean(associatePublicIPAddress),
DeviceIndex: aws.Integer(0),
SubnetID: aws.String(subnetID),
}
if v, ok := d.GetOk("private_ip"); ok {
ni.PrivateIPAddress = aws.String(v.(string))
}
if len(groups) > 0 {
ni.Groups = groups
}
runOpts.NetworkInterfaces = []ec2.InstanceNetworkInterfaceSpecification{ni}
} else {
if subnetID != "" {
runOpts.SubnetID = aws.String(subnetID)
}
if v, ok := d.GetOk("private_ip"); ok {
runOpts.PrivateIPAddress = aws.String(v.(string))
}
if runOpts.SubnetID != nil &&
*runOpts.SubnetID != "" {
runOpts.SecurityGroupIDs = groups
} else {
runOpts.SecurityGroups = groups
}
}
if v, ok := d.GetOk("key_name"); ok {
runOpts.KeyName = aws.String(v.(string))
}
blockDevices := make([]interface{}, 0)
if v := d.Get("block_device"); v != nil {
@ -311,24 +362,27 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
}
if len(blockDevices) > 0 {
runOpts.BlockDevices = make([]ec2.BlockDeviceMapping, len(blockDevices))
runOpts.BlockDeviceMappings = make([]ec2.BlockDeviceMapping, len(blockDevices))
for i, v := range blockDevices {
bd := v.(map[string]interface{})
runOpts.BlockDevices[i].DeviceName = bd["device_name"].(string)
runOpts.BlockDevices[i].VolumeType = bd["volume_type"].(string)
runOpts.BlockDevices[i].VolumeSize = int64(bd["volume_size"].(int))
runOpts.BlockDevices[i].DeleteOnTermination = bd["delete_on_termination"].(bool)
if v, ok := bd["virtual_name"].(string); ok {
runOpts.BlockDevices[i].VirtualName = v
runOpts.BlockDeviceMappings[i].DeviceName = aws.String(bd["device_name"].(string))
runOpts.BlockDeviceMappings[i].EBS = &ec2.EBSBlockDevice{
VolumeType: aws.String(bd["volume_type"].(string)),
VolumeSize: aws.Integer(bd["volume_size"].(int)),
DeleteOnTermination: aws.Boolean(bd["delete_on_termination"].(bool)),
}
if v, ok := bd["snapshot_id"].(string); ok {
runOpts.BlockDevices[i].SnapshotId = v
if v, ok := bd["virtual_name"].(string); ok {
runOpts.BlockDeviceMappings[i].VirtualName = aws.String(v)
}
if v, ok := bd["snapshot_id"].(string); ok && v != "" {
runOpts.BlockDeviceMappings[i].EBS.SnapshotID = aws.String(v)
}
if v, ok := bd["encrypted"].(bool); ok {
runOpts.BlockDevices[i].Encrypted = v
runOpts.BlockDeviceMappings[i].EBS.Encrypted = aws.Boolean(v)
}
if v, ok := bd["iops"].(int); ok {
runOpts.BlockDevices[i].IOPS = int64(v)
if v, ok := bd["iops"].(int); ok && v > 0 {
runOpts.BlockDeviceMappings[i].EBS.IOPS = aws.Integer(v)
}
}
}
@ -341,21 +395,21 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
}
instance := &runResp.Instances[0]
log.Printf("[INFO] Instance ID: %s", instance.InstanceId)
log.Printf("[INFO] Instance ID: %s", *instance.InstanceID)
// Store the resulting ID so we can look this up later
d.SetId(instance.InstanceId)
d.SetId(*instance.InstanceID)
// Wait for the instance to become running so we can get some attributes
// that aren't available until later.
log.Printf(
"[DEBUG] Waiting for instance (%s) to become running",
instance.InstanceId)
*instance.InstanceID)
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "running",
Refresh: InstanceStateRefreshFunc(ec2conn, instance.InstanceId),
Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceID),
Timeout: 10 * time.Minute,
Delay: 10 * time.Second,
MinTimeout: 3 * time.Second,
@ -365,16 +419,18 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return fmt.Errorf(
"Error waiting for instance (%s) to become ready: %s",
instance.InstanceId, err)
*instance.InstanceID, err)
}
instance = instanceRaw.(*ec2.Instance)
// Initialize the connection info
d.SetConnInfo(map[string]string{
"type": "ssh",
"host": instance.PublicIpAddress,
})
if instance.PublicIPAddress != nil {
d.SetConnInfo(map[string]string{
"type": "ssh",
"host": *instance.PublicIPAddress,
})
}
// Set our attributes
if err := resourceAwsInstanceRead(d, meta); err != nil {
@ -388,11 +444,13 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
resp, err := ec2conn.Instances([]string{d.Id()}, ec2.NewFilter())
resp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesRequest{
InstanceIDs: []string{d.Id()},
})
if err != nil {
// If the instance was not found, return nil so that we can show
// that the instance is gone.
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
d.SetId("")
return nil
}
@ -410,28 +468,33 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
instance := &resp.Reservations[0].Instances[0]
// If the instance is terminated, then it is gone
if instance.State.Name == "terminated" {
if *instance.State.Name == "terminated" {
d.SetId("")
return nil
}
d.Set("availability_zone", instance.AvailZone)
d.Set("availability_zone", instance.Placement.AvailabilityZone)
d.Set("key_name", instance.KeyName)
d.Set("public_dns", instance.DNSName)
d.Set("public_ip", instance.PublicIpAddress)
d.Set("public_dns", instance.PublicDNSName)
d.Set("public_ip", instance.PublicIPAddress)
d.Set("private_dns", instance.PrivateDNSName)
d.Set("private_ip", instance.PrivateIpAddress)
d.Set("subnet_id", instance.SubnetId)
d.Set("ebs_optimized", instance.EbsOptimized)
d.Set("private_ip", instance.PrivateIPAddress)
d.Set("subnet_id", instance.SubnetID)
if len(instance.NetworkInterfaces) > 0 {
d.Set("subnet_id", instance.NetworkInterfaces[0].SubnetID)
} else {
d.Set("subnet_id", instance.SubnetID)
}
d.Set("ebs_optimized", instance.EBSOptimized)
d.Set("tags", tagsToMap(instance.Tags))
d.Set("tenancy", instance.Tenancy)
d.Set("tenancy", instance.Placement.Tenancy)
// Determine whether we're referring to security groups with
// IDs or names. We use a heuristic to figure this out. By default,
// we use IDs if we're in a VPC. However, if we previously had an
// all-name list of security groups, we use names. Or, if we had any
// IDs, we use IDs.
useID := instance.SubnetId != ""
useID := *instance.SubnetID != ""
if v := d.Get("security_groups"); v != nil {
match := false
for _, v := range v.(*schema.Set).List() {
@ -448,24 +511,26 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
sgs := make([]string, len(instance.SecurityGroups))
for i, sg := range instance.SecurityGroups {
if useID {
sgs[i] = sg.Id
sgs[i] = *sg.GroupID
} else {
sgs[i] = sg.Name
sgs[i] = *sg.GroupName
}
}
d.Set("security_groups", sgs)
blockDevices := make(map[string]ec2.BlockDevice)
for _, bd := range instance.BlockDevices {
blockDevices[bd.VolumeId] = bd
blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
for _, bd := range instance.BlockDeviceMappings {
blockDevices[*bd.EBS.VolumeID] = bd
}
volIDs := make([]string, 0, len(blockDevices))
for volID := range blockDevices {
volIDs = append(volIDs, volID)
for _, vol := range blockDevices {
volIDs = append(volIDs, *vol.EBS.VolumeID)
}
volResp, err := ec2conn.Volumes(volIDs, ec2.NewFilter())
volResp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesRequest{
VolumeIDs: volIDs,
})
if err != nil {
return err
}
@ -473,28 +538,25 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
nonRootBlockDevices := make([]map[string]interface{}, 0)
rootBlockDevice := make([]interface{}, 0, 1)
for _, vol := range volResp.Volumes {
volSize, err := strconv.Atoi(vol.Size)
if err != nil {
return err
}
blockDevice := make(map[string]interface{})
blockDevice["device_name"] = blockDevices[vol.VolumeId].DeviceName
blockDevice["volume_type"] = vol.VolumeType
blockDevice["volume_size"] = volSize
blockDevice["device_name"] = *blockDevices[*vol.VolumeID].DeviceName
blockDevice["volume_type"] = *vol.VolumeType
blockDevice["volume_size"] = *vol.Size
if vol.IOPS != nil {
blockDevice["iops"] = *vol.IOPS
}
blockDevice["delete_on_termination"] =
blockDevices[vol.VolumeId].DeleteOnTermination
*blockDevices[*vol.VolumeID].EBS.DeleteOnTermination
// If this is the root device, save it. We stop here since we
// can't put invalid keys into this map.
if blockDevice["device_name"] == instance.RootDeviceName {
if blockDevice["device_name"] == *instance.RootDeviceName {
rootBlockDevice = []interface{}{blockDevice}
continue
}
blockDevice["snapshot_id"] = vol.SnapshotId
blockDevice["encrypted"] = vol.Encrypted
blockDevice["iops"] = vol.IOPS
blockDevice["snapshot_id"] = *vol.SnapshotID
blockDevice["encrypted"] = *vol.Encrypted
nonRootBlockDevices = append(nonRootBlockDevices, blockDevice)
}
d.Set("block_device", nonRootBlockDevices)
@ -505,13 +567,17 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
opts := new(ec2.ModifyInstance)
opts.SetSourceDestCheck = true
opts.SourceDestCheck = d.Get("source_dest_check").(bool)
opts := new(ec2.ModifyInstanceAttributeRequest)
log.Printf("[INFO] Modifying instance %s: %#v", d.Id(), opts)
if _, err := ec2conn.ModifyInstance(d.Id(), opts); err != nil {
err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeRequest{
InstanceID: aws.String(d.Id()),
SourceDestCheck: &ec2.AttributeBooleanValue{
Value: aws.Boolean(d.Get("source_dest_check").(bool)),
},
})
if err != nil {
return err
}
@ -531,7 +597,10 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
log.Printf("[INFO] Terminating instance: %s", d.Id())
if _, err := ec2conn.TerminateInstances([]string{d.Id()}); err != nil {
req := &ec2.TerminateInstancesRequest{
InstanceIDs: []string{d.Id()},
}
if _, err := ec2conn.TerminateInstances(req); err != nil {
return fmt.Errorf("Error terminating instance: %s", err)
}
@ -563,9 +632,11 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
// an EC2 instance.
func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
resp, err := conn.Instances([]string{instanceID}, ec2.NewFilter())
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
InstanceIDs: []string{instanceID},
})
if err != nil {
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
// Set this to nil as if we didn't find anything.
resp = nil
} else {
@ -581,7 +652,7 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRe
}
i := &resp.Reservations[0].Instances[0]
return i, i.State.Name, nil
return i, *i.State.Name, nil
}
}

View File

@ -5,24 +5,25 @@ import (
"reflect"
"testing"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
)
func TestAccAWSInstance_normal(t *testing.T) {
var v ec2.Instance
testCheck := func(*terraform.State) error {
if v.AvailZone != "us-west-2a" {
return fmt.Errorf("bad availability zone: %#v", v.AvailZone)
if *v.Placement.AvailabilityZone != "us-west-2a" {
return fmt.Errorf("bad availability zone: %#v", *v.Placement.AvailabilityZone)
}
if len(v.SecurityGroups) == 0 {
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
}
if v.SecurityGroups[0].Name != "tf_test_foo" {
if *v.SecurityGroups[0].GroupName != "tf_test_foo" {
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
}
@ -73,9 +74,9 @@ func TestAccAWSInstance_blockDevices(t *testing.T) {
return func(*terraform.State) error {
// Map out the block devices by name, which should be unique.
blockDevices := make(map[string]ec2.BlockDevice)
for _, blockDevice := range v.BlockDevices {
blockDevices[blockDevice.DeviceName] = blockDevice
blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
for _, blockDevice := range v.BlockDeviceMappings {
blockDevices[*blockDevice.DeviceName] = blockDevice
}
// Check if the root block device exists.
@ -147,8 +148,8 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
testCheck := func(enabled bool) resource.TestCheckFunc {
return func(*terraform.State) error {
if v.SourceDestCheck != enabled {
return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck)
if *v.SourceDestCheck != enabled {
return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
}
return nil
@ -206,7 +207,26 @@ func TestAccAWSInstance_vpc(t *testing.T) {
})
}
func TestAccInstance_tags(t *testing.T) {
func TestAccInstance_NetworkInstanceSecurityGroups(t *testing.T) {
var v ec2.Instance
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccInstanceNetworkInstanceSecurityGroups,
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists(
"aws_instance.foo_instance", &v),
),
},
},
})
}
func TestAccAWSInstance_tags(t *testing.T) {
var v ec2.Instance
resource.Test(t, resource.TestCase{
@ -236,13 +256,13 @@ func TestAccInstance_tags(t *testing.T) {
})
}
func TestAccInstance_privateIP(t *testing.T) {
func TestAccAWSInstance_privateIP(t *testing.T) {
var v ec2.Instance
testCheckPrivateIP := func() resource.TestCheckFunc {
return func(*terraform.State) error {
if v.PrivateIpAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress)
if *v.PrivateIPAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
}
return nil
@ -265,13 +285,13 @@ func TestAccInstance_privateIP(t *testing.T) {
})
}
func TestAccInstance_associatePublicIPAndPrivateIP(t *testing.T) {
func TestAccAWSInstance_associatePublicIPAndPrivateIP(t *testing.T) {
var v ec2.Instance
testCheckPrivateIP := func() resource.TestCheckFunc {
return func(*terraform.State) error {
if v.PrivateIpAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress)
if *v.PrivateIPAddress != "10.1.1.42" {
return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
}
return nil
@ -303,8 +323,9 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
}
// Try to find the resource
resp, err := conn.Instances(
[]string{rs.Primary.ID}, ec2.NewFilter())
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
InstanceIDs: []string{rs.Primary.ID},
})
if err == nil {
if len(resp.Reservations) > 0 {
return fmt.Errorf("still exist.")
@ -314,7 +335,7 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
}
// Verify the error is what we want
ec2err, ok := err.(*ec2.Error)
ec2err, ok := err.(aws.APIError)
if !ok {
return err
}
@ -338,8 +359,9 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.Instances(
[]string{rs.Primary.ID}, ec2.NewFilter())
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
InstanceIDs: []string{rs.Primary.ID},
})
if err != nil {
return err
}
@ -389,7 +411,7 @@ resource "aws_instance" "foo" {
instance_type = "m1.small"
security_groups = ["${aws_security_group.tf_test_foo.name}"]
user_data = "foo"
user_data = "foo:-with-character's"
}
`
@ -530,3 +552,49 @@ resource "aws_instance" "foo" {
private_ip = "10.1.1.42"
}
`
const testAccInstanceNetworkInstanceSecurityGroups = `
resource "aws_internet_gateway" "gw" {
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
tags {
Name = "tf-network-test"
}
}
resource "aws_security_group" "tf_test_foo" {
name = "tf_test_foo"
description = "foo"
vpc_id="${aws_vpc.foo.id}"
ingress {
protocol = "icmp"
from_port = -1
to_port = -1
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_subnet" "foo" {
cidr_block = "10.1.1.0/24"
vpc_id = "${aws_vpc.foo.id}"
}
resource "aws_instance" "foo_instance" {
ami = "ami-21f78e11"
instance_type = "t1.micro"
security_groups = ["${aws_security_group.tf_test_foo.id}"]
subnet_id = "${aws_subnet.foo.id}"
associate_public_ip_address = true
depends_on = ["aws_internet_gateway.gw"]
}
resource "aws_eip" "foo_eip" {
instance = "${aws_instance.foo_instance.id}"
vpc = true
depends_on = ["aws_internet_gateway.gw"]
}
`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,10 @@ import (
"fmt"
"testing"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
)
func TestAccAWSRouteTableAssociation(t *testing.T) {
@ -45,11 +46,12 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error {
}
// Try to find the resource
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.Attributes["route_table_Id"]}, ec2.NewFilter())
resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]},
})
if err != nil {
// Verify the error is what we want
ec2err, ok := err.(*ec2.Error)
ec2err, ok := err.(aws.APIError)
if !ok {
return err
}
@ -62,7 +64,7 @@ func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error {
rt := resp.RouteTables[0]
if len(rt.Associations) > 0 {
return fmt.Errorf(
"route table %s has associations", rt.RouteTableId)
"route table %s has associations", *rt.RouteTableID)
}
}
@ -82,8 +84,9 @@ func testAccCheckRouteTableAssociationExists(n string, v *ec2.RouteTable) resour
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.Attributes["route_table_id"]}, ec2.NewFilter())
resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
RouteTableIDs: []string{rs.Primary.Attributes["route_table_id"]},
})
if err != nil {
return err
}

View File

@ -4,9 +4,10 @@ import (
"fmt"
"testing"
"github.com/hashicorp/aws-sdk-go/aws"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
)
func TestAccAWSRouteTable_normal(t *testing.T) {
@ -19,7 +20,7 @@ func TestAccAWSRouteTable_normal(t *testing.T) {
routes := make(map[string]ec2.Route)
for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r
routes[*r.DestinationCIDRBlock] = r
}
if _, ok := routes["10.1.0.0/16"]; !ok {
@ -39,7 +40,7 @@ func TestAccAWSRouteTable_normal(t *testing.T) {
routes := make(map[string]ec2.Route)
for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r
routes[*r.DestinationCIDRBlock] = r
}
if _, ok := routes["10.1.0.0/16"]; !ok {
@ -91,7 +92,7 @@ func TestAccAWSRouteTable_instance(t *testing.T) {
routes := make(map[string]ec2.Route)
for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r
routes[*r.DestinationCIDRBlock] = r
}
if _, ok := routes["10.1.0.0/16"]; !ok {
@ -158,8 +159,9 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error {
}
// Try to find the resource
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.ID}, ec2.NewFilter())
resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
RouteTableIDs: []string{rs.Primary.ID},
})
if err == nil {
if len(resp.RouteTables) > 0 {
return fmt.Errorf("still exist.")
@ -169,7 +171,7 @@ func testAccCheckRouteTableDestroy(s *terraform.State) error {
}
// Verify the error is what we want
ec2err, ok := err.(*ec2.Error)
ec2err, ok := err.(aws.APIError)
if !ok {
return err
}
@ -193,8 +195,9 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.ID}, ec2.NewFilter())
resp, err := conn.DescribeRouteTables(&ec2.DescribeRouteTablesRequest{
RouteTableIDs: []string{rs.Primary.ID},
})
if err != nil {
return err
}
@ -208,7 +211,10 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
}
}
func TestAccAWSRouteTable_vpcPeering(t *testing.T) {
// TODO: re-enable this test.
// VPC Peering connections are prefixed with pcx
// Right now there is no VPC Peering resource
func _TestAccAWSRouteTable_vpcPeering(t *testing.T) {
var v ec2.RouteTable
testCheck := func(*terraform.State) error {
@ -218,7 +224,7 @@ func TestAccAWSRouteTable_vpcPeering(t *testing.T) {
routes := make(map[string]ec2.Route)
for _, r := range v.Routes {
routes[r.DestinationCidrBlock] = r
routes[*r.DestinationCIDRBlock] = r
}
if _, ok := routes["10.1.0.0/16"]; !ok {
@ -345,6 +351,9 @@ resource "aws_route_table" "foo" {
}
`
// TODO: re-enable this test.
// VPC Peering connections are prefixed with pcx
// Right now there is no VPC Peering resource
const testAccRouteTableVpcPeeringConfig = `
resource "aws_vpc" "foo" {
cidr_block = "10.1.0.0/16"
@ -359,7 +368,7 @@ resource "aws_route_table" "foo" {
route {
cidr_block = "10.2.0.0/16"
vpc_peering_connection_id = "vpc-12345"
vpc_peering_connection_id = "pcx-12345"
}
}
`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,9 @@ import (
"fmt"
"testing"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
)
func TestAccAWSVPCPeeringConnection_normal(t *testing.T) {
@ -35,10 +35,13 @@ func testAccCheckAWSVpcPeeringConnectionDestroy(s *terraform.State) error {
continue
}
describe, err := conn.DescribeVpcPeeringConnection([]string{rs.Primary.ID}, ec2.NewFilter())
describe, err := conn.DescribeVPCPeeringConnections(
&ec2.DescribeVPCPeeringConnectionsRequest{
VPCPeeringConnectionIDs: []string{rs.Primary.ID},
})
if err == nil {
if len(describe.VpcPeeringConnections) != 0 {
if len(describe.VPCPeeringConnections) != 0 {
return fmt.Errorf("vpc peering connection still exists")
}
}
@ -68,11 +71,10 @@ resource "aws_vpc" "foo" {
}
resource "aws_vpc" "bar" {
cidr_block = "10.0.1.0/16"
cidr_block = "10.1.0.0/16"
}
resource "aws_vpc_peering_connection" "foo" {
peer_owner_id = "12345"
vpc_id = "${aws_vpc.foo.id}"
peer_vpc_id = "${aws_vpc.bar.id}"
}

View File

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

View File

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

View File

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

View File

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

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"
"testing"
"github.com/hashicorp/aws-sdk-go/gen/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/mitchellh/goamz/ec2"
)
func TestDiffTags(t *testing.T) {

View File

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

View File

@ -51,7 +51,8 @@ var CLOUDSTACK_NETWORK_1_OFFERING = ""
var CLOUDSTACK_NETWORK_1_IPADDRESS = ""
var CLOUDSTACK_NETWORK_2 = ""
var CLOUDSTACK_NETWORK_2_IPADDRESS = ""
var CLOUDSTACK_VPC_CIDR = ""
var CLOUDSTACK_VPC_CIDR_1 = ""
var CLOUDSTACK_VPC_CIDR_2 = ""
var CLOUDSTACK_VPC_OFFERING = ""
var CLOUDSTACK_VPC_NETWORK_CIDR = ""
var CLOUDSTACK_VPC_NETWORK_OFFERING = ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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)
case "vpc":
uuid, err = cs.VPC.GetVPCID(value)
case "template":
uuid, err = cs.Template.GetTemplateID(value, "executable")
case "network":
uuid, err = cs.Network.GetNetworkID(value)
case "zone":
@ -71,6 +69,22 @@ func retrieveUUID(cs *cloudstack.CloudStackClient, name, value string) (uuid str
return uuid, nil
}
func retrieveTemplateUUID(cs *cloudstack.CloudStackClient, zoneid, value string) (uuid string, e *retrieveError) {
// If the supplied value isn't a UUID, try to retrieve the UUID ourselves
if isUUID(value) {
return value, nil
}
log.Printf("[DEBUG] Retrieving UUID of template: %s", value)
uuid, err := cs.Template.GetTemplateID(value, "executable", zoneid)
if err != nil {
return uuid, &retrieveError{name: "template", value: value, err: err}
}
return uuid, nil
}
func isUUID(s string) bool {
re := regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)
return re.MatchString(s)

View File

@ -21,6 +21,7 @@ func Provider() terraform.ResourceProvider {
"digitalocean_domain": resourceDigitalOceanDomain(),
"digitalocean_droplet": resourceDigitalOceanDroplet(),
"digitalocean_record": resourceDigitalOceanRecord(),
"digitalocean_ssh_key": resourceDigitalOceanSSHKey(),
},
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 (
"errors"
"fmt"
"strconv"
"github.com/hashicorp/terraform/terraform"
)
@ -24,6 +25,31 @@ type Resource struct {
// resource.
Schema map[string]*Schema
// SchemaVersion is the version number for this resource's Schema
// definition. The current SchemaVersion stored in the state for each
// resource. Provider authors can increment this version number
// when Schema semantics change. If the State's SchemaVersion is less than
// the current SchemaVersion, the InstanceState is yielded to the
// MigrateState callback, where the provider can make whatever changes it
// needs to update the state to be compatible to the latest version of the
// Schema.
//
// When unset, SchemaVersion defaults to 0, so provider authors can start
// their Versioning at any integer >= 1
SchemaVersion int
// MigrateState is responsible for updating an InstanceState with an old
// version to the format expected by the current version of the Schema.
//
// It is called during Refresh if the State's stored SchemaVersion is less
// than the current SchemaVersion of the Resource.
//
// The function is yielded the state's stored SchemaVersion and a pointer to
// the InstanceState that needs updating, as well as the configured
// provider's configured meta interface{}, in case the migration process
// needs to make any remote API calls.
MigrateState StateMigrateFunc
// The functions below are the CRUD operations for this resource.
//
// The only optional operation is Update. If Update is not implemented,
@ -69,6 +95,10 @@ type DeleteFunc func(*ResourceData, interface{}) error
// See Resource documentation.
type ExistsFunc func(*ResourceData, interface{}) (bool, error)
// See Resource documentation.
type StateMigrateFunc func(
int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error)
// Apply creates, updates, and/or deletes a resource.
func (r *Resource) Apply(
s *terraform.InstanceState,
@ -158,6 +188,14 @@ func (r *Resource) Refresh(
}
}
needsMigration, stateSchemaVersion := r.checkSchemaVersion(s)
if needsMigration && r.MigrateState != nil {
s, err := r.MigrateState(stateSchemaVersion, s, meta)
if err != nil {
return s, err
}
}
data, err := schemaMap(r.Schema).Data(s, nil)
if err != nil {
return s, err
@ -169,6 +207,13 @@ func (r *Resource) Refresh(
state = nil
}
if state != nil && r.SchemaVersion > 0 {
if state.Meta == nil {
state.Meta = make(map[string]string)
}
state.Meta["schema_version"] = strconv.Itoa(r.SchemaVersion)
}
return state, err
}
@ -189,3 +234,10 @@ func (r *Resource) InternalValidate() error {
return schemaMap(r.Schema).InternalValidate()
}
// Determines if a given InstanceState needs to be migrated by checking the
// stored version number with the current SchemaVersion
func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) {
stateSchemaVersion, _ := strconv.Atoi(is.Meta["schema_version"])
return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion
}

View File

@ -3,6 +3,7 @@ package schema
import (
"fmt"
"reflect"
"strconv"
"testing"
"github.com/hashicorp/terraform/terraform"
@ -478,3 +479,218 @@ func TestResourceRefresh_noExists(t *testing.T) {
t.Fatalf("should have no state")
}
}
func TestResourceRefresh_needsMigration(t *testing.T) {
// Schema v2 it deals only in newfoo, which tracks foo as an int
r := &Resource{
SchemaVersion: 2,
Schema: map[string]*Schema{
"newfoo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Read = func(d *ResourceData, m interface{}) error {
return d.Set("newfoo", d.Get("newfoo").(int)+1)
}
r.MigrateState = func(
v int,
s *terraform.InstanceState,
meta interface{}) (*terraform.InstanceState, error) {
// Real state migration functions will probably switch on this value,
// but we'll just assert on it for now.
if v != 1 {
t.Fatalf("Expected StateSchemaVersion to be 1, got %d", v)
}
if meta != 42 {
t.Fatal("Expected meta to be passed through to the migration function")
}
oldfoo, err := strconv.ParseFloat(s.Attributes["oldfoo"], 64)
if err != nil {
t.Fatalf("err: %#v", err)
}
s.Attributes["newfoo"] = strconv.Itoa((int(oldfoo * 10)))
delete(s.Attributes, "oldfoo")
return s, nil
}
// State is v1 and deals in oldfoo, which tracked foo as a float at 1/10th
// the scale of newfoo
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"oldfoo": "1.2",
},
Meta: map[string]string{
"schema_version": "1",
},
}
actual, err := r.Refresh(s, 42)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
"schema_version": "2",
},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
}
}
func TestResourceRefresh_noMigrationNeeded(t *testing.T) {
r := &Resource{
SchemaVersion: 2,
Schema: map[string]*Schema{
"newfoo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Read = func(d *ResourceData, m interface{}) error {
return d.Set("newfoo", d.Get("newfoo").(int)+1)
}
r.MigrateState = func(
v int,
s *terraform.InstanceState,
meta interface{}) (*terraform.InstanceState, error) {
t.Fatal("Migrate function shouldn't be called!")
return nil, nil
}
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"newfoo": "12",
},
Meta: map[string]string{
"schema_version": "2",
},
}
actual, err := r.Refresh(s, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
"schema_version": "2",
},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
}
}
func TestResourceRefresh_stateSchemaVersionUnset(t *testing.T) {
r := &Resource{
// Version 1 > Version 0
SchemaVersion: 1,
Schema: map[string]*Schema{
"newfoo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Read = func(d *ResourceData, m interface{}) error {
return d.Set("newfoo", d.Get("newfoo").(int)+1)
}
r.MigrateState = func(
v int,
s *terraform.InstanceState,
meta interface{}) (*terraform.InstanceState, error) {
s.Attributes["newfoo"] = s.Attributes["oldfoo"]
return s, nil
}
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"oldfoo": "12",
},
}
actual, err := r.Refresh(s, nil)
if err != nil {
t.Fatalf("err: %s", err)
}
expected := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"id": "bar",
"newfoo": "13",
},
Meta: map[string]string{
"schema_version": "1",
},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\n\nexpected: %#v\ngot: %#v", expected, actual)
}
}
func TestResourceRefresh_migrateStateErr(t *testing.T) {
r := &Resource{
SchemaVersion: 2,
Schema: map[string]*Schema{
"newfoo": &Schema{
Type: TypeInt,
Optional: true,
},
},
}
r.Read = func(d *ResourceData, m interface{}) error {
t.Fatal("Read should never be called!")
return nil
}
r.MigrateState = func(
v int,
s *terraform.InstanceState,
meta interface{}) (*terraform.InstanceState, error) {
return s, fmt.Errorf("triggering an error")
}
s := &terraform.InstanceState{
ID: "bar",
Attributes: map[string]string{
"oldfoo": "12",
},
}
_, err := r.Refresh(s, nil)
if err == nil {
t.Fatal("expected error, but got none!")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -832,6 +832,11 @@ type InstanceState struct {
// that is necessary for the Terraform run to complete, but is not
// persisted to a state file.
Ephemeral EphemeralState `json:"-"`
// Meta is a simple K/V map that is persisted to the State but otherwise
// ignored by Terraform core. It's meant to be used for accounting by
// external client code.
Meta map[string]string `json:"meta,omitempty"`
}
func (i *InstanceState) init() {

View File

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

View File

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

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
is persisted back to the remote server.
In this mode, users do not need to durably store the state using version
control or shared storaged.
control or shared storage.
## Usage

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

View File

@ -29,6 +29,18 @@ The following arguments are supported:
* `vpc_id` - (Required) The VPC ID to create in.
* `tags` - (Optional) A mapping of tags to assign to the resource.
-> **Note:** It's recommended to denote that the AWS Instance or Elastic IP depends on the Internet Gateway. For example:
resource "aws_internet_gateway" "gw" {
vpc_id = "${aws_vpc.main.id}"
}
resource "aws_instance" "foo" {
depends_on = ["aws_internet_gateway.gw"]
}
## Attributes Reference
The following attributes are exported:

View File

@ -56,4 +56,4 @@ The following attributes are exported:
## Notes
You still have to accept the peering with the aws console, aws-cli or goamz
You still have to accept the peering with the aws console, aws-cli or aws-sdk-go.

View File

@ -58,4 +58,4 @@ The `rule` block supports:
The following attributes are exported:
* `ID` - The network ID for which the egress firewall rules are created.
* `id` - The network ID for which the egress firewall rules are created.

View File

@ -58,4 +58,4 @@ The `rule` block supports:
The following attributes are exported:
* `ID` - The IP address ID for which the firewall rules are created.
* `id` - The IP address ID for which the firewall rules are created.

View File

@ -66,4 +66,4 @@ The `rule` block supports:
The following attributes are exported:
* `ID` - The ACL ID for which the rules are created.
* `id` - The ACL ID for which the rules are created.

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 %>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/index.html">&laquo; Documentation Home</a>
</li>
<% content_for :sidebar do %>
<div class="docs-sidebar hidden-print affix-top" role="complementary">
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/index.html">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-cloudstack-index") %>>
<a href="/docs/providers/cloudstack/index.html">CloudStack Provider</a>
</li>
<li<%= sidebar_current("docs-cloudstack-index") %>>
<a href="/docs/providers/cloudstack/index.html">CloudStack Provider</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource") %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-cloudstack-resource-disk") %>>
<a href="/docs/providers/cloudstack/r/disk.html">cloudstack_disk</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource") %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-cloudstack-resource-disk") %>>
<a href="/docs/providers/cloudstack/r/disk.html">cloudstack_disk</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-egress-firewall") %>>
<a href="/docs/providers/cloudstack/r/egress_firewall.html">cloudstack_egress_firewall</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-egress-firewall") %>>
<a href="/docs/providers/cloudstack/r/egress_firewall.html">cloudstack_egress_firewall</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-firewall") %>>
<a href="/docs/providers/cloudstack/r/firewall.html">cloudstack_firewall</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-firewall") %>>
<a href="/docs/providers/cloudstack/r/firewall.html">cloudstack_firewall</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-instance") %>>
<a href="/docs/providers/cloudstack/r/instance.html">cloudstack_instance</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-instance") %>>
<a href="/docs/providers/cloudstack/r/instance.html">cloudstack_instance</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-ipaddress") %>>
<a href="/docs/providers/cloudstack/r/ipaddress.html">cloudstack_ipaddress</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-ipaddress") %>>
<a href="/docs/providers/cloudstack/r/ipaddress.html">cloudstack_ipaddress</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network") %>>
<a href="/docs/providers/cloudstack/r/network.html">cloudstack_network</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network") %>>
<a href="/docs/providers/cloudstack/r/network.html">cloudstack_network</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl") %>>
<a href="/docs/providers/cloudstack/r/network_acl.html">cloudstack_network_acl</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl") %>>
<a href="/docs/providers/cloudstack/r/network_acl.html">cloudstack_network_acl</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl-rule") %>>
<a href="/docs/providers/cloudstack/r/network_acl_rule.html">cloudstack_network_acl_rule</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-network-acl-rule") %>>
<a href="/docs/providers/cloudstack/r/network_acl_rule.html">cloudstack_network_acl_rule</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-nic") %>>
<a href="/docs/providers/cloudstack/r/nic.html">cloudstack_nic</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-nic") %>>
<a href="/docs/providers/cloudstack/r/nic.html">cloudstack_nic</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-port-forward") %>>
<a href="/docs/providers/cloudstack/r/port_forward.html">cloudstack_port_forward</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-port-forward") %>>
<a href="/docs/providers/cloudstack/r/port_forward.html">cloudstack_port_forward</a>
</li>
<li<%= sidebar_current("docs-cloudstack-resource-vpc") %>>
<a href="/docs/providers/cloudstack/r/vpc.html">cloudstack_vpc</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<li<%= sidebar_current("docs-cloudstack-resource-vpc") %>>
<a href="/docs/providers/cloudstack/r/vpc.html">cloudstack_vpc</a>
</li>
<%= yield %>
<% end %>
<li<%= sidebar_current("docs-cloudstack-resource-vpn-gateway") %>>
<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") %>>
<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>
</ul>
</li>