Merge branch 'master' into aws-go-vpn

This commit is contained in:
Dan Everton 2015-03-12 08:09:27 +10:00
commit 9f50b048e1
31 changed files with 1100 additions and 679 deletions

View File

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

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

@ -6,14 +6,14 @@ import (
"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 {
@ -253,7 +253,7 @@ func resourceAwsInstance() *schema.Resource {
}
func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// Figure out user data
userData := ""
@ -261,38 +261,84 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
userData = 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
if v := d.Get("associate_public_ip_address"); v != nil {
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)
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))
}
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 v, ok := d.GetOk("key_name"); ok {
runOpts.KeyName = aws.String(v.(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
var groups []string
for _, v := range v.(*schema.Set).List() {
str := v.(string)
var g ec2.SecurityGroup
if runOpts.SubnetId != "" {
g.Id = str
} else {
g.Name = str
groups = append(groups, str)
}
runOpts.SecurityGroups = append(runOpts.SecurityGroups, g)
if runOpts.SubnetID != nil &&
*runOpts.SubnetID != "" {
runOpts.SecurityGroupIDs = groups
} else {
runOpts.SecurityGroups = groups
}
}
@ -311,24 +357,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 +390,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 +414,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
if instance.PublicIPAddress != nil {
d.SetConnInfo(map[string]string{
"type": "ssh",
"host": instance.PublicIpAddress,
"host": *instance.PublicIPAddress,
})
}
// Set our attributes
if err := resourceAwsInstanceRead(d, meta); err != nil {
@ -386,13 +437,15 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
}
func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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 +463,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("tags", tagsToMap(instance.Tags))
d.Set("tenancy", instance.Tenancy)
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", tagsToMapSDK(instance.Tags))
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 +506,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 +533,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)
@ -504,21 +561,25 @@ 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)
ec2conn := meta.(*AWSClient).awsEC2conn
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
}
// TODO(mitchellh): wait for the attributes we modified to
// persist the change...
if err := setTags(ec2conn, d); err != nil {
if err := setTagsSDK(ec2conn, d); err != nil {
return err
} else {
d.SetPartial("tags")
@ -528,10 +589,13 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
}
func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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 +627,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 +647,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,7 @@ func TestAccAWSInstance_vpc(t *testing.T) {
})
}
func TestAccInstance_tags(t *testing.T) {
func TestAccAWSInstance_tags(t *testing.T) {
var v ec2.Instance
resource.Test(t, resource.TestCase{
@ -218,9 +219,9 @@ func TestAccInstance_tags(t *testing.T) {
Config: testAccCheckInstanceConfigTags,
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists("aws_instance.foo", &v),
testAccCheckTags(&v.Tags, "foo", "bar"),
testAccCheckTagsSDK(&v.Tags, "foo", "bar"),
// Guard against regression of https://github.com/hashicorp/terraform/issues/914
testAccCheckTags(&v.Tags, "#", ""),
testAccCheckTagsSDK(&v.Tags, "#", ""),
),
},
@ -228,21 +229,21 @@ func TestAccInstance_tags(t *testing.T) {
Config: testAccCheckInstanceConfigTagsUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckInstanceExists("aws_instance.foo", &v),
testAccCheckTags(&v.Tags, "foo", ""),
testAccCheckTags(&v.Tags, "bar", "baz"),
testAccCheckTagsSDK(&v.Tags, "foo", ""),
testAccCheckTagsSDK(&v.Tags, "bar", "baz"),
),
},
},
})
}
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 +266,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
@ -295,7 +296,7 @@ func TestAccInstance_associatePublicIPAndPrivateIP(t *testing.T) {
}
func testAccCheckInstanceDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_instance" {
@ -303,8 +304,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 +316,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
}
@ -337,9 +339,10 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun
return fmt.Errorf("No ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.Instances(
[]string{rs.Primary.ID}, ec2.NewFilter())
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
InstanceIDs: []string{rs.Primary.ID},
})
if err != nil {
return err
}

