Merge pull request #1147 from hashicorp/aws-go-instance
provider/aws: Convert AWS Instance to aws-sdk-go
This commit is contained in:
commit
9654f2ff3a
|
@ -6,14 +6,14 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/aws-sdk-go/gen/ec2"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/mitchellh/goamz/ec2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceAwsInstance() *schema.Resource {
|
func resourceAwsInstance() *schema.Resource {
|
||||||
|
@ -253,7 +253,7 @@ func resourceAwsInstance() *schema.Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
ec2conn := meta.(*AWSClient).ec2conn
|
ec2conn := meta.(*AWSClient).awsEC2conn
|
||||||
|
|
||||||
// Figure out user data
|
// Figure out user data
|
||||||
userData := ""
|
userData := ""
|
||||||
|
@ -261,38 +261,84 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
userData = v.(string)
|
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
|
associatePublicIPAddress := false
|
||||||
if v := d.Get("associate_public_ip_address"); v != nil {
|
if v := d.Get("associate_public_ip_address"); v != nil {
|
||||||
associatePublicIPAddress = v.(bool)
|
associatePublicIPAddress = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the creation struct
|
// check for non-default Subnet, and cast it to a String
|
||||||
runOpts := &ec2.RunInstances{
|
var hasSubnet bool
|
||||||
ImageId: d.Get("ami").(string),
|
subnet, hasSubnet := d.GetOk("subnet_id")
|
||||||
AvailZone: d.Get("availability_zone").(string),
|
subnetID := subnet.(string)
|
||||||
InstanceType: d.Get("instance_type").(string),
|
|
||||||
KeyName: d.Get("key_name").(string),
|
if hasSubnet && associatePublicIPAddress {
|
||||||
SubnetId: d.Get("subnet_id").(string),
|
// If we have a non-default VPC / Subnet specified, we can flag
|
||||||
PrivateIPAddress: d.Get("private_ip").(string),
|
// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
|
||||||
AssociatePublicIpAddress: associatePublicIPAddress,
|
// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
|
||||||
UserData: []byte(userData),
|
// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
|
||||||
EbsOptimized: d.Get("ebs_optimized").(bool),
|
// You also need to attach Security Groups to the NetworkInterface instead of the instance,
|
||||||
IamInstanceProfile: d.Get("iam_instance_profile").(string),
|
// to avoid: Network interfaces and an instance-level security groups may not be specified on
|
||||||
Tenancy: d.Get("tenancy").(string),
|
// 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 {
|
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() {
|
for _, v := range v.(*schema.Set).List() {
|
||||||
str := v.(string)
|
str := v.(string)
|
||||||
|
groups = append(groups, str)
|
||||||
var g ec2.SecurityGroup
|
}
|
||||||
if runOpts.SubnetId != "" {
|
if runOpts.SubnetID != nil &&
|
||||||
g.Id = str
|
*runOpts.SubnetID != "" {
|
||||||
} else {
|
runOpts.SecurityGroupIDs = groups
|
||||||
g.Name = str
|
} else {
|
||||||
}
|
runOpts.SecurityGroups = groups
|
||||||
|
|
||||||
runOpts.SecurityGroups = append(runOpts.SecurityGroups, g)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,24 +357,27 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(blockDevices) > 0 {
|
if len(blockDevices) > 0 {
|
||||||
runOpts.BlockDevices = make([]ec2.BlockDeviceMapping, len(blockDevices))
|
runOpts.BlockDeviceMappings = make([]ec2.BlockDeviceMapping, len(blockDevices))
|
||||||
for i, v := range blockDevices {
|
for i, v := range blockDevices {
|
||||||
bd := v.(map[string]interface{})
|
bd := v.(map[string]interface{})
|
||||||
runOpts.BlockDevices[i].DeviceName = bd["device_name"].(string)
|
runOpts.BlockDeviceMappings[i].DeviceName = aws.String(bd["device_name"].(string))
|
||||||
runOpts.BlockDevices[i].VolumeType = bd["volume_type"].(string)
|
runOpts.BlockDeviceMappings[i].EBS = &ec2.EBSBlockDevice{
|
||||||
runOpts.BlockDevices[i].VolumeSize = int64(bd["volume_size"].(int))
|
VolumeType: aws.String(bd["volume_type"].(string)),
|
||||||
runOpts.BlockDevices[i].DeleteOnTermination = bd["delete_on_termination"].(bool)
|
VolumeSize: aws.Integer(bd["volume_size"].(int)),
|
||||||
if v, ok := bd["virtual_name"].(string); ok {
|
DeleteOnTermination: aws.Boolean(bd["delete_on_termination"].(bool)),
|
||||||
runOpts.BlockDevices[i].VirtualName = v
|
|
||||||
}
|
}
|
||||||
if v, ok := bd["snapshot_id"].(string); ok {
|
|
||||||
runOpts.BlockDevices[i].SnapshotId = v
|
if v, ok := bd["virtual_name"].(string); ok {
|
||||||
|
runOpts.BlockDeviceMappings[i].VirtualName = aws.String(v)
|
||||||
|
}
|
||||||
|
if v, ok := bd["snapshot_id"].(string); ok && v != "" {
|
||||||
|
runOpts.BlockDeviceMappings[i].EBS.SnapshotID = aws.String(v)
|
||||||
}
|
}
|
||||||
if v, ok := bd["encrypted"].(bool); ok {
|
if v, ok := bd["encrypted"].(bool); ok {
|
||||||
runOpts.BlockDevices[i].Encrypted = v
|
runOpts.BlockDeviceMappings[i].EBS.Encrypted = aws.Boolean(v)
|
||||||
}
|
}
|
||||||
if v, ok := bd["iops"].(int); ok {
|
if v, ok := bd["iops"].(int); ok && v > 0 {
|
||||||
runOpts.BlockDevices[i].IOPS = int64(v)
|
runOpts.BlockDeviceMappings[i].EBS.IOPS = aws.Integer(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,21 +390,21 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := &runResp.Instances[0]
|
instance := &runResp.Instances[0]
|
||||||
log.Printf("[INFO] Instance ID: %s", instance.InstanceId)
|
log.Printf("[INFO] Instance ID: %s", *instance.InstanceID)
|
||||||
|
|
||||||
// Store the resulting ID so we can look this up later
|
// Store the resulting ID so we can look this up later
|
||||||
d.SetId(instance.InstanceId)
|
d.SetId(*instance.InstanceID)
|
||||||
|
|
||||||
// Wait for the instance to become running so we can get some attributes
|
// Wait for the instance to become running so we can get some attributes
|
||||||
// that aren't available until later.
|
// that aren't available until later.
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"[DEBUG] Waiting for instance (%s) to become running",
|
"[DEBUG] Waiting for instance (%s) to become running",
|
||||||
instance.InstanceId)
|
*instance.InstanceID)
|
||||||
|
|
||||||
stateConf := &resource.StateChangeConf{
|
stateConf := &resource.StateChangeConf{
|
||||||
Pending: []string{"pending"},
|
Pending: []string{"pending"},
|
||||||
Target: "running",
|
Target: "running",
|
||||||
Refresh: InstanceStateRefreshFunc(ec2conn, instance.InstanceId),
|
Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceID),
|
||||||
Timeout: 10 * time.Minute,
|
Timeout: 10 * time.Minute,
|
||||||
Delay: 10 * time.Second,
|
Delay: 10 * time.Second,
|
||||||
MinTimeout: 3 * time.Second,
|
MinTimeout: 3 * time.Second,
|
||||||
|
@ -365,16 +414,18 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Error waiting for instance (%s) to become ready: %s",
|
"Error waiting for instance (%s) to become ready: %s",
|
||||||
instance.InstanceId, err)
|
*instance.InstanceID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = instanceRaw.(*ec2.Instance)
|
instance = instanceRaw.(*ec2.Instance)
|
||||||
|
|
||||||
// Initialize the connection info
|
// Initialize the connection info
|
||||||
d.SetConnInfo(map[string]string{
|
if instance.PublicIPAddress != nil {
|
||||||
"type": "ssh",
|
d.SetConnInfo(map[string]string{
|
||||||
"host": instance.PublicIpAddress,
|
"type": "ssh",
|
||||||
})
|
"host": *instance.PublicIPAddress,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Set our attributes
|
// Set our attributes
|
||||||
if err := resourceAwsInstanceRead(d, meta); err != nil {
|
if err := resourceAwsInstanceRead(d, meta); err != nil {
|
||||||
|
@ -386,13 +437,15 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
ec2conn := meta.(*AWSClient).ec2conn
|
ec2conn := meta.(*AWSClient).awsEC2conn
|
||||||
|
|
||||||
resp, err := ec2conn.Instances([]string{d.Id()}, ec2.NewFilter())
|
resp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesRequest{
|
||||||
|
InstanceIDs: []string{d.Id()},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the instance was not found, return nil so that we can show
|
// If the instance was not found, return nil so that we can show
|
||||||
// that the instance is gone.
|
// that the instance is gone.
|
||||||
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
|
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -410,28 +463,33 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
instance := &resp.Reservations[0].Instances[0]
|
instance := &resp.Reservations[0].Instances[0]
|
||||||
|
|
||||||
// If the instance is terminated, then it is gone
|
// If the instance is terminated, then it is gone
|
||||||
if instance.State.Name == "terminated" {
|
if *instance.State.Name == "terminated" {
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("availability_zone", instance.AvailZone)
|
d.Set("availability_zone", instance.Placement.AvailabilityZone)
|
||||||
d.Set("key_name", instance.KeyName)
|
d.Set("key_name", instance.KeyName)
|
||||||
d.Set("public_dns", instance.DNSName)
|
d.Set("public_dns", instance.PublicDNSName)
|
||||||
d.Set("public_ip", instance.PublicIpAddress)
|
d.Set("public_ip", instance.PublicIPAddress)
|
||||||
d.Set("private_dns", instance.PrivateDNSName)
|
d.Set("private_dns", instance.PrivateDNSName)
|
||||||
d.Set("private_ip", instance.PrivateIpAddress)
|
d.Set("private_ip", instance.PrivateIPAddress)
|
||||||
d.Set("subnet_id", instance.SubnetId)
|
d.Set("subnet_id", instance.SubnetID)
|
||||||
d.Set("ebs_optimized", instance.EbsOptimized)
|
if len(instance.NetworkInterfaces) > 0 {
|
||||||
d.Set("tags", tagsToMap(instance.Tags))
|
d.Set("subnet_id", instance.NetworkInterfaces[0].SubnetID)
|
||||||
d.Set("tenancy", instance.Tenancy)
|
} 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
|
// Determine whether we're referring to security groups with
|
||||||
// IDs or names. We use a heuristic to figure this out. By default,
|
// IDs or names. We use a heuristic to figure this out. By default,
|
||||||
// we use IDs if we're in a VPC. However, if we previously had an
|
// we use IDs if we're in a VPC. However, if we previously had an
|
||||||
// all-name list of security groups, we use names. Or, if we had any
|
// all-name list of security groups, we use names. Or, if we had any
|
||||||
// IDs, we use IDs.
|
// IDs, we use IDs.
|
||||||
useID := instance.SubnetId != ""
|
useID := *instance.SubnetID != ""
|
||||||
if v := d.Get("security_groups"); v != nil {
|
if v := d.Get("security_groups"); v != nil {
|
||||||
match := false
|
match := false
|
||||||
for _, v := range v.(*schema.Set).List() {
|
for _, v := range v.(*schema.Set).List() {
|
||||||
|
@ -448,24 +506,26 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
sgs := make([]string, len(instance.SecurityGroups))
|
sgs := make([]string, len(instance.SecurityGroups))
|
||||||
for i, sg := range instance.SecurityGroups {
|
for i, sg := range instance.SecurityGroups {
|
||||||
if useID {
|
if useID {
|
||||||
sgs[i] = sg.Id
|
sgs[i] = *sg.GroupID
|
||||||
} else {
|
} else {
|
||||||
sgs[i] = sg.Name
|
sgs[i] = *sg.GroupName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.Set("security_groups", sgs)
|
d.Set("security_groups", sgs)
|
||||||
|
|
||||||
blockDevices := make(map[string]ec2.BlockDevice)
|
blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
|
||||||
for _, bd := range instance.BlockDevices {
|
for _, bd := range instance.BlockDeviceMappings {
|
||||||
blockDevices[bd.VolumeId] = bd
|
blockDevices[*bd.EBS.VolumeID] = bd
|
||||||
}
|
}
|
||||||
|
|
||||||
volIDs := make([]string, 0, len(blockDevices))
|
volIDs := make([]string, 0, len(blockDevices))
|
||||||
for volID := range blockDevices {
|
for _, vol := range blockDevices {
|
||||||
volIDs = append(volIDs, volID)
|
volIDs = append(volIDs, *vol.EBS.VolumeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
volResp, err := ec2conn.Volumes(volIDs, ec2.NewFilter())
|
volResp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesRequest{
|
||||||
|
VolumeIDs: volIDs,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -473,28 +533,25 @@ func resourceAwsInstanceRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
nonRootBlockDevices := make([]map[string]interface{}, 0)
|
nonRootBlockDevices := make([]map[string]interface{}, 0)
|
||||||
rootBlockDevice := make([]interface{}, 0, 1)
|
rootBlockDevice := make([]interface{}, 0, 1)
|
||||||
for _, vol := range volResp.Volumes {
|
for _, vol := range volResp.Volumes {
|
||||||
volSize, err := strconv.Atoi(vol.Size)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
blockDevice := make(map[string]interface{})
|
blockDevice := make(map[string]interface{})
|
||||||
blockDevice["device_name"] = blockDevices[vol.VolumeId].DeviceName
|
blockDevice["device_name"] = *blockDevices[*vol.VolumeID].DeviceName
|
||||||
blockDevice["volume_type"] = vol.VolumeType
|
blockDevice["volume_type"] = *vol.VolumeType
|
||||||
blockDevice["volume_size"] = volSize
|
blockDevice["volume_size"] = *vol.Size
|
||||||
blockDevice["iops"] = vol.IOPS
|
if vol.IOPS != nil {
|
||||||
|
blockDevice["iops"] = *vol.IOPS
|
||||||
|
}
|
||||||
blockDevice["delete_on_termination"] =
|
blockDevice["delete_on_termination"] =
|
||||||
blockDevices[vol.VolumeId].DeleteOnTermination
|
*blockDevices[*vol.VolumeID].EBS.DeleteOnTermination
|
||||||
|
|
||||||
// If this is the root device, save it. We stop here since we
|
// If this is the root device, save it. We stop here since we
|
||||||
// can't put invalid keys into this map.
|
// can't put invalid keys into this map.
|
||||||
if blockDevice["device_name"] == instance.RootDeviceName {
|
if blockDevice["device_name"] == *instance.RootDeviceName {
|
||||||
rootBlockDevice = []interface{}{blockDevice}
|
rootBlockDevice = []interface{}{blockDevice}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
blockDevice["snapshot_id"] = vol.SnapshotId
|
blockDevice["snapshot_id"] = *vol.SnapshotID
|
||||||
blockDevice["encrypted"] = vol.Encrypted
|
blockDevice["encrypted"] = *vol.Encrypted
|
||||||
nonRootBlockDevices = append(nonRootBlockDevices, blockDevice)
|
nonRootBlockDevices = append(nonRootBlockDevices, blockDevice)
|
||||||
}
|
}
|
||||||
d.Set("block_device", nonRootBlockDevices)
|
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 {
|
func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
ec2conn := meta.(*AWSClient).ec2conn
|
ec2conn := meta.(*AWSClient).awsEC2conn
|
||||||
opts := new(ec2.ModifyInstance)
|
opts := new(ec2.ModifyInstanceAttributeRequest)
|
||||||
|
|
||||||
opts.SetSourceDestCheck = true
|
|
||||||
opts.SourceDestCheck = d.Get("source_dest_check").(bool)
|
|
||||||
|
|
||||||
log.Printf("[INFO] Modifying instance %s: %#v", d.Id(), opts)
|
log.Printf("[INFO] Modifying instance %s: %#v", d.Id(), opts)
|
||||||
if _, err := ec2conn.ModifyInstance(d.Id(), opts); err != nil {
|
err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeRequest{
|
||||||
|
InstanceID: aws.String(d.Id()),
|
||||||
|
SourceDestCheck: &ec2.AttributeBooleanValue{
|
||||||
|
Value: aws.Boolean(d.Get("source_dest_check").(bool)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mitchellh): wait for the attributes we modified to
|
// TODO(mitchellh): wait for the attributes we modified to
|
||||||
// persist the change...
|
// persist the change...
|
||||||
|
|
||||||
if err := setTags(ec2conn, d); err != nil {
|
if err := setTagsSDK(ec2conn, d); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
d.SetPartial("tags")
|
d.SetPartial("tags")
|
||||||
|
@ -528,10 +589,13 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceAwsInstanceDelete(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())
|
log.Printf("[INFO] Terminating instance: %s", d.Id())
|
||||||
if _, err := ec2conn.TerminateInstances([]string{d.Id()}); err != nil {
|
req := &ec2.TerminateInstancesRequest{
|
||||||
|
InstanceIDs: []string{d.Id()},
|
||||||
|
}
|
||||||
|
if _, err := ec2conn.TerminateInstances(req); err != nil {
|
||||||
return fmt.Errorf("Error terminating instance: %s", err)
|
return fmt.Errorf("Error terminating instance: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,9 +627,11 @@ func resourceAwsInstanceDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
// an EC2 instance.
|
// an EC2 instance.
|
||||||
func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
|
func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
resp, err := conn.Instances([]string{instanceID}, ec2.NewFilter())
|
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
|
||||||
|
InstanceIDs: []string{instanceID},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
|
if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidInstanceID.NotFound" {
|
||||||
// Set this to nil as if we didn't find anything.
|
// Set this to nil as if we didn't find anything.
|
||||||
resp = nil
|
resp = nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -581,7 +647,7 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, instanceID string) resource.StateRe
|
||||||
}
|
}
|
||||||
|
|
||||||
i := &resp.Reservations[0].Instances[0]
|
i := &resp.Reservations[0].Instances[0]
|
||||||
return i, i.State.Name, nil
|
return i, *i.State.Name, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,24 +5,25 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/aws-sdk-go/gen/ec2"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/goamz/ec2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAWSInstance_normal(t *testing.T) {
|
func TestAccAWSInstance_normal(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
testCheck := func(*terraform.State) error {
|
testCheck := func(*terraform.State) error {
|
||||||
if v.AvailZone != "us-west-2a" {
|
if *v.Placement.AvailabilityZone != "us-west-2a" {
|
||||||
return fmt.Errorf("bad availability zone: %#v", v.AvailZone)
|
return fmt.Errorf("bad availability zone: %#v", *v.Placement.AvailabilityZone)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v.SecurityGroups) == 0 {
|
if len(v.SecurityGroups) == 0 {
|
||||||
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
|
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
|
||||||
}
|
}
|
||||||
if v.SecurityGroups[0].Name != "tf_test_foo" {
|
if *v.SecurityGroups[0].GroupName != "tf_test_foo" {
|
||||||
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
|
return fmt.Errorf("no security groups: %#v", v.SecurityGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +74,9 @@ func TestAccAWSInstance_blockDevices(t *testing.T) {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
|
|
||||||
// Map out the block devices by name, which should be unique.
|
// Map out the block devices by name, which should be unique.
|
||||||
blockDevices := make(map[string]ec2.BlockDevice)
|
blockDevices := make(map[string]ec2.InstanceBlockDeviceMapping)
|
||||||
for _, blockDevice := range v.BlockDevices {
|
for _, blockDevice := range v.BlockDeviceMappings {
|
||||||
blockDevices[blockDevice.DeviceName] = blockDevice
|
blockDevices[*blockDevice.DeviceName] = blockDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the root block device exists.
|
// Check if the root block device exists.
|
||||||
|
@ -147,8 +148,8 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) {
|
||||||
|
|
||||||
testCheck := func(enabled bool) resource.TestCheckFunc {
|
testCheck := func(enabled bool) resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
if v.SourceDestCheck != enabled {
|
if *v.SourceDestCheck != enabled {
|
||||||
return fmt.Errorf("bad source_dest_check: %#v", v.SourceDestCheck)
|
return fmt.Errorf("bad source_dest_check: %#v", *v.SourceDestCheck)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -206,7 +207,7 @@ func TestAccAWSInstance_vpc(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccInstance_tags(t *testing.T) {
|
func TestAccAWSInstance_tags(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
|
@ -218,9 +219,9 @@ func TestAccInstance_tags(t *testing.T) {
|
||||||
Config: testAccCheckInstanceConfigTags,
|
Config: testAccCheckInstanceConfigTags,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInstanceExists("aws_instance.foo", &v),
|
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
|
// 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,
|
Config: testAccCheckInstanceConfigTagsUpdate,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckInstanceExists("aws_instance.foo", &v),
|
testAccCheckInstanceExists("aws_instance.foo", &v),
|
||||||
testAccCheckTags(&v.Tags, "foo", ""),
|
testAccCheckTagsSDK(&v.Tags, "foo", ""),
|
||||||
testAccCheckTags(&v.Tags, "bar", "baz"),
|
testAccCheckTagsSDK(&v.Tags, "bar", "baz"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccInstance_privateIP(t *testing.T) {
|
func TestAccAWSInstance_privateIP(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
if v.PrivateIpAddress != "10.1.1.42" {
|
if *v.PrivateIPAddress != "10.1.1.42" {
|
||||||
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress)
|
return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -265,13 +266,13 @@ func TestAccInstance_privateIP(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
func TestAccAWSInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||||
var v ec2.Instance
|
var v ec2.Instance
|
||||||
|
|
||||||
testCheckPrivateIP := func() resource.TestCheckFunc {
|
testCheckPrivateIP := func() resource.TestCheckFunc {
|
||||||
return func(*terraform.State) error {
|
return func(*terraform.State) error {
|
||||||
if v.PrivateIpAddress != "10.1.1.42" {
|
if *v.PrivateIPAddress != "10.1.1.42" {
|
||||||
return fmt.Errorf("bad private IP: %s", v.PrivateIpAddress)
|
return fmt.Errorf("bad private IP: %s", *v.PrivateIPAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -295,7 +296,7 @@ func TestAccInstance_associatePublicIPAndPrivateIP(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckInstanceDestroy(s *terraform.State) error {
|
func testAccCheckInstanceDestroy(s *terraform.State) error {
|
||||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
|
||||||
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
for _, rs := range s.RootModule().Resources {
|
||||||
if rs.Type != "aws_instance" {
|
if rs.Type != "aws_instance" {
|
||||||
|
@ -303,8 +304,9 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find the resource
|
// Try to find the resource
|
||||||
resp, err := conn.Instances(
|
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
|
||||||
[]string{rs.Primary.ID}, ec2.NewFilter())
|
InstanceIDs: []string{rs.Primary.ID},
|
||||||
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if len(resp.Reservations) > 0 {
|
if len(resp.Reservations) > 0 {
|
||||||
return fmt.Errorf("still exist.")
|
return fmt.Errorf("still exist.")
|
||||||
|
@ -314,7 +316,7 @@ func testAccCheckInstanceDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the error is what we want
|
// Verify the error is what we want
|
||||||
ec2err, ok := err.(*ec2.Error)
|
ec2err, ok := err.(aws.APIError)
|
||||||
if !ok {
|
if !ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -337,9 +339,10 @@ func testAccCheckInstanceExists(n string, i *ec2.Instance) resource.TestCheckFun
|
||||||
return fmt.Errorf("No ID is set")
|
return fmt.Errorf("No ID is set")
|
||||||
}
|
}
|
||||||
|
|
||||||
conn := testAccProvider.Meta().(*AWSClient).ec2conn
|
conn := testAccProvider.Meta().(*AWSClient).awsEC2conn
|
||||||
resp, err := conn.Instances(
|
resp, err := conn.DescribeInstances(&ec2.DescribeInstancesRequest{
|
||||||
[]string{rs.Primary.ID}, ec2.NewFilter())
|
InstanceIDs: []string{rs.Primary.ID},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue