-
Notifications
You must be signed in to change notification settings - Fork 9.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Data Source: aws_ec2_spot_price (#12504)
* d/aws_ec2_instance_spot_price - add new data source Adds a data source to fetch the most recently published Spot Price value for a given EC2 instance type and availability zone. The value can be manipulated and fed into spot price parameters in other resources, allowing for dynamic adjusted of spot requests. * remove accidental inclusion of local test example * make instance_type and availability_zone optional * simplify DescribeSpotPriceHistory usage since we are only interested in fetching the latest SpotPrice, we don't need to iterate over a series of requests, as we only ever need to fetch call's worth of data * copypasta * add more thorough tests for aws_ec2_instance_spot_price resource * use dynamic data sources for filtering in tests * format spot_price_timestamp as RFC3339 * rebase aws/provider.go * fix up tests * lint and typo fixes * update docs * whitespace * aws_ec2_instance_spot_price -> aws_ec2_spot_price * support spot price data by solely filtering add a test for sourcing an aws_ec2_spot_price data source using filter{} blocks exclusively. this commit also involves a rework of the DescribeSpotPriceHistory AWS API call, namely using the native SDK paging function; without this function, making a singular direct call to the DescribeSpotPriceHistory SDK function returned a response with an empty slice, but the underlying API call returned a response with a non-nil NextToken value. to support this, we simply use the DescribeSpotPriceHistoryPages unconditionally during reads, and add all found SpotPriceHistory objects to a slice. * update documentation following resource rename * copypasta documention updates
- Loading branch information
Showing
5 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ec2" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
func dataSourceAwsEc2SpotPrice() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceAwsEc2SpotPriceRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"filter": dataSourceFiltersSchema(), | ||
"instance_type": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"availability_zone": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
"spot_price": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"spot_price_timestamp": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceAwsEc2SpotPriceRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).ec2conn | ||
|
||
now := time.Now() | ||
input := &ec2.DescribeSpotPriceHistoryInput{ | ||
StartTime: &now, | ||
} | ||
|
||
if v, ok := d.GetOk("instance_type"); ok { | ||
instanceType := v.(string) | ||
input.InstanceTypes = []*string{ | ||
aws.String(instanceType), | ||
} | ||
} | ||
|
||
if v, ok := d.GetOk("availability_zone"); ok { | ||
availabilityZone := v.(string) | ||
input.AvailabilityZone = aws.String(availabilityZone) | ||
} | ||
|
||
if v, ok := d.GetOk("filter"); ok { | ||
input.Filters = buildAwsDataSourceFilters(v.(*schema.Set)) | ||
} | ||
|
||
var foundSpotPrice []*ec2.SpotPrice | ||
|
||
err := conn.DescribeSpotPriceHistoryPages(input, func(output *ec2.DescribeSpotPriceHistoryOutput, lastPage bool) bool { | ||
foundSpotPrice = append(foundSpotPrice, output.SpotPriceHistory...) | ||
return true | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("error reading EC2 Spot Price History: %w", err) | ||
} | ||
|
||
if len(foundSpotPrice) == 0 { | ||
return fmt.Errorf("no EC2 Spot Price History found matching criteria; try different search") | ||
} | ||
|
||
if len(foundSpotPrice) > 1 { | ||
return fmt.Errorf("multiple EC2 Spot Price History results found matching criteria; try different search") | ||
} | ||
|
||
resultSpotPrice := foundSpotPrice[0] | ||
|
||
d.Set("spot_price", resultSpotPrice.SpotPrice) | ||
d.Set("spot_price_timestamp", (*resultSpotPrice.Timestamp).Format(time.RFC3339)) | ||
d.SetId(resource.UniqueId()) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ec2" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
) | ||
|
||
func TestAccAwsEc2SpotPriceDataSource(t *testing.T) { | ||
dataSourceName := "data.aws_ec2_spot_price.test" | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAwsEc2SpotPrice(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: nil, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAwsEc2SpotPriceDataSourceConfig(), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestMatchResourceAttr(dataSourceName, "spot_price", regexp.MustCompile(`^\d+\.\d+$`)), | ||
resource.TestMatchResourceAttr(dataSourceName, "spot_price_timestamp", regexp.MustCompile(rfc3339RegexPattern)), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccAwsEc2SpotPriceDataSourceFilter(t *testing.T) { | ||
dataSourceName := "data.aws_ec2_spot_price.test" | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t); testAccPreCheckAwsEc2SpotPrice(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: nil, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAwsEc2SpotPriceDataSourceFilterConfig(), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestMatchResourceAttr(dataSourceName, "spot_price", regexp.MustCompile(`^\d+\.\d+$`)), | ||
resource.TestMatchResourceAttr(dataSourceName, "spot_price_timestamp", regexp.MustCompile(rfc3339RegexPattern)), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccPreCheckAwsEc2SpotPrice(t *testing.T) { | ||
conn := testAccProvider.Meta().(*AWSClient).ec2conn | ||
|
||
input := &ec2.DescribeSpotPriceHistoryInput{ | ||
MaxResults: aws.Int64(5), | ||
} | ||
|
||
_, err := conn.DescribeSpotPriceHistory(input) | ||
|
||
if testAccPreCheckSkipError(err) { | ||
t.Skipf("skipping acceptance testing: %s", err) | ||
} | ||
|
||
if err != nil { | ||
t.Fatalf("unexpected PreCheck error: %s", err) | ||
} | ||
} | ||
|
||
func testAccAwsEc2SpotPriceDataSourceConfig() string { | ||
return fmt.Sprintf(` | ||
data "aws_region" "current" {} | ||
data "aws_ec2_instance_type_offering" "test" { | ||
filter { | ||
name = "instance-type" | ||
values = ["m5.xlarge"] | ||
} | ||
} | ||
data "aws_availability_zones" "available" { | ||
state = "available" | ||
filter { | ||
name = "region-name" | ||
values = [data.aws_region.current.name] | ||
} | ||
} | ||
data "aws_ec2_spot_price" "test" { | ||
instance_type = data.aws_ec2_instance_type_offering.test.instance_type | ||
availability_zone = data.aws_availability_zones.available.names[0] | ||
filter { | ||
name = "product-description" | ||
values = ["Linux/UNIX"] | ||
} | ||
} | ||
`) | ||
} | ||
|
||
func testAccAwsEc2SpotPriceDataSourceFilterConfig() string { | ||
return fmt.Sprintf(` | ||
data "aws_region" "current" {} | ||
data "aws_ec2_instance_type_offering" "test" { | ||
filter { | ||
name = "instance-type" | ||
values = ["m5.xlarge"] | ||
} | ||
} | ||
data "aws_availability_zones" "available" { | ||
state = "available" | ||
filter { | ||
name = "region-name" | ||
values = [data.aws_region.current.name] | ||
} | ||
} | ||
data "aws_ec2_spot_price" "test" { | ||
filter { | ||
name = "product-description" | ||
values = ["Linux/UNIX"] | ||
} | ||
filter { | ||
name = "instance-type" | ||
values = [data.aws_ec2_instance_type_offering.test.instance_type] | ||
} | ||
filter { | ||
name = "availability-zone" | ||
values = [data.aws_availability_zones.available.names[0]] | ||
} | ||
} | ||
`) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
subcategory: "EC2" | ||
layout: "aws" | ||
page_title: "AWS: aws_ec2_instance_spot_price" | ||
description: |- | ||
Information about most recent Spot Price for a given EC2 instance. | ||
--- | ||
|
||
# Data Source: aws_ec2_instance_spot_price | ||
|
||
Information about most recent Spot Price for a given EC2 instance. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
data "aws_ec2_instance_spot_price" "example" { | ||
instance_type = "t3.medium" | ||
availability_zone = "us-west-2a" | ||
filter { | ||
name = "product-description" | ||
values = ["Linux/UNIX"] | ||
} | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `instance_type` - (Optional) The type of instance for which to query Spot Price information. | ||
* `availability_zone` - (Optional) The availability zone in which to query Spot price information. | ||
* `filter` - (Optional) One or more configuration blocks containing name-values filters. See the [EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSpotPriceHistory.html) for supported filters. Detailed below. | ||
|
||
### filter Argument Reference | ||
|
||
* `name` - (Required) Name of the filter. | ||
* `values` - (Required) List of one or more values for the filter. | ||
|
||
## Attribute Reference | ||
|
||
In addition to all arguments above, the following attributes are exported: | ||
|
||
* `spot_price` - The most recent Spot Price value for the given instance type and AZ. | ||
* `spot_price_timestamp` - The timestamp at which the Spot Price value was published. |