View File

@ -43,6 +43,11 @@ func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{})
d.SetId(*ig.InternetGatewayID)
log.Printf("[INFO] InternetGateway ID: %s", d.Id())
err = setTagsSDK(ec2conn, d)
if err != nil {
return err
}
// Attach the new gateway to the correct vpc
return resourceAwsInternetGatewayAttach(d, meta)
}

View File

@ -98,6 +98,7 @@ func TestAccInternetGateway_tags(t *testing.T) {
Config: testAccCheckInternetGatewayConfigTags,
Check: resource.ComposeTestCheckFunc(
testAccCheckInternetGatewayExists("aws_internet_gateway.foo", &v),
testAccCheckTagsSDK(&v.Tags, "foo", "bar"),
),
},

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 {
@ -39,7 +40,7 @@ func resourceAwsMainRouteTableAssociation() *schema.Resource {
}
func resourceAwsMainRouteTableAssociationCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
vpcId := d.Get("vpc_id").(string)
routeTableId := d.Get("route_table_id").(string)
@ -50,23 +51,23 @@ 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
}
func resourceAwsMainRouteTableAssociationRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
mainAssociation, err := findMainRouteTableAssociation(
ec2conn,
@ -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("")
}
@ -87,25 +88,28 @@ func resourceAwsMainRouteTableAssociationRead(d *schema.ResourceData, meta inter
// original_route_table_id - this needs to stay recorded as the AWS-created
// table from VPC creation.
func resourceAwsMainRouteTableAssociationUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
vpcId := d.Get("vpc_id").(string)
routeTableId := d.Get("route_table_id").(string)
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
}
func resourceAwsMainRouteTableAssociationDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
vpcId := d.Get("vpc_id").(string)
originalRouteTableId := d.Get("original_route_table_id").(string)
@ -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

