From ec4c4a662fe3f5d9d8cf594a41d31e10e13bd8f9 Mon Sep 17 00:00:00 2001 From: Yonatan Koren Date: Fri, 11 Jun 2021 12:31:28 -0400 Subject: [PATCH] Fix: Add Thorough Automated Testing and Bump Minimum Terraform Version to 0.13.0 (#139) * Ensure examples/complete is creating an aws_ecs_task_definition and test the task definition in the automated testing. * Fix examples/multiple_definitions * Bump minimum Terraform required version to 0.13.0 * Add context.tf to examples/complete to ensure aws_ecs_task_definition has tags. --- README.md | 14 +- README.yaml | 2 + docs/terraform.md | 2 +- examples/complete/context.tf | 202 ++++++++++++++++++++ examples/complete/fixtures.us-east-2.tfvars | 8 +- examples/complete/main.tf | 9 +- examples/complete/outputs.tf | 5 + examples/complete/variables.tf | 2 +- examples/complete/versions.tf | 2 +- examples/env_vars_files/versions.tf | 2 +- examples/map_environment/versions.tf | 2 +- examples/multi_port_mappings/versions.tf | 2 +- examples/multi_type_env_vars/versions.tf | 2 +- examples/multiple_definitions/main.tf | 4 +- examples/multiple_definitions/versions.tf | 2 +- examples/string_env_vars/versions.tf | 2 +- test/src/examples_complete_test.go | 5 + versions.tf | 2 +- 18 files changed, 248 insertions(+), 21 deletions(-) create mode 100644 examples/complete/context.tf diff --git a/README.md b/README.md index 36c7409..b121aea 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # terraform-aws-ecs-container-definition @@ -31,7 +32,6 @@ Terraform module to generate well-formed JSON documents that are passed to the `aws_ecs_task_definition` Terraform resource as [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions). - --- This project is part of our comprehensive ["SweetOps"](https://cpco.io/sweetops) approach towards DevOps. @@ -62,7 +62,6 @@ We literally have [*hundreds of terraform modules*][terraform_modules] that are - ## Security & Compliance [](https://bridgecrew.io/) Security scanning is graciously provided by Bridgecrew. Bridgecrew is the leading fully hosted, cloud-native solution providing continuous Terraform security and compliance. @@ -133,7 +132,7 @@ Available targets: | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.12.26 | +| [terraform](#requirement\_terraform) | >= 0.13.0 | | [local](#requirement\_local) | >= 1.2 | ## Providers @@ -216,6 +215,7 @@ Like this project? Please give it a ★ on [our GitHub](https://github.com/cloud Are you using this project or any of our other projects? Consider [leaving a testimonial][testimonial]. =) + ## Related Projects Check out these related projects. @@ -230,8 +230,6 @@ Check out these related projects. - [terraform-aws-ecs-cloudwatch-sns-alarms](https://github.com/cloudposse/terraform-aws-ecs-cloudwatch-sns-alarms) - Terraform module to create CloudWatch Alarms on ECS Service level metrics - [terraform-aws-ecs-alb-service-task](https://github.com/cloudposse/terraform-aws-ecs-alb-service-task) - Terraform module which implements an ECS service which exposes a web service via ALB - - ## Help **Got a question?** We got answers. @@ -361,8 +359,8 @@ Check out [our other projects][github], [follow us on twitter][twitter], [apply ### Contributors -| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Sarkis Varozian][sarkis_avatar]][sarkis_homepage]
[Sarkis Varozian][sarkis_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | -|---|---|---|---| +| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Sarkis Varozian][sarkis_avatar]][sarkis_homepage]
[Sarkis Varozian][sarkis_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | [![Yonatan Koren][korenyoni_avatar]][korenyoni_homepage]
[Yonatan Koren][korenyoni_homepage] | +|---|---|---|---|---| [osterman_homepage]: https://github.com/osterman @@ -373,6 +371,8 @@ Check out [our other projects][github], [follow us on twitter][twitter], [apply [aknysh_avatar]: https://img.cloudposse.com/150x150/https://github.com/aknysh.png [goruha_homepage]: https://github.com/goruha [goruha_avatar]: https://img.cloudposse.com/150x150/https://github.com/goruha.png + [korenyoni_homepage]: https://github.com/korenyoni + [korenyoni_avatar]: https://img.cloudposse.com/150x150/https://github.com/korenyoni.png [![README Footer][readme_footer_img]][readme_footer_link] [![Beacon][beacon]][website] diff --git a/README.yaml b/README.yaml index 713ac55..6571af5 100644 --- a/README.yaml +++ b/README.yaml @@ -70,3 +70,5 @@ contributors: github: aknysh - name: Igor Rodionov github: goruha +- name: Yonatan Koren + github: korenyoni diff --git a/docs/terraform.md b/docs/terraform.md index c7755aa..f5394d4 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -3,7 +3,7 @@ | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 0.12.26 | +| [terraform](#requirement\_terraform) | >= 0.13.0 | | [local](#requirement\_local) | >= 1.2 | ## Providers diff --git a/examples/complete/context.tf b/examples/complete/context.tf new file mode 100644 index 0000000..81f99b4 --- /dev/null +++ b/examples/complete/context.tf @@ -0,0 +1,202 @@ +# +# ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label +# All other instances of this file should be a copy of that one +# +# +# Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf +# and then place it in your Terraform module to automatically get +# Cloud Posse's standard configuration inputs suitable for passing +# to Cloud Posse modules. +# +# Modules should access the whole context as `module.this.context` +# to get the input variables with nulls for defaults, +# for example `context = module.this.context`, +# and access individual variables as `module.this.`, +# with final values filled in. +# +# For example, when using defaults, `module.this.context.delimiter` +# will be null, and `module.this.delimiter` will be `-` (hyphen). +# + +module "this" { + source = "cloudposse/label/null" + version = "0.24.1" # requires Terraform >= 0.13.0 + + enabled = var.enabled + namespace = var.namespace + environment = var.environment + stage = var.stage + name = var.name + delimiter = var.delimiter + attributes = var.attributes + tags = var.tags + additional_tag_map = var.additional_tag_map + label_order = var.label_order + regex_replace_chars = var.regex_replace_chars + id_length_limit = var.id_length_limit + label_key_case = var.label_key_case + label_value_case = var.label_value_case + + context = var.context +} + +# Copy contents of cloudposse/terraform-null-label/variables.tf here + +variable "context" { + type = any + default = { + enabled = true + namespace = null + environment = null + stage = null + name = null + delimiter = null + attributes = [] + tags = {} + additional_tag_map = {} + regex_replace_chars = null + label_order = [] + id_length_limit = null + label_key_case = null + label_value_case = null + } + description = <<-EOT + Single object for setting entire context at once. + See description of individual variables for details. + Leave string and numeric variables as `null` to use default value. + Individual variable settings (non-null) override settings in context object, + except for attributes, tags, and additional_tag_map, which are merged. + EOT + + validation { + condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`." + } + + validation { + condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} + +variable "enabled" { + type = bool + default = null + description = "Set to false to prevent the module from creating any resources" +} + +variable "namespace" { + type = string + default = null + description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" +} + +variable "environment" { + type = string + default = null + description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'" +} + +variable "stage" { + type = string + default = null + description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'" +} + +variable "name" { + type = string + default = null + description = "Solution name, e.g. 'app' or 'jenkins'" +} + +variable "delimiter" { + type = string + default = null + description = <<-EOT + Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`. + Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. + EOT +} + +variable "attributes" { + type = list(string) + default = [] + description = "Additional attributes (e.g. `1`)" +} + +variable "tags" { + type = map(string) + default = {} + description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`" +} + +variable "additional_tag_map" { + type = map(string) + default = {} + description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`." +} + +variable "label_order" { + type = list(string) + default = null + description = <<-EOT + The naming order of the id output and Name tag. + Defaults to ["namespace", "environment", "stage", "name", "attributes"]. + You can omit any of the 5 elements, but at least one must be present. + EOT +} + +variable "regex_replace_chars" { + type = string + default = null + description = <<-EOT + Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. + If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. + EOT +} + +variable "id_length_limit" { + type = number + default = null + description = <<-EOT + Limit `id` to this many characters (minimum 6). + Set to `0` for unlimited length. + Set to `null` for default, which is `0`. + Does not affect `id_full`. + EOT + validation { + condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 + error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." + } +} + +variable "label_key_case" { + type = string + default = null + description = <<-EOT + The letter case of label keys (`tag` names) (i.e. `name`, `namespace`, `environment`, `stage`, `attributes`) to use in `tags`. + Possible values: `lower`, `title`, `upper`. + Default value: `title`. + EOT + + validation { + condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) + error_message = "Allowed values: `lower`, `title`, `upper`." + } +} + +variable "label_value_case" { + type = string + default = null + description = <<-EOT + The letter case of output label values (also used in `tags` and `id`). + Possible values: `lower`, `title`, `upper` and `none` (no transformation). + Default value: `lower`. + EOT + + validation { + condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) + error_message = "Allowed values: `lower`, `title`, `upper`, `none`." + } +} +#### End of copy of cloudposse/terraform-null-label/variables.tf diff --git a/examples/complete/fixtures.us-east-2.tfvars b/examples/complete/fixtures.us-east-2.tfvars index 13240b8..eee59fe 100644 --- a/examples/complete/fixtures.us-east-2.tfvars +++ b/examples/complete/fixtures.us-east-2.tfvars @@ -1,5 +1,11 @@ region = "us-east-2" +namespace = "eg" + +stage = "test" + +name = "container-definition" + container_name = "app" container_image = "cloudposse/geodesic" container_memory = 256 @@ -8,7 +14,7 @@ container_cpu = 256 essential = true readonly_root_filesystem = false -environment = [ +container_environment = [ { name = "string_var" value = "I am a string" diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 21db43b..9740abe 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -11,7 +11,7 @@ module "container" { container_cpu = var.container_cpu essential = var.essential readonly_root_filesystem = var.readonly_root_filesystem - environment = var.environment + environment = var.container_environment port_mappings = var.port_mappings log_configuration = var.log_configuration privileged = var.privileged @@ -20,3 +20,10 @@ module "container" { pseudo_terminal = var.pseudo_terminal interactive = var.interactive } + +resource "aws_ecs_task_definition" "task" { + family = module.this.id + container_definitions = module.container.json_map_encoded_list + + tags = module.this.tags +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index 71d8003..3d27bbd 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -12,3 +12,8 @@ output "json_map_object" { description = "JSON map encoded container definition" value = module.container.json_map_object } + +output "task_definition_container_definition" { + description = "The aws_ecs_task_definition container definition" + value = jsondecode(aws_ecs_task_definition.task.container_definitions)[0] +} \ No newline at end of file diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index a884d7a..d9e4edb 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -85,7 +85,7 @@ variable "working_directory" { default = null } -variable "environment" { +variable "container_environment" { type = list(object({ name = string value = string diff --git a/examples/complete/versions.tf b/examples/complete/versions.tf index 91805f0..81ea840 100644 --- a/examples/complete/versions.tf +++ b/examples/complete/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/env_vars_files/versions.tf b/examples/env_vars_files/versions.tf index 86b1b25..0a440ee 100644 --- a/examples/env_vars_files/versions.tf +++ b/examples/env_vars_files/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/map_environment/versions.tf b/examples/map_environment/versions.tf index 86b1b25..0a440ee 100644 --- a/examples/map_environment/versions.tf +++ b/examples/map_environment/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/multi_port_mappings/versions.tf b/examples/multi_port_mappings/versions.tf index 86b1b25..0a440ee 100644 --- a/examples/multi_port_mappings/versions.tf +++ b/examples/multi_port_mappings/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/multi_type_env_vars/versions.tf b/examples/multi_type_env_vars/versions.tf index 86b1b25..0a440ee 100644 --- a/examples/multi_type_env_vars/versions.tf +++ b/examples/multi_type_env_vars/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/multiple_definitions/main.tf b/examples/multiple_definitions/main.tf index bd802c4..857fca8 100644 --- a/examples/multiple_definitions/main.tf +++ b/examples/multiple_definitions/main.tf @@ -49,7 +49,7 @@ output "second_container_json" { resource "aws_ecs_task_definition" "task" { family = "foo" container_definitions = jsonencode([ - module.first_container.json_map, - module.second_container.json_map + module.first_container.json_map_object, + module.second_container.json_map_object ]) } diff --git a/examples/multiple_definitions/versions.tf b/examples/multiple_definitions/versions.tf index 91805f0..81ea840 100644 --- a/examples/multiple_definitions/versions.tf +++ b/examples/multiple_definitions/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/examples/string_env_vars/versions.tf b/examples/string_env_vars/versions.tf index 86b1b25..0a440ee 100644 --- a/examples/string_env_vars/versions.tf +++ b/examples/string_env_vars/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = { diff --git a/test/src/examples_complete_test.go b/test/src/examples_complete_test.go index 9da2d6e..4c04007 100644 --- a/test/src/examples_complete_test.go +++ b/test/src/examples_complete_test.go @@ -41,4 +41,9 @@ func TestExamplesComplete(t *testing.T) { assert.Equal(t, 256, int((jsonObject["cpu"]).(float64))) assert.Equal(t, true, jsonObject["essential"]) assert.Equal(t, false, jsonObject["readonlyRootFilesystem"]) + + // Run `terraform output` to compare the expected container definition with the actual aws_ecs_task_definition container definition + containerDefinitionExpected := terraform.OutputRequired(t, terraformOptions, "json_map_object") + containerDefinition := terraform.OutputRequired(t, terraformOptions, "task_definition_container_definition") + assert.Equal(t, containerDefinitionExpected, containerDefinition) } diff --git a/versions.tf b/versions.tf index 86b1b25..0a440ee 100644 --- a/versions.tf +++ b/versions.tf @@ -1,5 +1,5 @@ terraform { - required_version = ">= 0.12.26" + required_version = ">= 0.13.0" required_providers { local = {