2014-06-27 18:47:19 +02:00
package aws
import (
2014-10-17 18:12:45 +02:00
"bytes"
2014-08-22 17:46:48 +02:00
"crypto/sha1"
2015-03-13 16:54:00 +01:00
"encoding/base64"
2014-08-22 17:46:48 +02:00
"encoding/hex"
2014-06-27 18:47:19 +02:00
"fmt"
"log"
2014-07-28 18:47:40 +02:00
"strings"
2014-07-01 19:10:11 +02:00
"time"
2014-06-27 18:47:19 +02:00
2015-06-03 20:36:57 +02:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
2014-08-22 03:38:43 +02:00
"github.com/hashicorp/terraform/helper/hashcode"
2014-07-01 19:10:11 +02:00
"github.com/hashicorp/terraform/helper/resource"
2014-08-22 03:38:43 +02:00
"github.com/hashicorp/terraform/helper/schema"
2014-06-27 18:47:19 +02:00
)
2014-08-22 03:38:43 +02:00
func resourceAwsInstance ( ) * schema . Resource {
return & schema . Resource {
Create : resourceAwsInstanceCreate ,
Read : resourceAwsInstanceRead ,
Update : resourceAwsInstanceUpdate ,
Delete : resourceAwsInstanceDelete ,
2015-02-24 18:00:22 +01:00
SchemaVersion : 1 ,
MigrateState : resourceAwsInstanceMigrateState ,
2014-08-22 03:38:43 +02:00
Schema : map [ string ] * schema . Schema {
"ami" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
"associate_public_ip_address" : & schema . Schema {
Type : schema . TypeBool ,
ForceNew : true ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
Optional : true ,
2014-08-22 03:38:43 +02:00
} ,
"availability_zone" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
2015-04-02 08:33:16 +02:00
"placement_group" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
2014-08-22 03:38:43 +02:00
"instance_type" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
"key_name" : & schema . Schema {
Type : schema . TypeString ,
2014-08-22 21:20:06 +02:00
Optional : true ,
2014-08-22 03:38:43 +02:00
ForceNew : true ,
2014-08-22 21:20:06 +02:00
Computed : true ,
2014-08-22 03:38:43 +02:00
} ,
"subnet_id" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
"private_ip" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
2014-09-18 22:26:49 +02:00
ForceNew : true ,
2014-08-22 03:38:43 +02:00
Computed : true ,
} ,
"source_dest_check" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
2015-06-03 22:54:01 +02:00
Default : true ,
2014-08-22 03:38:43 +02:00
} ,
"user_data" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
ForceNew : true ,
2014-08-22 17:46:48 +02:00
StateFunc : func ( v interface { } ) string {
2014-08-23 03:11:06 +02:00
switch v . ( type ) {
case string :
hash := sha1 . Sum ( [ ] byte ( v . ( string ) ) )
return hex . EncodeToString ( hash [ : ] )
default :
return ""
}
2014-08-22 17:46:48 +02:00
} ,
2014-08-22 03:38:43 +02:00
} ,
"security_groups" : & schema . Schema {
Type : schema . TypeSet ,
Optional : true ,
2014-08-28 00:26:15 +02:00
Computed : true ,
2014-10-11 02:14:35 +02:00
ForceNew : true ,
2014-08-22 03:38:43 +02:00
Elem : & schema . Schema { Type : schema . TypeString } ,
2015-04-09 15:38:16 +02:00
Set : schema . HashString ,
2014-08-22 03:38:43 +02:00
} ,
2015-03-07 07:14:04 +01:00
"vpc_security_group_ids" : & schema . Schema {
2015-03-07 07:04:53 +01:00
Type : schema . TypeSet ,
Optional : true ,
Computed : true ,
Elem : & schema . Schema { Type : schema . TypeString } ,
Set : func ( v interface { } ) int {
return hashcode . String ( v . ( string ) )
} ,
} ,
2014-08-22 03:38:43 +02:00
"public_dns" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
"public_ip" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2015-09-17 12:26:38 +02:00
"instance_state" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2014-08-22 03:38:43 +02:00
"private_dns" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2014-09-03 12:18:40 +02:00
"ebs_optimized" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
2016-01-11 08:16:49 +01:00
ForceNew : true ,
2014-09-03 12:18:40 +02:00
} ,
2014-10-09 01:43:13 +02:00
2015-05-15 21:18:05 +02:00
"disable_api_termination" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
2015-07-29 22:54:56 +02:00
"instance_initiated_shutdown_behavior" : & schema . Schema {
2015-07-18 18:45:34 +02:00
Type : schema . TypeString ,
Optional : true ,
} ,
2015-07-29 22:54:56 +02:00
2015-06-25 16:58:28 +02:00
"monitoring" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
} ,
2014-09-23 20:06:30 +02:00
"iam_instance_profile" : & schema . Schema {
2014-09-28 20:51:49 +02:00
Type : schema . TypeString ,
2014-09-23 20:06:30 +02:00
ForceNew : true ,
Optional : true ,
} ,
2015-02-24 18:00:22 +01:00
2014-11-04 12:08:30 +01:00
"tenancy" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
2014-10-13 22:55:59 +02:00
"tags" : tagsSchema ( ) ,
2014-10-17 18:12:45 +02:00
"block_device" : & schema . Schema {
2015-02-24 18:00:22 +01:00
Type : schema . TypeMap ,
Optional : true ,
Removed : "Split out into three sub-types; see Changelog and Docs" ,
} ,
"ebs_block_device" : & schema . Schema {
2014-10-17 18:12:45 +02:00
Type : schema . TypeSet ,
Optional : true ,
2014-12-25 21:58:26 +01:00
Computed : true ,
2014-10-17 18:12:45 +02:00
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
2015-02-24 18:00:22 +01:00
"delete_on_termination" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
Default : true ,
ForceNew : true ,
} ,
2014-10-17 18:12:45 +02:00
"device_name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
"encrypted" : & schema . Schema {
Type : schema . TypeBool ,
2014-11-22 10:50:22 +01:00
Optional : true ,
2015-02-24 18:00:22 +01:00
Computed : true ,
2014-11-22 10:50:22 +01:00
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
"iops" : & schema . Schema {
Type : schema . TypeInt ,
2014-10-17 18:12:45 +02:00
Optional : true ,
2014-12-25 18:21:05 +01:00
Computed : true ,
2014-10-17 18:12:45 +02:00
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
"snapshot_id" : & schema . Schema {
2014-10-17 18:12:45 +02:00
Type : schema . TypeString ,
Optional : true ,
2014-12-25 18:21:05 +01:00
Computed : true ,
2014-10-17 18:12:45 +02:00
ForceNew : true ,
} ,
"volume_size" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
2014-12-25 18:21:05 +01:00
Computed : true ,
2014-10-17 18:12:45 +02:00
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
"volume_type" : & schema . Schema {
Type : schema . TypeString ,
2014-10-17 18:12:45 +02:00
Optional : true ,
2015-02-24 18:00:22 +01:00
Computed : true ,
2014-10-17 18:12:45 +02:00
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
} ,
} ,
Set : func ( v interface { } ) int {
var buf bytes . Buffer
m := v . ( map [ string ] interface { } )
buf . WriteString ( fmt . Sprintf ( "%s-" , m [ "device_name" ] . ( string ) ) )
buf . WriteString ( fmt . Sprintf ( "%s-" , m [ "snapshot_id" ] . ( string ) ) )
return hashcode . String ( buf . String ( ) )
} ,
} ,
2014-10-17 18:12:45 +02:00
2015-02-24 18:00:22 +01:00
"ephemeral_block_device" : & schema . Schema {
Type : schema . TypeSet ,
Optional : true ,
Computed : true ,
ForceNew : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"device_name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
2014-10-17 18:12:45 +02:00
} ,
2015-03-03 07:07:36 +01:00
2015-02-24 18:00:22 +01:00
"virtual_name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
2015-03-03 07:07:36 +01:00
} ,
2014-10-17 18:12:45 +02:00
} ,
} ,
2015-02-24 18:00:22 +01:00
Set : func ( v interface { } ) int {
var buf bytes . Buffer
m := v . ( map [ string ] interface { } )
buf . WriteString ( fmt . Sprintf ( "%s-" , m [ "device_name" ] . ( string ) ) )
buf . WriteString ( fmt . Sprintf ( "%s-" , m [ "virtual_name" ] . ( string ) ) )
return hashcode . String ( buf . String ( ) )
} ,
2014-10-17 18:12:45 +02:00
} ,
providers/aws: add root_block_device to aws_instance
AWS provides a single `BlockDeviceMapping` to manage three different
kinds of block devices:
(a) The root volume
(b) Ephemeral storage
(c) Additional EBS volumes
Each of these types has slightly different semantics [1].
(a) The root volume is defined by the AMI; it can only be customized
with `volume_size`, `volume_type`, and `delete_on_termination`.
(b) Ephemeral storage is made available based on instance type [2]. It's
attached automatically if _no_ block device mappings are specified, and
must otherwise be defined with block device mapping entries that contain
only DeviceName set to a device like "/dev/sdX" and VirtualName set to
"ephemeralN".
(c) Additional EBS volumes are controlled by mappings that omit
`virtual_name` and can specify `volume_size`, `volume_type`,
`delete_on_termination`, `snapshot_id`, and `encryption`.
After deciding to ignore root block devices to fix #859, we had users
with configurations that were attempting to manage the root block device chime
in on #913.
Terraform does not have the primitives to be able to properly handle a
single collection of resources that is partially managed and partially
computed, so our strategy here is to break out logical sub-resources for
Terraform and hide the BlockDeviceMapping inside the provider
implementation.
Now (a) is supported by the `root_block_device` sub-resource, and (b)
and (c) are still both merged together under `block_device`, though I
have yet to see ephemeral block devices working properly.
Looking into possibly separating out `ephemeral_block_device` and
`ebs_block_device` sub-resources as well, which seem like the logical
next step. We'll wait until the next big release for this, though, since
it will break backcompat.
[1] http://bit.ly/ec2bdmap
[2] http://bit.ly/instancestorebytype
Fixes #913
Refs #858
2015-02-18 18:45:30 +01:00
"root_block_device" : & schema . Schema {
2015-02-24 18:00:22 +01:00
// TODO: This is a set because we don't support singleton
// sub-resources today. We'll enforce that the set only ever has
providers/aws: add root_block_device to aws_instance
AWS provides a single `BlockDeviceMapping` to manage three different
kinds of block devices:
(a) The root volume
(b) Ephemeral storage
(c) Additional EBS volumes
Each of these types has slightly different semantics [1].
(a) The root volume is defined by the AMI; it can only be customized
with `volume_size`, `volume_type`, and `delete_on_termination`.
(b) Ephemeral storage is made available based on instance type [2]. It's
attached automatically if _no_ block device mappings are specified, and
must otherwise be defined with block device mapping entries that contain
only DeviceName set to a device like "/dev/sdX" and VirtualName set to
"ephemeralN".
(c) Additional EBS volumes are controlled by mappings that omit
`virtual_name` and can specify `volume_size`, `volume_type`,
`delete_on_termination`, `snapshot_id`, and `encryption`.
After deciding to ignore root block devices to fix #859, we had users
with configurations that were attempting to manage the root block device chime
in on #913.
Terraform does not have the primitives to be able to properly handle a
single collection of resources that is partially managed and partially
computed, so our strategy here is to break out logical sub-resources for
Terraform and hide the BlockDeviceMapping inside the provider
implementation.
Now (a) is supported by the `root_block_device` sub-resource, and (b)
and (c) are still both merged together under `block_device`, though I
have yet to see ephemeral block devices working properly.
Looking into possibly separating out `ephemeral_block_device` and
`ebs_block_device` sub-resources as well, which seem like the logical
next step. We'll wait until the next big release for this, though, since
it will break backcompat.
[1] http://bit.ly/ec2bdmap
[2] http://bit.ly/instancestorebytype
Fixes #913
Refs #858
2015-02-18 18:45:30 +01:00
// length zero or one below. When TF gains support for
// sub-resources this can be converted.
2015-02-24 18:00:22 +01:00
Type : schema . TypeSet ,
providers/aws: add root_block_device to aws_instance
AWS provides a single `BlockDeviceMapping` to manage three different
kinds of block devices:
(a) The root volume
(b) Ephemeral storage
(c) Additional EBS volumes
Each of these types has slightly different semantics [1].
(a) The root volume is defined by the AMI; it can only be customized
with `volume_size`, `volume_type`, and `delete_on_termination`.
(b) Ephemeral storage is made available based on instance type [2]. It's
attached automatically if _no_ block device mappings are specified, and
must otherwise be defined with block device mapping entries that contain
only DeviceName set to a device like "/dev/sdX" and VirtualName set to
"ephemeralN".
(c) Additional EBS volumes are controlled by mappings that omit
`virtual_name` and can specify `volume_size`, `volume_type`,
`delete_on_termination`, `snapshot_id`, and `encryption`.
After deciding to ignore root block devices to fix #859, we had users
with configurations that were attempting to manage the root block device chime
in on #913.
Terraform does not have the primitives to be able to properly handle a
single collection of resources that is partially managed and partially
computed, so our strategy here is to break out logical sub-resources for
Terraform and hide the BlockDeviceMapping inside the provider
implementation.
Now (a) is supported by the `root_block_device` sub-resource, and (b)
and (c) are still both merged together under `block_device`, though I
have yet to see ephemeral block devices working properly.
Looking into possibly separating out `ephemeral_block_device` and
`ebs_block_device` sub-resources as well, which seem like the logical
next step. We'll wait until the next big release for this, though, since
it will break backcompat.
[1] http://bit.ly/ec2bdmap
[2] http://bit.ly/instancestorebytype
Fixes #913
Refs #858
2015-02-18 18:45:30 +01:00
Optional : true ,
Computed : true ,
Elem : & schema . Resource {
// "You can only modify the volume size, volume type, and Delete on
// Termination flag on the block device mapping entry for the root
// device volume." - bit.ly/ec2bdmap
Schema : map [ string ] * schema . Schema {
"delete_on_termination" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
Default : true ,
ForceNew : true ,
} ,
2015-02-24 18:00:22 +01:00
"iops" : & schema . Schema {
providers/aws: add root_block_device to aws_instance
AWS provides a single `BlockDeviceMapping` to manage three different
kinds of block devices:
(a) The root volume
(b) Ephemeral storage
(c) Additional EBS volumes
Each of these types has slightly different semantics [1].
(a) The root volume is defined by the AMI; it can only be customized
with `volume_size`, `volume_type`, and `delete_on_termination`.
(b) Ephemeral storage is made available based on instance type [2]. It's
attached automatically if _no_ block device mappings are specified, and
must otherwise be defined with block device mapping entries that contain
only DeviceName set to a device like "/dev/sdX" and VirtualName set to
"ephemeralN".
(c) Additional EBS volumes are controlled by mappings that omit
`virtual_name` and can specify `volume_size`, `volume_type`,
`delete_on_termination`, `snapshot_id`, and `encryption`.
After deciding to ignore root block devices to fix #859, we had users
with configurations that were attempting to manage the root block device chime
in on #913.
Terraform does not have the primitives to be able to properly handle a
single collection of resources that is partially managed and partially
computed, so our strategy here is to break out logical sub-resources for
Terraform and hide the BlockDeviceMapping inside the provider
implementation.
Now (a) is supported by the `root_block_device` sub-resource, and (b)
and (c) are still both merged together under `block_device`, though I
have yet to see ephemeral block devices working properly.
Looking into possibly separating out `ephemeral_block_device` and
`ebs_block_device` sub-resources as well, which seem like the logical
next step. We'll wait until the next big release for this, though, since
it will break backcompat.
[1] http://bit.ly/ec2bdmap
[2] http://bit.ly/instancestorebytype
Fixes #913
Refs #858
2015-02-18 18:45:30 +01:00
Type : schema . TypeInt ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
"volume_size" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
"volume_type" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
Computed : true ,
ForceNew : true ,
} ,
} ,
} ,
2015-02-24 18:00:22 +01:00
Set : func ( v interface { } ) int {
2015-04-08 00:05:00 +02:00
// there can be only one root device; no need to hash anything
return 0
2015-02-24 18:00:22 +01:00
} ,
providers/aws: add root_block_device to aws_instance
AWS provides a single `BlockDeviceMapping` to manage three different
kinds of block devices:
(a) The root volume
(b) Ephemeral storage
(c) Additional EBS volumes
Each of these types has slightly different semantics [1].
(a) The root volume is defined by the AMI; it can only be customized
with `volume_size`, `volume_type`, and `delete_on_termination`.
(b) Ephemeral storage is made available based on instance type [2]. It's
attached automatically if _no_ block device mappings are specified, and
must otherwise be defined with block device mapping entries that contain
only DeviceName set to a device like "/dev/sdX" and VirtualName set to
"ephemeralN".
(c) Additional EBS volumes are controlled by mappings that omit
`virtual_name` and can specify `volume_size`, `volume_type`,
`delete_on_termination`, `snapshot_id`, and `encryption`.
After deciding to ignore root block devices to fix #859, we had users
with configurations that were attempting to manage the root block device chime
in on #913.
Terraform does not have the primitives to be able to properly handle a
single collection of resources that is partially managed and partially
computed, so our strategy here is to break out logical sub-resources for
Terraform and hide the BlockDeviceMapping inside the provider
implementation.
Now (a) is supported by the `root_block_device` sub-resource, and (b)
and (c) are still both merged together under `block_device`, though I
have yet to see ephemeral block devices working properly.
Looking into possibly separating out `ephemeral_block_device` and
`ebs_block_device` sub-resources as well, which seem like the logical
next step. We'll wait until the next big release for this, though, since
it will break backcompat.
[1] http://bit.ly/ec2bdmap
[2] http://bit.ly/instancestorebytype
Fixes #913
Refs #858
2015-02-18 18:45:30 +01:00
} ,
2014-08-22 03:38:43 +02:00
} ,
}
}
func resourceAwsInstanceCreate ( d * schema . ResourceData , meta interface { } ) error {
2015-04-16 22:05:55 +02:00
conn := meta . ( * AWSClient ) . ec2conn
2014-06-27 18:47:19 +02:00
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
instanceOpts , err := buildAwsInstanceOpts ( d , meta )
if err != nil {
return err
2014-07-29 14:06:53 +02:00
}
2014-07-15 06:56:37 +02:00
// Build the creation struct
2015-04-16 19:01:10 +02:00
runOpts := & ec2 . RunInstancesInput {
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
BlockDeviceMappings : instanceOpts . BlockDeviceMappings ,
2015-08-17 20:27:16 +02:00
DisableApiTermination : instanceOpts . DisableAPITermination ,
EbsOptimized : instanceOpts . EBSOptimized ,
2015-06-25 16:58:28 +02:00
Monitoring : instanceOpts . Monitoring ,
2015-08-17 20:27:16 +02:00
IamInstanceProfile : instanceOpts . IAMInstanceProfile ,
ImageId : instanceOpts . ImageID ,
2015-07-18 18:45:34 +02:00
InstanceInitiatedShutdownBehavior : instanceOpts . InstanceInitiatedShutdownBehavior ,
2015-08-18 21:40:01 +02:00
InstanceType : instanceOpts . InstanceType ,
KeyName : instanceOpts . KeyName ,
MaxCount : aws . Int64 ( int64 ( 1 ) ) ,
MinCount : aws . Int64 ( int64 ( 1 ) ) ,
NetworkInterfaces : instanceOpts . NetworkInterfaces ,
Placement : instanceOpts . Placement ,
PrivateIpAddress : instanceOpts . PrivateIPAddress ,
SecurityGroupIds : instanceOpts . SecurityGroupIDs ,
SecurityGroups : instanceOpts . SecurityGroups ,
SubnetId : instanceOpts . SubnetID ,
UserData : instanceOpts . UserData64 ,
2015-02-24 18:00:22 +01:00
}
2014-07-15 06:56:37 +02:00
// Create the instance
2015-07-28 22:29:46 +02:00
log . Printf ( "[DEBUG] Run configuration: %s" , runOpts )
2015-05-21 21:58:34 +02:00
var runResp * ec2 . Reservation
for i := 0 ; i < 5 ; i ++ {
runResp , err = conn . RunInstances ( runOpts )
if awsErr , ok := err . ( awserr . Error ) ; ok {
// IAM profiles can take ~10 seconds to propagate in AWS:
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
if awsErr . Code ( ) == "InvalidParameterValue" && strings . Contains ( awsErr . Message ( ) , "Invalid IAM Instance Profile" ) {
log . Printf ( "[DEBUG] Invalid IAM Instance Profile referenced, retrying..." )
time . Sleep ( 2 * time . Second )
continue
}
2015-12-09 22:59:36 +01:00
// Warn if the AWS Error involves group ids, to help identify situation
// where a user uses group ids in security_groups for the Default VPC.
// See https://github.com/hashicorp/terraform/issues/3798
if awsErr . Code ( ) == "InvalidParameterValue" && strings . Contains ( awsErr . Message ( ) , "groupId is invalid" ) {
return fmt . Errorf ( "Error launching instance, possible mismatch of Security Group IDs and Names. See AWS Instance docs here: %s.\n\n\tAWS Error: %s" , "https://terraform.io/docs/providers/aws/r/instance.html" , awsErr . Message ( ) )
}
2015-05-21 21:58:34 +02:00
}
break
}
2014-06-27 18:47:19 +02:00
if err != nil {
2014-08-22 03:38:43 +02:00
return fmt . Errorf ( "Error launching source instance: %s" , err )
2014-06-27 18:47:19 +02:00
}
2015-12-08 21:22:51 +01:00
if runResp == nil || len ( runResp . Instances ) == 0 {
return fmt . Errorf ( "Error launching source instance: no instances returned in response" )
}
2014-06-27 18:47:19 +02:00
2015-04-16 19:01:10 +02:00
instance := runResp . Instances [ 0 ]
2015-08-17 20:27:16 +02:00
log . Printf ( "[INFO] Instance ID: %s" , * instance . InstanceId )
2014-06-27 18:47:19 +02:00
// Store the resulting ID so we can look this up later
2015-08-17 20:27:16 +02:00
d . SetId ( * instance . InstanceId )
2014-06-27 18:47:19 +02:00
// 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" ,
2015-08-17 20:27:16 +02:00
* instance . InstanceId )
2014-07-01 19:10:11 +02:00
stateConf := & resource . StateChangeConf {
2014-07-28 18:10:28 +02:00
Pending : [ ] string { "pending" } ,
2016-01-21 02:20:41 +01:00
Target : [ ] string { "running" } ,
2015-08-17 20:27:16 +02:00
Refresh : InstanceStateRefreshFunc ( conn , * instance . InstanceId ) ,
2014-07-28 18:10:28 +02:00
Timeout : 10 * time . Minute ,
Delay : 10 * time . Second ,
MinTimeout : 3 * time . Second ,
2014-07-01 19:10:11 +02:00
}
instanceRaw , err := stateConf . WaitForState ( )
2014-06-27 18:47:19 +02:00
if err != nil {
2014-08-22 03:38:43 +02:00
return fmt . Errorf (
2014-06-27 18:47:19 +02:00
"Error waiting for instance (%s) to become ready: %s" ,
2015-08-17 20:27:16 +02:00
* instance . InstanceId , err )
2014-06-27 18:47:19 +02:00
}
2014-07-01 19:10:11 +02:00
2014-06-27 18:47:19 +02:00
instance = instanceRaw . ( * ec2 . Instance )
2014-07-15 02:24:10 +02:00
// Initialize the connection info
2015-08-17 20:27:16 +02:00
if instance . PublicIpAddress != nil {
2015-03-05 16:45:39 +01:00
d . SetConnInfo ( map [ string ] string {
"type" : "ssh" ,
2015-08-17 20:27:16 +02:00
"host" : * instance . PublicIpAddress ,
2015-03-05 16:45:39 +01:00
} )
2015-08-17 20:27:16 +02:00
} else if instance . PrivateIpAddress != nil {
2015-04-22 12:25:28 +02:00
d . SetConnInfo ( map [ string ] string {
"type" : "ssh" ,
2015-08-17 20:27:16 +02:00
"host" : * instance . PrivateIpAddress ,
2015-04-22 12:25:28 +02:00
} )
2015-03-05 16:45:39 +01:00
}
2014-07-15 02:24:10 +02:00
2014-07-14 23:16:59 +02:00
// Update if we need to
2014-08-22 03:38:43 +02:00
return resourceAwsInstanceUpdate ( d , meta )
2014-07-14 23:16:59 +02:00
}
2014-08-22 03:38:43 +02:00
func resourceAwsInstanceRead ( d * schema . ResourceData , meta interface { } ) error {
2015-04-16 22:05:55 +02:00
conn := meta . ( * AWSClient ) . ec2conn
2014-06-27 18:47:19 +02:00
2015-04-16 19:01:10 +02:00
resp , err := conn . DescribeInstances ( & ec2 . DescribeInstancesInput {
2015-08-17 20:27:16 +02:00
InstanceIds : [ ] * string { aws . String ( d . Id ( ) ) } ,
2015-03-05 16:45:39 +01:00
} )
2014-06-27 18:47:19 +02:00
if err != nil {
// If the instance was not found, return nil so that we can show
// that the instance is gone.
2015-05-20 13:21:23 +02:00
if ec2err , ok := err . ( awserr . Error ) ; ok && ec2err . Code ( ) == "InvalidInstanceID.NotFound" {
2014-08-22 03:38:43 +02:00
d . SetId ( "" )
return nil
2014-06-27 18:47:19 +02:00
}
// Some other error, report it
2014-08-22 03:38:43 +02:00
return err
2014-06-27 18:47:19 +02:00
}
// If nothing was found, then return no state
if len ( resp . Reservations ) == 0 {
2014-08-22 03:38:43 +02:00
d . SetId ( "" )
return nil
2014-06-27 18:47:19 +02:00
}
2015-04-16 19:01:10 +02:00
instance := resp . Reservations [ 0 ] . Instances [ 0 ]
2014-06-27 18:47:19 +02:00
2015-09-17 12:26:38 +02:00
if instance . State != nil {
// If the instance is terminated, then it is gone
if * instance . State . Name == "terminated" {
d . SetId ( "" )
return nil
}
d . Set ( "instance_state" , instance . State . Name )
2014-06-27 18:47:19 +02:00
}
2015-03-19 16:07:46 +01:00
if instance . Placement != nil {
d . Set ( "availability_zone" , instance . Placement . AvailabilityZone )
}
if instance . Placement . Tenancy != nil {
d . Set ( "tenancy" , instance . Placement . Tenancy )
}
2015-08-17 20:27:16 +02:00
d . Set ( "ami" , instance . ImageId )
2015-06-16 20:23:50 +02:00
d . Set ( "instance_type" , instance . InstanceType )
2014-08-22 03:38:43 +02:00
d . Set ( "key_name" , instance . KeyName )
2015-08-17 20:27:16 +02:00
d . Set ( "public_dns" , instance . PublicDnsName )
d . Set ( "public_ip" , instance . PublicIpAddress )
d . Set ( "private_dns" , instance . PrivateDnsName )
d . Set ( "private_ip" , instance . PrivateIpAddress )
2015-09-05 02:20:58 +02:00
d . Set ( "iam_instance_profile" , iamInstanceProfileArnToName ( instance . IamInstanceProfile ) )
2015-09-03 21:43:41 +02:00
2015-03-05 16:45:39 +01:00
if len ( instance . NetworkInterfaces ) > 0 {
2015-08-17 20:27:16 +02:00
d . Set ( "subnet_id" , instance . NetworkInterfaces [ 0 ] . SubnetId )
2015-03-05 16:45:39 +01:00
} else {
2015-08-17 20:27:16 +02:00
d . Set ( "subnet_id" , instance . SubnetId )
2015-03-05 16:45:39 +01:00
}
2015-08-17 20:27:16 +02:00
d . Set ( "ebs_optimized" , instance . EbsOptimized )
2015-09-02 20:04:45 +02:00
if instance . SubnetId != nil && * instance . SubnetId != "" {
d . Set ( "source_dest_check" , instance . SourceDestCheck )
}
2015-06-25 16:58:28 +02:00
if instance . Monitoring != nil && instance . Monitoring . State != nil {
2015-06-25 18:07:11 +02:00
monitoringState := * instance . Monitoring . State
d . Set ( "monitoring" , monitoringState == "enabled" || monitoringState == "pending" )
2015-06-25 16:58:28 +02:00
}
2015-05-12 21:58:10 +02:00
d . Set ( "tags" , tagsToMap ( instance . Tags ) )
2014-08-22 03:38:43 +02:00
// 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.
2015-08-17 20:27:16 +02:00
useID := instance . SubnetId != nil && * instance . SubnetId != ""
2014-08-22 03:38:43 +02:00
if v := d . Get ( "security_groups" ) ; v != nil {
2015-04-22 00:07:30 +02:00
match := useID
sgs := v . ( * schema . Set ) . List ( )
if len ( sgs ) > 0 {
match = false
for _ , v := range v . ( * schema . Set ) . List ( ) {
if strings . HasPrefix ( v . ( string ) , "sg-" ) {
match = true
break
}
2014-07-28 18:47:40 +02:00
}
}
2014-08-22 03:38:43 +02:00
useID = match
2014-07-28 18:47:40 +02:00
}
2014-07-15 06:56:37 +02:00
// Build up the security groups
2015-04-15 19:12:17 +02:00
sgs := make ( [ ] string , 0 , len ( instance . SecurityGroups ) )
2015-03-07 07:04:53 +01:00
if useID {
2015-04-15 19:12:17 +02:00
for _ , sg := range instance . SecurityGroups {
2015-08-17 20:27:16 +02:00
sgs = append ( sgs , * sg . GroupId )
2015-03-07 07:04:53 +01:00
}
2015-04-15 19:12:17 +02:00
log . Printf ( "[DEBUG] Setting Security Group IDs: %#v" , sgs )
if err := d . Set ( "vpc_security_group_ids" , sgs ) ; err != nil {
return err
2015-03-07 07:16:59 +01:00
}
2015-03-07 07:04:53 +01:00
} else {
2015-04-15 19:12:17 +02:00
for _ , sg := range instance . SecurityGroups {
sgs = append ( sgs , * sg . GroupName )
2014-07-15 06:56:37 +02:00
}
2015-04-15 19:12:17 +02:00
log . Printf ( "[DEBUG] Setting Security Group Names: %#v" , sgs )
if err := d . Set ( "security_groups" , sgs ) ; err != nil {
2014-12-25 18:21:05 +01:00
return err
}
2014-07-15 06:56:37 +02:00
}
2015-02-18 23:45:13 +01:00
2015-04-16 19:01:10 +02:00
if err := readBlockDevices ( d , instance , conn ) ; err != nil {
2014-10-17 18:12:45 +02:00
return err
}
2014-08-22 03:38:43 +02:00
return nil
2014-06-27 18:47:19 +02:00
}
2014-07-01 19:10:11 +02:00
2014-11-21 17:58:34 +01:00
func resourceAwsInstanceUpdate ( d * schema . ResourceData , meta interface { } ) error {
2015-04-16 22:05:55 +02:00
conn := meta . ( * AWSClient ) . ec2conn
2015-04-16 19:01:10 +02:00
d . Partial ( true )
2015-05-20 22:33:01 +02:00
if err := setTags ( conn , d ) ; err != nil {
return err
} else {
d . SetPartial ( "tags" )
}
2014-11-21 17:58:34 +01:00
2015-03-18 20:54:44 +01:00
// SourceDestCheck can only be set on VPC instances
2015-10-02 00:00:30 +02:00
// AWS will return an error of InvalidParameterCombination if we attempt
// to modify the source_dest_check of an instance in EC2 Classic
log . Printf ( "[INFO] Modifying instance %s" , d . Id ( ) )
_ , err := conn . ModifyInstanceAttribute ( & ec2 . ModifyInstanceAttributeInput {
InstanceId : aws . String ( d . Id ( ) ) ,
SourceDestCheck : & ec2 . AttributeBooleanValue {
Value : aws . Bool ( d . Get ( "source_dest_check" ) . ( bool ) ) ,
} ,
} )
if err != nil {
if ec2err , ok := err . ( awserr . Error ) ; ok {
// Toloerate InvalidParameterCombination error in Classic, otherwise
// return the error
if "InvalidParameterCombination" != ec2err . Code ( ) {
return err
}
log . Printf ( "[WARN] Attempted to modify SourceDestCheck on non VPC instance: %s" , ec2err . Message ( ) )
2015-03-18 20:54:44 +01:00
}
2014-11-21 17:58:34 +01:00
}
2015-04-22 00:07:30 +02:00
if d . HasChange ( "vpc_security_group_ids" ) {
var groups [ ] * string
2015-06-11 14:59:42 +02:00
if v := d . Get ( "vpc_security_group_ids" ) . ( * schema . Set ) ; v . Len ( ) > 0 {
for _ , v := range v . List ( ) {
2015-04-22 00:07:30 +02:00
groups = append ( groups , aws . String ( v . ( string ) ) )
}
}
_ , err := conn . ModifyInstanceAttribute ( & ec2 . ModifyInstanceAttributeInput {
2015-08-17 20:27:16 +02:00
InstanceId : aws . String ( d . Id ( ) ) ,
2015-04-22 00:07:30 +02:00
Groups : groups ,
} )
if err != nil {
return err
}
2015-05-15 21:18:05 +02:00
}
2015-04-22 00:07:30 +02:00
2015-05-15 21:18:05 +02:00
if d . HasChange ( "disable_api_termination" ) {
_ , err := conn . ModifyInstanceAttribute ( & ec2 . ModifyInstanceAttributeInput {
2015-08-17 20:27:16 +02:00
InstanceId : aws . String ( d . Id ( ) ) ,
DisableApiTermination : & ec2 . AttributeBooleanValue {
2015-07-28 22:29:46 +02:00
Value : aws . Bool ( d . Get ( "disable_api_termination" ) . ( bool ) ) ,
2015-05-15 21:18:05 +02:00
} ,
} )
if err != nil {
return err
}
2015-04-22 00:07:30 +02:00
}
2015-07-18 18:45:34 +02:00
if d . HasChange ( "instance_initiated_shutdown_behavior" ) {
log . Printf ( "[INFO] Modifying instance %s" , d . Id ( ) )
_ , err := conn . ModifyInstanceAttribute ( & ec2 . ModifyInstanceAttributeInput {
2015-08-18 21:40:01 +02:00
InstanceId : aws . String ( d . Id ( ) ) ,
2015-07-18 18:45:34 +02:00
InstanceInitiatedShutdownBehavior : & ec2 . AttributeValue {
Value : aws . String ( d . Get ( "instance_initiated_shutdown_behavior" ) . ( string ) ) ,
} ,
} )
if err != nil {
return err
}
}
2015-07-29 22:54:56 +02:00
2015-07-20 19:32:58 +02:00
if d . HasChange ( "monitoring" ) {
var mErr error
if d . Get ( "monitoring" ) . ( bool ) {
log . Printf ( "[DEBUG] Enabling monitoring for Instance (%s)" , d . Id ( ) )
_ , mErr = conn . MonitorInstances ( & ec2 . MonitorInstancesInput {
2015-08-17 20:27:16 +02:00
InstanceIds : [ ] * string { aws . String ( d . Id ( ) ) } ,
2015-07-20 19:32:58 +02:00
} )
} else {
log . Printf ( "[DEBUG] Disabling monitoring for Instance (%s)" , d . Id ( ) )
_ , mErr = conn . UnmonitorInstances ( & ec2 . UnmonitorInstancesInput {
2015-08-17 20:27:16 +02:00
InstanceIds : [ ] * string { aws . String ( d . Id ( ) ) } ,
2015-07-20 19:32:58 +02:00
} )
}
if mErr != nil {
return fmt . Errorf ( "[WARN] Error updating Instance monitoring: %s" , mErr )
}
}
2015-02-21 21:26:46 +01:00
// TODO(mitchellh): wait for the attributes we modified to
// persist the change...
2015-04-16 19:01:10 +02:00
d . Partial ( false )
2014-11-21 17:58:34 +01:00
2015-04-16 19:01:10 +02:00
return resourceAwsInstanceRead ( d , meta )
2014-11-21 17:58:34 +01:00
}
func resourceAwsInstanceDelete ( d * schema . ResourceData , meta interface { } ) error {
2015-04-16 22:05:55 +02:00
conn := meta . ( * AWSClient ) . ec2conn
2014-11-21 17:58:34 +01:00
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
if err := awsTerminateInstance ( conn , d . Id ( ) ) ; err != nil {
return err
2014-11-21 17:58:34 +01:00
}
d . SetId ( "" )
2014-08-22 03:38:43 +02:00
return nil
2014-06-27 18:47:19 +02:00
}
2014-07-01 19:10:11 +02:00
// InstanceStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
// an EC2 instance.
func InstanceStateRefreshFunc ( conn * ec2 . EC2 , instanceID string ) resource . StateRefreshFunc {
return func ( ) ( interface { } , string , error ) {
2015-04-16 19:01:10 +02:00
resp , err := conn . DescribeInstances ( & ec2 . DescribeInstancesInput {
2015-08-17 20:27:16 +02:00
InstanceIds : [ ] * string { aws . String ( instanceID ) } ,
2015-03-05 16:45:39 +01:00
} )
2014-07-01 19:10:11 +02:00
if err != nil {
2015-05-20 13:21:23 +02:00
if ec2err , ok := err . ( awserr . Error ) ; ok && ec2err . Code ( ) == "InvalidInstanceID.NotFound" {
2014-07-01 19:10:11 +02:00
// Set this to nil as if we didn't find anything.
resp = nil
} else {
log . Printf ( "Error on InstanceStateRefresh: %s" , err )
return nil , "" , err
}
}
if resp == nil || len ( resp . Reservations ) == 0 || len ( resp . Reservations [ 0 ] . Instances ) == 0 {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil , "" , nil
}
2015-04-16 19:01:10 +02:00
i := resp . Reservations [ 0 ] . Instances [ 0 ]
2015-03-05 16:45:39 +01:00
return i , * i . State . Name , nil
2014-07-01 19:10:11 +02:00
}
}
2014-10-17 18:12:45 +02:00
2015-04-16 19:01:10 +02:00
func readBlockDevices ( d * schema . ResourceData , instance * ec2 . Instance , conn * ec2 . EC2 ) error {
ibds , err := readBlockDevicesFromInstance ( instance , conn )
2015-02-24 18:00:22 +01:00
if err != nil {
return err
}
if err := d . Set ( "ebs_block_device" , ibds [ "ebs" ] ) ; err != nil {
return err
}
if ibds [ "root" ] != nil {
if err := d . Set ( "root_block_device" , [ ] interface { } { ibds [ "root" ] } ) ; err != nil {
return err
}
2014-07-01 19:10:11 +02:00
}
2015-02-24 18:00:22 +01:00
return nil
2014-07-01 19:10:11 +02:00
}
2014-10-17 18:12:45 +02:00
2015-04-16 19:01:10 +02:00
func readBlockDevicesFromInstance ( instance * ec2 . Instance , conn * ec2 . EC2 ) ( map [ string ] interface { } , error ) {
2015-02-24 18:00:22 +01:00
blockDevices := make ( map [ string ] interface { } )
blockDevices [ "ebs" ] = make ( [ ] map [ string ] interface { } , 0 )
blockDevices [ "root" ] = nil
2015-04-16 19:01:10 +02:00
instanceBlockDevices := make ( map [ string ] * ec2 . InstanceBlockDeviceMapping )
2015-02-24 18:00:22 +01:00
for _ , bd := range instance . BlockDeviceMappings {
2015-08-17 20:27:16 +02:00
if bd . Ebs != nil {
2015-10-08 14:48:04 +02:00
instanceBlockDevices [ * bd . Ebs . VolumeId ] = bd
2015-02-24 18:00:22 +01:00
}
}
2015-03-19 19:14:31 +01:00
if len ( instanceBlockDevices ) == 0 {
return nil , nil
}
2015-04-16 19:01:10 +02:00
volIDs := make ( [ ] * string , 0 , len ( instanceBlockDevices ) )
2015-02-24 18:00:22 +01:00
for volID := range instanceBlockDevices {
2015-04-16 19:01:10 +02:00
volIDs = append ( volIDs , aws . String ( volID ) )
2015-02-24 18:00:22 +01:00
}
// Need to call DescribeVolumes to get volume_size and volume_type for each
// EBS block device
2015-04-16 19:01:10 +02:00
volResp , err := conn . DescribeVolumes ( & ec2 . DescribeVolumesInput {
2015-08-17 20:27:16 +02:00
VolumeIds : volIDs ,
2015-02-24 18:00:22 +01:00
} )
if err != nil {
return nil , err
}
for _ , vol := range volResp . Volumes {
2015-08-17 20:27:16 +02:00
instanceBd := instanceBlockDevices [ * vol . VolumeId ]
2015-02-24 18:00:22 +01:00
bd := make ( map [ string ] interface { } )
2015-08-17 20:27:16 +02:00
if instanceBd . Ebs != nil && instanceBd . Ebs . DeleteOnTermination != nil {
bd [ "delete_on_termination" ] = * instanceBd . Ebs . DeleteOnTermination
2015-02-24 18:00:22 +01:00
}
if vol . Size != nil {
bd [ "volume_size" ] = * vol . Size
}
if vol . VolumeType != nil {
bd [ "volume_type" ] = * vol . VolumeType
}
2015-08-17 20:27:16 +02:00
if vol . Iops != nil {
bd [ "iops" ] = * vol . Iops
2015-02-24 18:00:22 +01:00
}
if blockDeviceIsRoot ( instanceBd , instance ) {
blockDevices [ "root" ] = bd
} else {
2015-03-23 17:58:45 +01:00
if instanceBd . DeviceName != nil {
bd [ "device_name" ] = * instanceBd . DeviceName
}
2015-02-24 18:00:22 +01:00
if vol . Encrypted != nil {
bd [ "encrypted" ] = * vol . Encrypted
}
2015-08-17 20:27:16 +02:00
if vol . SnapshotId != nil {
bd [ "snapshot_id" ] = * vol . SnapshotId
2015-02-24 18:00:22 +01:00
}
blockDevices [ "ebs" ] = append ( blockDevices [ "ebs" ] . ( [ ] map [ string ] interface { } ) , bd )
}
}
return blockDevices , nil
}
2015-04-16 19:01:10 +02:00
func blockDeviceIsRoot ( bd * ec2 . InstanceBlockDeviceMapping , instance * ec2 . Instance ) bool {
2015-10-08 14:48:04 +02:00
return bd . DeviceName != nil &&
2015-02-24 18:00:22 +01:00
instance . RootDeviceName != nil &&
2015-10-08 14:48:04 +02:00
* bd . DeviceName == * instance . RootDeviceName
2014-10-17 18:12:45 +02:00
}
2015-03-23 17:58:45 +01:00
2015-04-16 19:01:10 +02:00
func fetchRootDeviceName ( ami string , conn * ec2 . EC2 ) ( * string , error ) {
2015-03-23 17:58:45 +01:00
if ami == "" {
return nil , fmt . Errorf ( "Cannot fetch root device name for blank AMI ID." )
}
log . Printf ( "[DEBUG] Describing AMI %q to get root block device name" , ami )
2015-06-08 18:00:05 +02:00
res , err := conn . DescribeImages ( & ec2 . DescribeImagesInput {
2015-08-17 20:27:16 +02:00
ImageIds : [ ] * string { aws . String ( ami ) } ,
2015-06-08 18:00:05 +02:00
} )
if err != nil {
2015-03-23 17:58:45 +01:00
return nil , err
}
2015-06-08 18:00:05 +02:00
// For a bad image, we just return nil so we don't block a refresh
if len ( res . Images ) == 0 {
return nil , nil
}
image := res . Images [ 0 ]
rootDeviceName := image . RootDeviceName
// Some AMIs have a RootDeviceName like "/dev/sda1" that does not appear as a
// DeviceName in the BlockDeviceMapping list (which will instead have
// something like "/dev/sda")
//
// While this seems like it breaks an invariant of AMIs, it ends up working
// on the AWS side, and AMIs like this are common enough that we need to
// special case it so Terraform does the right thing.
//
// Our heuristic is: if the RootDeviceName does not appear in the
// BlockDeviceMapping, assume that the DeviceName of the first
// BlockDeviceMapping entry serves as the root device.
rootDeviceNameInMapping := false
for _ , bdm := range image . BlockDeviceMappings {
if bdm . DeviceName == image . RootDeviceName {
rootDeviceNameInMapping = true
}
}
if ! rootDeviceNameInMapping && len ( image . BlockDeviceMappings ) > 0 {
rootDeviceName = image . BlockDeviceMappings [ 0 ] . DeviceName
}
2015-07-28 22:29:46 +02:00
if rootDeviceName == nil {
return nil , fmt . Errorf ( "[WARN] Error finding Root Device Name for AMI (%s)" , ami )
}
2015-07-07 23:12:41 +02:00
2015-06-08 18:00:05 +02:00
return rootDeviceName , nil
2014-10-17 18:12:45 +02:00
}
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
func readBlockDeviceMappingsFromConfig (
d * schema . ResourceData , conn * ec2 . EC2 ) ( [ ] * ec2 . BlockDeviceMapping , error ) {
blockDevices := make ( [ ] * ec2 . BlockDeviceMapping , 0 )
if v , ok := d . GetOk ( "ebs_block_device" ) ; ok {
vL := v . ( * schema . Set ) . List ( )
for _ , v := range vL {
bd := v . ( map [ string ] interface { } )
2015-08-17 20:27:16 +02:00
ebs := & ec2 . EbsBlockDevice {
2015-07-28 22:29:46 +02:00
DeleteOnTermination : aws . Bool ( bd [ "delete_on_termination" ] . ( bool ) ) ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "snapshot_id" ] . ( string ) ; ok && v != "" {
2015-08-17 20:27:16 +02:00
ebs . SnapshotId = aws . String ( v )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "encrypted" ] . ( bool ) ; ok && v {
2015-07-28 22:29:46 +02:00
ebs . Encrypted = aws . Bool ( v )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "volume_size" ] . ( int ) ; ok && v != 0 {
2015-07-28 22:29:46 +02:00
ebs . VolumeSize = aws . Int64 ( int64 ( v ) )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "volume_type" ] . ( string ) ; ok && v != "" {
ebs . VolumeType = aws . String ( v )
}
if v , ok := bd [ "iops" ] . ( int ) ; ok && v > 0 {
2015-08-17 20:27:16 +02:00
ebs . Iops = aws . Int64 ( int64 ( v ) )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
blockDevices = append ( blockDevices , & ec2 . BlockDeviceMapping {
DeviceName : aws . String ( bd [ "device_name" ] . ( string ) ) ,
2015-08-17 20:27:16 +02:00
Ebs : ebs ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
} )
}
}
if v , ok := d . GetOk ( "ephemeral_block_device" ) ; ok {
vL := v . ( * schema . Set ) . List ( )
for _ , v := range vL {
bd := v . ( map [ string ] interface { } )
blockDevices = append ( blockDevices , & ec2 . BlockDeviceMapping {
DeviceName : aws . String ( bd [ "device_name" ] . ( string ) ) ,
VirtualName : aws . String ( bd [ "virtual_name" ] . ( string ) ) ,
} )
}
}
if v , ok := d . GetOk ( "root_block_device" ) ; ok {
vL := v . ( * schema . Set ) . List ( )
if len ( vL ) > 1 {
return nil , fmt . Errorf ( "Cannot specify more than one root_block_device." )
}
for _ , v := range vL {
bd := v . ( map [ string ] interface { } )
2015-08-17 20:27:16 +02:00
ebs := & ec2 . EbsBlockDevice {
2015-07-28 22:29:46 +02:00
DeleteOnTermination : aws . Bool ( bd [ "delete_on_termination" ] . ( bool ) ) ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "volume_size" ] . ( int ) ; ok && v != 0 {
2015-07-28 22:29:46 +02:00
ebs . VolumeSize = aws . Int64 ( int64 ( v ) )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if v , ok := bd [ "volume_type" ] . ( string ) ; ok && v != "" {
ebs . VolumeType = aws . String ( v )
}
if v , ok := bd [ "iops" ] . ( int ) ; ok && v > 0 {
2015-08-17 20:27:16 +02:00
ebs . Iops = aws . Int64 ( int64 ( v ) )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if dn , err := fetchRootDeviceName ( d . Get ( "ami" ) . ( string ) , conn ) ; err == nil {
if dn == nil {
return nil , fmt . Errorf (
"Expected 1 AMI for ID: %s, got none" ,
d . Get ( "ami" ) . ( string ) )
}
blockDevices = append ( blockDevices , & ec2 . BlockDeviceMapping {
DeviceName : dn ,
2015-08-17 20:27:16 +02:00
Ebs : ebs ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
} )
} else {
return nil , err
}
}
}
return blockDevices , nil
}
type awsInstanceOpts struct {
2015-07-29 22:54:56 +02:00
BlockDeviceMappings [ ] * ec2 . BlockDeviceMapping
DisableAPITermination * bool
EBSOptimized * bool
Monitoring * ec2 . RunInstancesMonitoringEnabled
2015-08-18 21:40:01 +02:00
IAMInstanceProfile * ec2 . IamInstanceProfileSpecification
2015-07-29 22:54:56 +02:00
ImageID * string
InstanceInitiatedShutdownBehavior * string
2015-08-18 21:40:01 +02:00
InstanceType * string
2015-07-29 22:54:56 +02:00
KeyName * string
NetworkInterfaces [ ] * ec2 . InstanceNetworkInterfaceSpecification
Placement * ec2 . Placement
PrivateIPAddress * string
SecurityGroupIDs [ ] * string
SecurityGroups [ ] * string
SpotPlacement * ec2 . SpotPlacement
SubnetID * string
UserData64 * string
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
func buildAwsInstanceOpts (
d * schema . ResourceData , meta interface { } ) ( * awsInstanceOpts , error ) {
conn := meta . ( * AWSClient ) . ec2conn
opts := & awsInstanceOpts {
2015-08-04 13:09:47 +02:00
DisableAPITermination : aws . Bool ( d . Get ( "disable_api_termination" ) . ( bool ) ) ,
EBSOptimized : aws . Bool ( d . Get ( "ebs_optimized" ) . ( bool ) ) ,
ImageID : aws . String ( d . Get ( "ami" ) . ( string ) ) ,
InstanceType : aws . String ( d . Get ( "instance_type" ) . ( string ) ) ,
}
if v := d . Get ( "instance_initiated_shutdown_behavior" ) . ( string ) ; v != "" {
opts . InstanceInitiatedShutdownBehavior = aws . String ( v )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
2015-06-25 16:58:28 +02:00
opts . Monitoring = & ec2 . RunInstancesMonitoringEnabled {
2015-07-28 22:29:46 +02:00
Enabled : aws . Bool ( d . Get ( "monitoring" ) . ( bool ) ) ,
2015-06-25 16:58:28 +02:00
}
2015-08-17 20:27:16 +02:00
opts . IAMInstanceProfile = & ec2 . IamInstanceProfileSpecification {
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
Name : aws . String ( d . Get ( "iam_instance_profile" ) . ( string ) ) ,
}
opts . UserData64 = aws . String (
base64 . StdEncoding . EncodeToString ( [ ] byte ( d . Get ( "user_data" ) . ( string ) ) ) )
// check for non-default Subnet, and cast it to a String
subnet , hasSubnet := d . GetOk ( "subnet_id" )
subnetID := subnet . ( string )
// Placement is used for aws_instance; SpotPlacement is used for
// aws_spot_instance_request. They represent the same data. :-|
opts . Placement = & ec2 . Placement {
AvailabilityZone : aws . String ( d . Get ( "availability_zone" ) . ( string ) ) ,
GroupName : aws . String ( d . Get ( "placement_group" ) . ( string ) ) ,
}
opts . SpotPlacement = & ec2 . SpotPlacement {
AvailabilityZone : aws . String ( d . Get ( "availability_zone" ) . ( string ) ) ,
GroupName : aws . String ( d . Get ( "placement_group" ) . ( string ) ) ,
}
if v := d . Get ( "tenancy" ) . ( string ) ; v != "" {
opts . Placement . Tenancy = aws . String ( v )
}
associatePublicIPAddress := d . Get ( "associate_public_ip_address" ) . ( bool )
var groups [ ] * string
if v := d . Get ( "security_groups" ) ; v != nil {
// Security group names.
// For a nondefault VPC, you must use security group IDs instead.
// See http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html
sgs := v . ( * schema . Set ) . List ( )
if len ( sgs ) > 0 && hasSubnet {
log . Printf ( "[WARN] Deprecated. Attempting to use 'security_groups' within a VPC instance. Use 'vpc_security_group_ids' instead." )
}
for _ , v := range sgs {
str := v . ( string )
groups = append ( groups , aws . String ( str ) )
}
}
if hasSubnet && associatePublicIPAddress {
// If we have a non-default VPC / Subnet specified, we can flag
// AssociatePublicIpAddress to get a Public IP assigned. By default these are not provided.
// You cannot specify both SubnetId and the NetworkInterface.0.* parameters though, otherwise
// you get: Network interfaces and an instance-level subnet ID may not be specified on the same request
// You also need to attach Security Groups to the NetworkInterface instead of the instance,
// to avoid: Network interfaces and an instance-level security groups may not be specified on
// the same request
ni := & ec2 . InstanceNetworkInterfaceSpecification {
2015-08-17 20:27:16 +02:00
AssociatePublicIpAddress : aws . Bool ( associatePublicIPAddress ) ,
2015-07-28 22:29:46 +02:00
DeviceIndex : aws . Int64 ( int64 ( 0 ) ) ,
2015-08-17 20:27:16 +02:00
SubnetId : aws . String ( subnetID ) ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
Groups : groups ,
}
if v , ok := d . GetOk ( "private_ip" ) ; ok {
2015-08-17 20:27:16 +02:00
ni . PrivateIpAddress = aws . String ( v . ( string ) )
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
2015-06-11 14:59:42 +02:00
if v := d . Get ( "vpc_security_group_ids" ) . ( * schema . Set ) ; v . Len ( ) > 0 {
for _ , v := range v . List ( ) {
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
ni . Groups = append ( ni . Groups , aws . String ( v . ( string ) ) )
}
}
opts . NetworkInterfaces = [ ] * ec2 . InstanceNetworkInterfaceSpecification { ni }
} else {
if subnetID != "" {
opts . SubnetID = aws . String ( subnetID )
}
if v , ok := d . GetOk ( "private_ip" ) ; ok {
opts . PrivateIPAddress = aws . String ( v . ( string ) )
}
if opts . SubnetID != nil &&
* opts . SubnetID != "" {
opts . SecurityGroupIDs = groups
} else {
opts . SecurityGroups = groups
}
2015-06-11 14:59:42 +02:00
if v := d . Get ( "vpc_security_group_ids" ) . ( * schema . Set ) ; v . Len ( ) > 0 {
for _ , v := range v . List ( ) {
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
opts . SecurityGroupIDs = append ( opts . SecurityGroupIDs , aws . String ( v . ( string ) ) )
}
}
}
if v , ok := d . GetOk ( "key_name" ) ; ok {
opts . KeyName = aws . String ( v . ( string ) )
}
blockDevices , err := readBlockDeviceMappingsFromConfig ( d , conn )
if err != nil {
return nil , err
}
if len ( blockDevices ) > 0 {
opts . BlockDeviceMappings = blockDevices
}
return opts , nil
}
func awsTerminateInstance ( conn * ec2 . EC2 , id string ) error {
log . Printf ( "[INFO] Terminating instance: %s" , id )
req := & ec2 . TerminateInstancesInput {
2015-08-17 20:27:16 +02:00
InstanceIds : [ ] * string { aws . String ( id ) } ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
}
if _ , err := conn . TerminateInstances ( req ) ; err != nil {
return fmt . Errorf ( "Error terminating instance: %s" , err )
}
log . Printf ( "[DEBUG] Waiting for instance (%s) to become terminated" , id )
stateConf := & resource . StateChangeConf {
Pending : [ ] string { "pending" , "running" , "shutting-down" , "stopped" , "stopping" } ,
2016-01-21 02:20:41 +01:00
Target : [ ] string { "terminated" } ,
provider/aws: spot_instance_request
This is an iteration on the great work done by @dalehamel in PRs #2095
and #2109.
The core team went back and forth on how to best model Spot Instance
Requests, requesting and then rejecting a separate-resource
implementation in #2109.
After more internal discussion, we landed once again on a separate
resource to model Spot Instance Requests. Out of respect for
@dalehamel's already-significant donated time, with this I'm attempting
to pick up the work to take this across the finish line.
Important architectural decisions represented here:
* Spot Instance Requests are always of type "persistent", to properly
match Terraform's declarative model.
* The spot_instance_request resource exports several attributes that
are expected to be constantly changing as the spot market changes:
spot_bid_status, spot_request_state, and instance_id. Creating
additional resource dependencies based on these attributes is not
recommended, as Terraform diffs will be continually generated to keep
up with the live changes.
* When a Spot Instance Request is deleted/canceled, an attempt is made
to terminate the last-known attached spot instance. Race conditions
dictate that this attempt cannot guarantee that the associated spot
instance is terminated immediately.
Implementation notes:
* This version of aws_spot_instance_request borrows a lot of common
code from aws_instance.
* In order to facilitate borrowing, we introduce `awsInstanceOpts`, an
internal representation of instance details that's meant to be shared
between resources. The goal here would be to refactor ASG Launch
Configurations to use the same struct.
* The new aws_spot_instance_request acc. test is passing.
* All aws_instance acc. tests remain passing.
2015-06-05 17:12:09 +02:00
Refresh : InstanceStateRefreshFunc ( conn , id ) ,
Timeout : 10 * time . Minute ,
Delay : 10 * time . Second ,
MinTimeout : 3 * time . Second ,
}
_ , err := stateConf . WaitForState ( )
if err != nil {
return fmt . Errorf (
"Error waiting for instance (%s) to terminate: %s" , id , err )
}
return nil
}
2015-09-03 21:43:41 +02:00
2015-09-05 02:20:58 +02:00
func iamInstanceProfileArnToName ( ip * ec2 . IamInstanceProfile ) string {
2015-09-06 19:01:57 +02:00
if ip == nil || ip . Arn == nil {
2015-09-05 02:20:58 +02:00
return ""
}
2015-10-28 03:30:11 +01:00
parts := strings . Split ( * ip . Arn , "/" )
return parts [ len ( parts ) - 1 ]
2015-09-03 21:43:41 +02:00
}