@ -65,15 +65,15 @@ func testAccCheckMainRouteTableAssociation(
return fmt.Errorf("Not found: %s", vpcResource)
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
mainAssociation, err := findMainRouteTableAssociation(conn, vpc.Primary.ID)
if err != nil {
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 {
@ -108,32 +109,34 @@ func resourceAwsNetworkAcl() *schema.Resource {
func resourceAwsNetworkAclCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// 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)
}
func resourceAwsNetworkAclRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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,29 +145,29 @@ 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))
d.Set("tags", tagsToMapSDK(networkAcl.Tags))
return nil
}
func resourceAwsNetworkAclUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
d.Partial(true)
if d.HasChange("ingress") {
@ -190,13 +193,16 @@ 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
}
}
if err := setTags(ec2conn, d); err != nil {
if err := setTagsSDK(ec2conn, d); err != nil {
return err
} else {
d.SetPartial("tags")
@ -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)
}
@ -247,12 +265,15 @@ func updateNetworkAclEntries(d *schema.ResourceData, entryType string, ec2conn *
}
func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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) },
@ -150,14 +151,14 @@ func TestAccAWSNetworkAclsOnlyEgressRules(t *testing.T) {
Config: testAccAWSNetworkAclEgressConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSNetworkAclExists("aws_network_acl.bond", &networkAcl),
testAccCheckTags(&networkAcl.Tags, "foo", "bar"),
testAccCheckTagsSDK(&networkAcl.Tags, "foo", "bar"),
),
},
},
})
}
func TestAccNetworkAcl_SubnetChange(t *testing.T) {
func TestAccAWSNetworkAcl_SubnetChange(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -183,7 +184,7 @@ func TestAccNetworkAcl_SubnetChange(t *testing.T) {
}
func testAccCheckAWSNetworkAclDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_network" {
@ -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 {
@ -223,15 +226,17 @@ func testAccCheckAWSNetworkAclExists(n string, networkAcl *ec2.NetworkAcl) resou
if rs.Primary.ID == "" {
return fmt.Errorf("No Security Group is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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)
}
}
@ -261,21 +266,26 @@ func testAccCheckSubnetIsAssociatedWithAcl(acl string, sub string) resource.Test
networkAcl := s.RootModule().Resources[acl]
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)
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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)
}
@ -286,15 +296,21 @@ func testAccCheckSubnetIsNotAssociatedWithAcl(acl string, subnet string) resourc
networkAcl := s.RootModule().Resources[acl]
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)
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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 {
@ -61,11 +62,11 @@ func resourceAwsRouteTable() *schema.Resource {
}
func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// 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
@ -99,7 +100,7 @@ func resourceAwsRouteTableCreate(d *schema.ResourceData, meta interface{}) error
}
func resourceAwsRouteTableRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(ec2conn, d.Id())()
if err != nil {
@ -110,40 +111,48 @@ 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)
}
d.Set("route", route)
// Tags
d.Set("tags", tagsToMap(rt.Tags))
d.Set("tags", tagsToMapSDK(rt.Tags))
return nil
}
func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// Check if the route set as a whole has changed
if d.HasChange("route") {
@ -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
}
@ -193,7 +203,7 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error
}
}
if err := setTags(ec2conn, d); err != nil {
if err := setTagsSDK(ec2conn, d); err != nil {
return err
} else {
d.SetPartial("tags")
@ -203,7 +213,7 @@ func resourceAwsRouteTableUpdate(d *schema.ResourceData, meta interface{}) error
}
func resourceAwsRouteTableDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// First request the routing table since we'll have to disassociate
// all the subnets first.
@ -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 {
@ -31,30 +32,31 @@ func resourceAwsRouteTableAssociation() *schema.Resource {
}
func resourceAwsRouteTableAssociationCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
log.Printf(
"[INFO] Creating route table association: %s => %s",
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
}
func resourceAwsRouteTableAssociationRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// Get the routing table that this association belongs to
rtRaw, _, err := resourceAwsRouteTableStateRefreshFunc(
@ -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
}
}
@ -86,19 +88,21 @@ func resourceAwsRouteTableAssociationRead(d *schema.ResourceData, meta interface
}
func resourceAwsRouteTableAssociationUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
log.Printf(
"[INFO] Creating route table association: %s => %s",
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,18 +112,21 @@ 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
}
func resourceAwsRouteTableAssociationDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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) {
@ -37,7 +38,7 @@ func TestAccAWSRouteTableAssociation(t *testing.T) {
}
func testAccCheckRouteTableAssociationDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_route_table_association" {
@ -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)
}
}
@ -81,9 +83,10 @@ func testAccCheckRouteTableAssociationExists(n string, v *ec2.RouteTable) resour
return fmt.Errorf("No ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.Attributes["route_table_id"]}, ec2.NewFilter())
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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 {
@ -133,7 +134,7 @@ func TestAccAWSRouteTable_tags(t *testing.T) {
Config: testAccRouteTableConfigTags,
Check: resource.ComposeTestCheckFunc(
testAccCheckRouteTableExists("aws_route_table.foo", &route_table),
testAccCheckTags(&route_table.Tags, "foo", "bar"),
testAccCheckTagsSDK(&route_table.Tags, "foo", "bar"),
),
},
@ -141,8 +142,8 @@ func TestAccAWSRouteTable_tags(t *testing.T) {
Config: testAccRouteTableConfigTagsUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckRouteTableExists("aws_route_table.foo", &route_table),
testAccCheckTags(&route_table.Tags, "foo", ""),
testAccCheckTags(&route_table.Tags, "bar", "baz"),
testAccCheckTagsSDK(&route_table.Tags, "foo", ""),
testAccCheckTagsSDK(&route_table.Tags, "bar", "baz"),
),
},
},
@ -150,7 +151,7 @@ func TestAccAWSRouteTable_tags(t *testing.T) {
}
func testAccCheckRouteTableDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_route_table" {
@ -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
}
@ -192,9 +194,10 @@ func testAccCheckRouteTableExists(n string, v *ec2.RouteTable) resource.TestChec
return fmt.Errorf("No ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
resp, err := conn.DescribeRouteTables(
[]string{rs.Primary.ID}, ec2.NewFilter())
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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 {
@ -141,18 +142,18 @@ func resourceAwsSecurityGroup() *schema.Resource {
}
func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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())
@ -186,7 +187,7 @@ func resourceAwsSecurityGroupCreate(d *schema.ResourceData, meta interface{}) er
}
func resourceAwsSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
sgRaw, _, err := SGStateRefreshFunc(ec2conn, d.Id())()
if err != nil {
@ -197,24 +198,23 @@ 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))
d.Set("tags", tagsToMapSDK(sg.Tags))
return nil
}
func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
sgRaw, _, err := SGStateRefreshFunc(ec2conn, d.Id())()
if err != 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 {
@ -238,7 +239,7 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er
}
}
if err := setTags(ec2conn, d); err != nil {
if err := setTagsSDK(ec2conn, d); err != nil {
return err
}
@ -248,14 +249,16 @@ func resourceAwsSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) er
}
func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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 = flattenSecurityGroupsSDK(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
}
@ -383,6 +396,7 @@ func resourceAwsSecurityGroupUpdateRules(
os := o.(*schema.Set)
ns := n.(*schema.Set)
// TODO: re-munge this when test is updated
remove := expandIPPerms(d.Id(), os.Difference(ns).List())
add := expandIPPerms(d.Id(), ns.Difference(os).List())
@ -396,34 +410,53 @@ func resourceAwsSecurityGroupUpdateRules(
// not have service issues.
if len(remove) > 0 || len(add) > 0 {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
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 +464,6 @@ func resourceAwsSecurityGroupUpdateRules(
}
}
}
return nil
}
@ -439,10 +471,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 +494,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) },
@ -184,30 +186,27 @@ func TestAccAWSSecurityGroup_Change(t *testing.T) {
}
func testAccCheckAWSSecurityGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_security_group" {
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 {
@ -231,20 +230,19 @@ func testAccCheckAWSSecurityGroupExists(n string, group *ec2.SecurityGroupInfo)
return fmt.Errorf("No Security Group is set")
}
conn := testAccProvider.Meta().(*AWSClient).ec2conn
sgs := []ec2.SecurityGroup{
ec2.SecurityGroup{
Id: rs.Primary.ID,
},
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
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) },
@ -298,7 +296,7 @@ func TestAccAWSSecurityGroup_tags(t *testing.T) {
Config: testAccAWSSecurityGroupConfigTags,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSecurityGroupExists("aws_security_group.foo", &group),
testAccCheckTags(&group.Tags, "foo", "bar"),
testAccCheckTagsSDK(&group.Tags, "foo", "bar"),
),
},
@ -306,56 +304,56 @@ func TestAccAWSSecurityGroup_tags(t *testing.T) {
Config: testAccAWSSecurityGroupConfigTagsUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSSecurityGroupExists("aws_security_group.foo", &group),
testAccCheckTags(&group.Tags, "foo", ""),
testAccCheckTags(&group.Tags, "bar", "baz"),
testAccCheckTagsSDK(&group.Tags, "foo", ""),
testAccCheckTagsSDK(&group.Tags, "bar", "baz"),
),
},
},
})
}
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

