Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws_instance): add instance market options block #202

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3fae908
feat(aws_instance): add instance market options block
haidargit Aug 4, 2024
a091c71
Update variables.tf
haidargit Aug 16, 2024
5d80543
feat(aws_instance): add instance market options block
haidargit Aug 16, 2024
d3ef54e
feat(aws_instance): add instance market options block
haidargit Aug 17, 2024
83ed75a
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
34d6120
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
1592913
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
b8b4a51
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
d2d7d82
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
b3491a9
feat(aws_instance): add instance market options block
haidargit Aug 18, 2024
da7c2fc
Update outputs.tf
haidargit Aug 19, 2024
5063e1b
Update variables.tf
haidargit Aug 19, 2024
a3258fa
feat(aws_instance): add instance market options block
haidargit Aug 19, 2024
2f9f24b
feat(aws_instance): add instance market options block
haidargit Sep 1, 2024
ee7cfce
feat(aws_instance): add instance market options block
haidargit Sep 21, 2024
f946f21
feat(aws_instance): add instance market options block
haidargit Sep 21, 2024
bfcff37
feat(aws_instance): add instance market options block
haidargit Sep 21, 2024
385ef64
feat(aws_instance): add instance market options block
haidargit Sep 22, 2024
2e1572a
Update variables.tf
haidargit Sep 22, 2024
da21bfa
Update variables.tf
haidargit Sep 22, 2024
9927a06
Update outputs.tf
haidargit Sep 22, 2024
fae5cd4
Update outputs.tf
haidargit Sep 22, 2024
5b0f411
feat(aws_instance): add instance market options block
haidargit Oct 4, 2024
9291881
feat(aws_instance): add instance market options block
haidargit Oct 5, 2024
b9c4c70
feat(aws_instance): add instance market options block
haidargit Oct 5, 2024
87c6f06
feat(aws_instance): add instance market options block
haidargit Oct 5, 2024
1e608b5
feat(aws_instance): add instance market options block
haidargit Oct 5, 2024
1ccfbd0
feat(aws_instance): add instance market options block
haidargit Oct 5, 2024
8b67bbc
feat(aws_instance): add instance market options block
haidargit Oct 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ Available targets:
| <a name="input_force_detach_ebs"></a> [force\_detach\_ebs](#input\_force\_detach\_ebs) | force the volume/s to detach from the instance. | `bool` | `false` | no |
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br/>Set to `0` for unlimited length.<br/>Set to `null` for keep the existing setting, which defaults to `0`.<br/>Does not affect `id_full`. | `number` | `null` | no |
| <a name="input_instance_initiated_shutdown_behavior"></a> [instance\_initiated\_shutdown\_behavior](#input\_instance\_initiated\_shutdown\_behavior) | Specifies whether an instance stops or terminates when you initiate shutdown from the instance. Can be one of 'stop' or 'terminate'. | `string` | `null` | no |
| <a name="input_instance_market_options"></a> [instance\_market\_options](#input\_instance\_market\_options) | Describes the market (purchasing) option for the instances.<br/>See [docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#spot-options) for more information. | <pre>object({<br/> market_type = string<br/> spot_options = optional(object({<br/> instance_interruption_behavior = optional(string)<br/> max_price = optional(number)<br/> spot_instance_type = optional(string)<br/> valid_until = optional(string)<br/> }))<br/> })</pre> | `null` | no |
| <a name="input_instance_profile"></a> [instance\_profile](#input\_instance\_profile) | A pre-defined profile to attach to the instance (default is to build our own) | `string` | `""` | no |
| <a name="input_instance_profile_enabled"></a> [instance\_profile\_enabled](#input\_instance\_profile\_enabled) | Whether an IAM instance profile is created to pass a role to an Amazon EC2 instance when the instance starts | `bool` | `true` | no |
| <a name="input_instance_type"></a> [instance\_type](#input\_instance\_type) | The type of the instance | `string` | `"t2.micro"` | no |
Expand Down Expand Up @@ -312,6 +313,7 @@ Available targets:
| <a name="output_arn"></a> [arn](#output\_arn) | ARN of the instance |
| <a name="output_ebs_ids"></a> [ebs\_ids](#output\_ebs\_ids) | IDs of EBSs |
| <a name="output_id"></a> [id](#output\_id) | Disambiguated ID of the instance |
| <a name="output_instance_lifecycle"></a> [instance\_lifecycle](#output\_instance\_lifecycle) | Indicates whether this is a Spot Instance or a Scheduled Instance |
| <a name="output_instance_profile"></a> [instance\_profile](#output\_instance\_profile) | Name of the instance's profile (either built or supplied) |
| <a name="output_name"></a> [name](#output\_name) | Instance name |
| <a name="output_primary_network_interface_id"></a> [primary\_network\_interface\_id](#output\_primary\_network\_interface\_id) | ID of the instance's primary network interface |
Expand All @@ -325,6 +327,7 @@ Available targets:
| <a name="output_security_group_id"></a> [security\_group\_id](#output\_security\_group\_id) | EC2 instance Security Group ID |
| <a name="output_security_group_ids"></a> [security\_group\_ids](#output\_security\_group\_ids) | IDs on the AWS Security Groups associated with the instance |
| <a name="output_security_group_name"></a> [security\_group\_name](#output\_security\_group\_name) | EC2 instance Security Group name |
| <a name="output_spot_instance_request_id"></a> [spot\_instance\_request\_id](#output\_spot\_instance\_request\_id) | ID of the Spot Instance request |
| <a name="output_ssh_key_pair"></a> [ssh\_key\_pair](#output\_ssh\_key\_pair) | Name of the SSH key pair provisioned on the instance |
<!-- markdownlint-restore -->

Expand Down
36 changes: 20 additions & 16 deletions docs/terraform.md

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions examples/complete/fixtures.us-east-2.spot.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
enabled = true

region = "us-east-2"

namespace = "eg"

stage = "test"

name = "ec2-instance"

availability_zones = ["us-east-2a", "us-east-2b"]

assign_eip_address = false

associate_public_ip_address = true

instance_type = "t3.micro"

security_group_rules = [
{
type = "egress"
from_port = 0
to_port = 65535
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
},
{
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
type = "ingress"
from_port = 53
to_port = 53
protocol = "udp"
cidr_blocks = ["0.0.0.0/0"]
},
]

ssh_public_key_path = "/secrets"

metric_treat_missing_data = "notBreaching"

instance_market_options = {
market_type = "spot"
spot_options = {
instance_interruption_behavior = "terminate"
max_price = null
spot_instance_type = "one-time"
valid_until = null
}
}
1 change: 1 addition & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ module "ec2_instance" {
instance_profile = aws_iam_instance_profile.test.name
tenancy = var.tenancy
metric_treat_missing_data = var.metric_treat_missing_data
instance_market_options = var.instance_market_options

depends_on = [aws_iam_instance_profile.test]

Expand Down
10 changes: 10 additions & 0 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,13 @@ output "security_group_name" {
value = module.ec2_instance.security_group_name
description = "EC2 instance Security Group name"
}

output "instance_lifecycle" {
value = try(one(module.ec2_instance[*].instance_lifecycle), null)
description = "Indicates whether this is a Spot Instance or a Scheduled Instance"
}

output "spot_instance_request_id" {
value = try(one(module.ec2_instance[*].spot_instance_request_id), null)
description = "ID of the Spot Instance request"
}
23 changes: 22 additions & 1 deletion examples/complete/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,25 @@ variable "metric_treat_missing_data" {
condition = contains(["missing", "ignore", "breaching", "notBreaching"], var.metric_treat_missing_data)
error_message = "The value of metric_treat_missing_data must be one of the following: \"missing\", \"ignore\", \"breaching\", and \"notBreaching\"."
}
}
}

variable "instance_market_options" {
type = object({
market_type = string
spot_options = optional(object({
instance_interruption_behavior = optional(string)
max_price = optional(number)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
})
description = <<-EOT
Describes the market (purchasing) option for the instances.
See [docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#spot-options) for more information.
EOT
default = null
validation {
condition = var.instance_market_options != null ? contains(["spot", "capacity-block"], var.instance_market_options.market_type) : true
error_message = "The value of market_type must be one of the following: \"spot\" and \"capacity-block\"."
}
}
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.0"
version = ">= 4.7.0"
}
null = {
source = "hashicorp/null"
Expand Down
2 changes: 1 addition & 1 deletion examples/external-eni/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.0"
version = ">= 4.7.0"
}
null = {
source = "hashicorp/null"
Expand Down
18 changes: 18 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,24 @@ resource "aws_instance" "default" {
cpu_credits = var.burstable_mode
}

dynamic "instance_market_options" {
for_each = var.instance_market_options != null ? [var.instance_market_options] : []
content {
market_type = lookup(instance_market_options.value, "market_type", null)

dynamic "spot_options" {
for_each = (instance_market_options.value.spot_options != null ?
[instance_market_options.value.spot_options] : [])
content {
instance_interruption_behavior = lookup(spot_options.value, "instance_interruption_behavior", null)
max_price = lookup(spot_options.value, "max_price", null)
spot_instance_type = lookup(spot_options.value, "spot_instance_type", null)
valid_until = lookup(spot_options.value, "valid_until", null)
}
}
}
}

tags = module.this.tags

volume_tags = var.volume_tags_enabled ? module.this.tags : {}
Expand Down
10 changes: 10 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,13 @@ output "security_group_name" {
value = module.security_group.name
description = "EC2 instance Security Group name"
}

output "instance_lifecycle" {
value = try(one(aws_instance.default[*].instance_lifecycle), null)
description = "Indicates whether this is a Spot Instance or a Scheduled Instance"
}

output "spot_instance_request_id" {
value = try(one(aws_instance.default[*].spot_instance_request_id), null)
description = "ID of the Spot Instance request"
}
77 changes: 77 additions & 0 deletions test/src/examples_complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,80 @@ func TestExternalEniComplete(t *testing.T) {
// Verify we're getting back the outputs we expect
assert.Contains(t, securityGroupARN, "arn:aws:ec2", "SG ID should contains substring 'arn:aws:ec2'")
}

func TestSpotInstanceComplete(t *testing.T) {
t.Parallel()

rand.Seed(time.Now().UnixNano())

randId := strconv.Itoa(rand.Intn(100000))
attributes := []string{randId}

terraformOptions := &terraform.Options{
// The path to where our Terraform code is located
TerraformDir: "../../examples/complete",
Upgrade: true,
// Variables to pass to our Terraform code using -var-file options
VarFiles: []string{"fixtures.us-east-2.spot.tfvars"},
Vars: map[string]interface{}{
"attributes": attributes,
},
}

// At the end of the test, run `terraform destroy` to clean up any resources that were created
defer terraform.Destroy(t, terraformOptions)

// This will run `terraform init` and `terraform apply` and fail the test if there are any errors
terraform.InitAndApply(t, terraformOptions)

// Run `terraform output` to get the value of an output variable
vpcCidr := terraform.Output(t, terraformOptions, "vpc_cidr")
// Verify we're getting back the outputs we expect
assert.Equal(t, "172.16.0.0/16", vpcCidr)

// Run `terraform output` to get the value of an output variable
privateSubnetCidrs := terraform.OutputList(t, terraformOptions, "private_subnet_cidrs")
// Verify we're getting back the outputs we expect
assert.Equal(t, []string{"172.16.0.0/19", "172.16.32.0/19"}, privateSubnetCidrs)

// Run `terraform output` to get the value of an output variable
publicSubnetCidrs := terraform.OutputList(t, terraformOptions, "public_subnet_cidrs")
// Verify we're getting back the outputs we expect
assert.Equal(t, []string{"172.16.96.0/19", "172.16.128.0/19"}, publicSubnetCidrs)

// Run `terraform output` to get the value of an output variable
keyName := terraform.Output(t, terraformOptions, "key_name")
// Verify we're getting back the outputs we expect
assert.Equal(t, "eg-test-ec2-instance-"+randId, keyName)

// Run `terraform output` to get the value of an output variable
publicDns := terraform.Output(t, terraformOptions, "public_dns")
// Verify we're getting back the outputs we expect
assert.Contains(t, publicDns, ".us-east-2.compute.amazonaws.com")

// Run `terraform output` to get the value of an output variable
role := terraform.Output(t, terraformOptions, "role")
// Verify we're getting back the outputs we expect
assert.Equal(t, "eg-test-ec2-instance-"+randId+"-profile", role)

// Run `terraform output` to get the value of an output variable
securityGroupName := terraform.Output(t, terraformOptions, "security_group_name")
expectedSecurityGroupName := "eg-test-ec2-instance-" + randId
// Verify we're getting back the outputs we expect
assert.Equal(t, expectedSecurityGroupName, securityGroupName)

// Run `terraform output` to get the value of an output variable
securityGroupID := terraform.Output(t, terraformOptions, "security_group_id")
// Verify we're getting back the outputs we expect
assert.Contains(t, securityGroupID, "sg-", "SG ID should contains substring 'sg-'")

// Run `terraform output` to get the value of an output variable
securityGroupARN := terraform.Output(t, terraformOptions, "security_group_arn")
// Verify we're getting back the outputs we expect
assert.Contains(t, securityGroupARN, "arn:aws:ec2", "SG ID should contains substring 'arn:aws:ec2'")

// Run `terraform output` to get the value of an output variable
spotInstanceRequestID := terraform.Output(t, terraformOptions, "spot_instance_request_id")
// Verify we're getting back the outputs we expect
assert.Contains(t, spotInstanceRequestID, "sir-", "Spot instance request ID should contains substring 'sir-'")
}
21 changes: 21 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ variable "burstable_mode" {
default = null
}

variable "instance_market_options" {
type = object({
market_type = string
spot_options = optional(object({
instance_interruption_behavior = optional(string)
max_price = optional(number)
spot_instance_type = optional(string)
valid_until = optional(string)
}))
})
description = <<-EOT
Describes the market (purchasing) option for the instances.
See [docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#spot-options) for more information.
EOT
default = null
validation {
condition = var.instance_market_options != null ? contains(["spot", "capacity-block"], var.instance_market_options.market_type) : true
error_message = "The value of market_type must be one of the following: \"spot\" and \"capacity-block\"."
}
}

variable "vpc_id" {
type = string
description = "The ID of the VPC that the instance security group belongs to"
Expand Down