@ -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 {
@ -22,6 +23,7 @@ func resourceAwsVpcPeeringConnection() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("AWS_ACCOUNT_ID", nil),
},
"peer_vpc_id": &schema.Schema{
Type: schema.TypeString,
@ -39,23 +41,23 @@ func resourceAwsVpcPeeringConnection() *schema.Resource {
}
func resourceAwsVpcPeeringCreate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
// 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
@ -78,7 +80,7 @@ func resourceAwsVpcPeeringCreate(d *schema.ResourceData, meta interface{}) error
}
func resourceAwsVpcPeeringRead(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
pcRaw, _, err := resourceAwsVpcPeeringConnectionStateRefreshFunc(ec2conn, d.Id())()
if err != nil {
return err
@ -88,20 +90,20 @@ 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("tags", tagsToMap(pc.Tags))
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", tagsToMapSDK(pc.Tags))
return nil
}
func resourceAwsVpcPeeringUpdate(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
if err := setTags(ec2conn, d); err != nil {
if err := setTagsSDK(ec2conn, d); err != nil {
return err
} else {
d.SetPartial("tags")
@ -111,9 +113,12 @@ func resourceAwsVpcPeeringUpdate(d *schema.ResourceData, meta interface{}) error
}
func resourceAwsVpcPeeringDelete(d *schema.ResourceData, meta interface{}) error {
ec2conn := meta.(*AWSClient).ec2conn
ec2conn := meta.(*AWSClient).awsEC2conn
_, 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) {
@ -28,17 +28,20 @@ func TestAccAWSVPCPeeringConnection_normal(t *testing.T) {
}
func testAccCheckAWSVpcPeeringConnectionDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).ec2conn
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_vpc_peering_connection" {
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

@ -4,6 +4,7 @@ import (
"strings"
"github.com/hashicorp/aws-sdk-go/aws"
awsEC2 "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"
@ -39,15 +40,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{}) []awsEC2.IPPermission {
perms := make([]awsEC2.IPPermission, len(configured))
for i, mRaw := range configured {
var perm ec2.IPPerm
var perm awsEC2.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 +62,25 @@ func expandIPPerms(id string, configured []interface{}) []ec2.IPPerm {
}
if len(groups) > 0 {
perm.SourceGroups = make([]ec2.UserSecurityGroup, len(groups))
perm.UserIDGroupPairs = make([]awsEC2.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] = awsEC2.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([]awsEC2.IPRange, len(list))
for i, v := range list {
perm.SourceIPs[i] = v.(string)
perm.IPRanges[i] = awsEC2.IPRange{aws.String(v.(string))}
}
}
@ -111,31 +112,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{} {
@ -162,6 +138,15 @@ func flattenSecurityGroups(list []ec2.UserSecurityGroup) []string {
return result
}
// Flattens an array of UserSecurityGroups into a []string
func flattenSecurityGroupsSDK(list []awsEC2.UserIDGroupPair) []string {
result := make([]string, 0, len(list))
for _, g := range list {
result = append(result, *g.GroupID)
}
return result
}
// Flattens an array of Instances into a []string
func flattenInstances(list []elb.Instance) []string {
result := make([]string, 0, len(list))

View File

@ -5,12 +5,12 @@ import (
"testing"
"github.com/hashicorp/aws-sdk-go/aws"
awsEC2 "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 := []awsEC2.IPPermission{
awsEC2.IPPermission{
IPProtocol: aws.String("icmp"),
FromPort: aws.Integer(1),
ToPort: aws.Integer(-1),
IPRanges: []awsEC2.IPRange{awsEC2.IPRange{aws.String("0.0.0.0/0")}},
UserIDGroupPairs: []awsEC2.UserIDGroupPair{
awsEC2.UserIDGroupPair{
UserID: aws.String("foo"),
GroupID: aws.String("sg-22222"),
},
ec2.UserSecurityGroup{
Id: "sg-11111",
awsEC2.UserIDGroupPair{
GroupID: aws.String("sg-22222"),
},
},
},
ec2.IPPerm{
Protocol: "icmp",
FromPort: 1,
ToPort: -1,
SourceGroups: []ec2.UserSecurityGroup{
ec2.UserSecurityGroup{
Id: "foo",
awsEC2.IPPermission{
IPProtocol: aws.String("icmp"),
FromPort: aws.Integer(1),
ToPort: aws.Integer(-1),
UserIDGroupPairs: []awsEC2.UserIDGroupPair{
awsEC2.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,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

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

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