Skip to content

Commit 24746cc

Browse files
authored
feat: Remove empty containers (map/list) from container definition (#336)
fix: Remove empty containers (map/list) from container definition
1 parent 53e7ac1 commit 24746cc

File tree

10 files changed

+113
-39
lines changed

10 files changed

+113
-39
lines changed

examples/container-definition/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@ Note that this example may create resources which will incur monetary charges on
3636
| Name | Source | Version |
3737
|------|--------|---------|
3838
| <a name="module_ecs_container_definition"></a> [ecs\_container\_definition](#module\_ecs\_container\_definition) | ../../modules/container-definition | n/a |
39+
| <a name="module_ecs_container_definition_simple"></a> [ecs\_container\_definition\_simple](#module\_ecs\_container\_definition\_simple) | ../../modules/container-definition | n/a |
3940

4041
## Resources
4142

4243
| Name | Type |
4344
|------|------|
4445
| [null_resource.container_definition_json](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
46+
| [null_resource.container_definition_json_simple](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
4547

4648
## Inputs
4749

@@ -57,6 +59,8 @@ Note that this example may create resources which will incur monetary charges on
5759
| <a name="output_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of CloudWatch log group created |
5860
| <a name="output_container_definition"></a> [container\_definition](#output\_container\_definition) | Container definition |
5961
| <a name="output_container_definition_json"></a> [container\_definition\_json](#output\_container\_definition\_json) | Container definition |
62+
| <a name="output_container_definition_json_simple"></a> [container\_definition\_json\_simple](#output\_container\_definition\_json\_simple) | Container definition |
63+
| <a name="output_container_definition_simple"></a> [container\_definition\_simple](#output\_container\_definition\_simple) | Container definition |
6064
<!-- END_TF_DOCS -->
6165

6266
## License
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"cpu": 256,
3+
"essential": true,
4+
"image": "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50",
5+
"interactive": false,
6+
"linuxParameters": {
7+
"initProcessEnabled": false
8+
},
9+
"logConfiguration": {
10+
"logDriver": "awslogs",
11+
"options": {
12+
"awslogs-group": "/aws/ecs",
13+
"awslogs-region": "eu-west-1",
14+
"awslogs-stream-prefix": "ecs"
15+
}
16+
},
17+
"memory": 512,
18+
"portMappings": [
19+
{
20+
"containerPort": 80,
21+
"hostPort": 80,
22+
"name": "app",
23+
"protocol": "tcp"
24+
}
25+
],
26+
"privileged": false,
27+
"pseudoTerminal": false,
28+
"readonlyRootFilesystem": false,
29+
"startTimeout": 30,
30+
"stopTimeout": 120,
31+
"versionConsistency": "disabled"
32+
}

examples/container-definition/main.tf

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,24 @@ module "ecs_container_definition" {
155155

156156
tags = local.tags
157157
}
158+
159+
module "ecs_container_definition_simple" {
160+
source = "../../modules/container-definition"
161+
162+
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
163+
cpu = 256
164+
memory = 512
165+
essential = true
166+
readonlyRootFilesystem = false
167+
portMappings = [{
168+
name = "app"
169+
protocol = "tcp"
170+
containerPort = 80
171+
hostPort = 80
172+
}]
173+
restartPolicy = {
174+
enabled = false
175+
}
176+
177+
tags = local.tags
178+
}

examples/container-definition/outputs.tf

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,29 @@ resource "null_resource" "container_definition_json" {
2525
}
2626
}
2727

28+
output "container_definition_simple" {
29+
description = "Container definition"
30+
value = module.ecs_container_definition.container_definition
31+
}
32+
33+
output "container_definition_json_simple" {
34+
description = "Container definition"
35+
value = module.ecs_container_definition.container_definition_json
36+
}
37+
38+
resource "null_resource" "container_definition_json_simple" {
39+
count = var.write_container_definition_to_file ? 1 : 0
40+
41+
triggers = {
42+
container_definition_json = timestamp()
43+
}
44+
45+
provisioner "local-exec" {
46+
# Need the output pretty-printed and sorted for comparison
47+
command = "echo '${module.ecs_container_definition_simple.container_definition_json}' | jq -S > ./definition_simple.json"
48+
}
49+
}
50+
2851
################################################################################
2952
# CloudWatch Log Group
3053
################################################################################

modules/container-definition/README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ No modules.
155155
| <a name="input_dockerSecurityOptions"></a> [dockerSecurityOptions](#input\_dockerSecurityOptions) | A list of strings to provide custom labels for SELinux and AppArmor multi-level security systems. This field isn't valid for containers in tasks using the Fargate launch type | `list(string)` | `null` | no |
156156
| <a name="input_enable_cloudwatch_logging"></a> [enable\_cloudwatch\_logging](#input\_enable\_cloudwatch\_logging) | Determines whether CloudWatch logging is configured for this container definition. Set to `false` to use other logging drivers | `bool` | `true` | no |
157157
| <a name="input_enable_execute_command"></a> [enable\_execute\_command](#input\_enable\_execute\_command) | Specifies whether to enable Amazon ECS Exec for the tasks within the service | `bool` | `false` | no |
158-
| <a name="input_entrypoint"></a> [entrypoint](#input\_entrypoint) | The entry point that is passed to the container | `list(string)` | `[]` | no |
159-
| <a name="input_environment"></a> [environment](#input\_environment) | The environment variables to pass to the container | <pre>list(object({<br/> name = string<br/> value = string<br/> }))</pre> | `[]` | no |
160-
| <a name="input_environmentFiles"></a> [environmentFiles](#input\_environmentFiles) | A list of files containing the environment variables to pass to a container | <pre>list(object({<br/> value = string<br/> type = string<br/> }))</pre> | `[]` | no |
158+
| <a name="input_entrypoint"></a> [entrypoint](#input\_entrypoint) | The entry point that is passed to the container | `list(string)` | `null` | no |
159+
| <a name="input_environment"></a> [environment](#input\_environment) | The environment variables to pass to the container | <pre>list(object({<br/> name = string<br/> value = string<br/> }))</pre> | `null` | no |
160+
| <a name="input_environmentFiles"></a> [environmentFiles](#input\_environmentFiles) | A list of files containing the environment variables to pass to a container | <pre>list(object({<br/> value = string<br/> type = string<br/> }))</pre> | `null` | no |
161161
| <a name="input_essential"></a> [essential](#input\_essential) | If the `essential` parameter of a container is marked as `true`, and that container fails or stops for any reason, all other containers that are part of the task are stopped | `bool` | `null` | no |
162162
| <a name="input_extraHosts"></a> [extraHosts](#input\_extraHosts) | A list of hostnames and IP address mappings to append to the `/etc/hosts` file on the container | <pre>list(object({<br/> hostname = string<br/> ipAddress = string<br/> }))</pre> | `null` | no |
163163
| <a name="input_firelensConfiguration"></a> [firelensConfiguration](#input\_firelensConfiguration) | The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more information, see [Custom Log Routing](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html) in the Amazon Elastic Container Service Developer Guide | <pre>object({<br/> options = optional(map(string))<br/> type = optional(string)<br/> })</pre> | `null` | no |
@@ -170,7 +170,7 @@ No modules.
170170
| <a name="input_logConfiguration"></a> [logConfiguration](#input\_logConfiguration) | The log configuration for the container. For more information see [LogConfiguration](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html) | <pre>object({<br/> logDriver = optional(string)<br/> options = optional(map(string))<br/> secretOptions = optional(list(object({<br/> name = string<br/> valueFrom = string<br/> })))<br/> })</pre> | `{}` | no |
171171
| <a name="input_memory"></a> [memory](#input\_memory) | The amount (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed. The total amount of memory reserved for all containers within a task must be lower than the task `memory` value, if one is specified | `number` | `null` | no |
172172
| <a name="input_memoryReservation"></a> [memoryReservation](#input\_memoryReservation) | The soft limit (in MiB) of memory to reserve for the container. When system memory is under heavy contention, Docker attempts to keep the container memory to this soft limit. However, your container can consume more memory when it needs to, up to either the hard limit specified with the `memory` parameter (if applicable), or all of the available memory on the container instance | `number` | `null` | no |
173-
| <a name="input_mountPoints"></a> [mountPoints](#input\_mountPoints) | The mount points for data volumes in your container | <pre>list(object({<br/> containerPath = optional(string)<br/> readOnly = optional(bool)<br/> sourceVolume = optional(string)<br/> }))</pre> | `[]` | no |
173+
| <a name="input_mountPoints"></a> [mountPoints](#input\_mountPoints) | The mount points for data volumes in your container | <pre>list(object({<br/> containerPath = optional(string)<br/> readOnly = optional(bool)<br/> sourceVolume = optional(string)<br/> }))</pre> | `null` | no |
174174
| <a name="input_name"></a> [name](#input\_name) | The name of a container. If you're linking multiple containers together in a task definition, the name of one container can be entered in the links of another container to connect the containers. Up to 255 letters (uppercase and lowercase), numbers, underscores, and hyphens are allowed | `string` | `null` | no |
175175
| <a name="input_operating_system_family"></a> [operating\_system\_family](#input\_operating\_system\_family) | The OS family for task | `string` | `"LINUX"` | no |
176176
| <a name="input_portMappings"></a> [portMappings](#input\_portMappings) | The list of port mappings for the container. Port mappings allow containers to access ports on the host container instance to send or receive traffic. For task definitions that use the awsvpc network mode, only specify the containerPort. The hostPort can be left blank or it must be the same value as the containerPort | <pre>list(object({<br/> appProtocol = optional(string)<br/> containerPort = optional(number)<br/> containerPortRange = optional(string)<br/> hostPort = optional(number)<br/> name = optional(string)<br/> protocol = optional(string)<br/> }))</pre> | `null` | no |
@@ -180,17 +180,17 @@ No modules.
180180
| <a name="input_region"></a> [region](#input\_region) | Region where the resource(s) will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no |
181181
| <a name="input_repositoryCredentials"></a> [repositoryCredentials](#input\_repositoryCredentials) | Container repository credentials; required when using a private repo. This map currently supports a single key; "credentialsParameter", which should be the ARN of a Secrets Manager's secret holding the credentials | <pre>object({<br/> credentialsParameter = optional(string)<br/> })</pre> | `null` | no |
182182
| <a name="input_resourceRequirements"></a> [resourceRequirements](#input\_resourceRequirements) | The type and amount of a resource to assign to a container. The only supported resource is a GPU | <pre>list(object({<br/> type = string<br/> value = string<br/> }))</pre> | `null` | no |
183-
| <a name="input_restartPolicy"></a> [restartPolicy](#input\_restartPolicy) | Container restart policy; helps overcome transient failures faster and maintain task availability | <pre>object({<br/> enabled = optional(bool)<br/> ignoredExitCodes = optional(list(number))<br/> restartAttemptPeriod = optional(number)<br/> })</pre> | <pre>{<br/> "enabled": true<br/>}</pre> | no |
183+
| <a name="input_restartPolicy"></a> [restartPolicy](#input\_restartPolicy) | Container restart policy; helps overcome transient failures faster and maintain task availability | <pre>object({<br/> enabled = optional(bool, true)<br/> ignoredExitCodes = optional(list(number))<br/> restartAttemptPeriod = optional(number)<br/> })</pre> | <pre>{<br/> "enabled": true<br/>}</pre> | no |
184184
| <a name="input_secrets"></a> [secrets](#input\_secrets) | The secrets to pass to the container. For more information, see [Specifying Sensitive Data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the Amazon Elastic Container Service Developer Guide | <pre>list(object({<br/> name = string<br/> valueFrom = string<br/> }))</pre> | `null` | no |
185185
| <a name="input_service"></a> [service](#input\_service) | The name of the service that the container definition is associated with. Used in CloudWatch log group default name (if one is not provided) | `string` | `null` | no |
186186
| <a name="input_startTimeout"></a> [startTimeout](#input\_startTimeout) | Time duration (in seconds) to wait before giving up on resolving dependencies for a container | `number` | `30` | no |
187187
| <a name="input_stopTimeout"></a> [stopTimeout](#input\_stopTimeout) | Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own | `number` | `120` | no |
188-
| <a name="input_systemControls"></a> [systemControls](#input\_systemControls) | A list of namespaced kernel parameters to set in the container | <pre>list(object({<br/> namespace = optional(string)<br/> value = optional(string)<br/> }))</pre> | `[]` | no |
188+
| <a name="input_systemControls"></a> [systemControls](#input\_systemControls) | A list of namespaced kernel parameters to set in the container | <pre>list(object({<br/> namespace = optional(string)<br/> value = optional(string)<br/> }))</pre> | `null` | no |
189189
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
190190
| <a name="input_ulimits"></a> [ulimits](#input\_ulimits) | A list of ulimits to set in the container. If a ulimit value is specified in a task definition, it overrides the default values set by Docker | <pre>list(object({<br/> hardLimit = number<br/> name = string<br/> softLimit = number<br/> }))</pre> | `null` | no |
191191
| <a name="input_user"></a> [user](#input\_user) | The user to run as inside the container. Can be any of these formats: user, user:group, uid, uid:gid, user:gid, uid:group. The default (null) will use the container's configured `USER` directive or root if not set | `string` | `null` | no |
192192
| <a name="input_versionConsistency"></a> [versionConsistency](#input\_versionConsistency) | Specifies whether Amazon ECS will resolve the container image tag provided in the container definition to an image digest | `string` | `"disabled"` | no |
193-
| <a name="input_volumesFrom"></a> [volumesFrom](#input\_volumesFrom) | Data volumes to mount from another container | <pre>list(object({<br/> readOnly = optional(bool)<br/> sourceContainer = optional(string)<br/> }))</pre> | `[]` | no |
193+
| <a name="input_volumesFrom"></a> [volumesFrom](#input\_volumesFrom) | Data volumes to mount from another container | <pre>list(object({<br/> readOnly = optional(bool)<br/> sourceContainer = optional(string)<br/> }))</pre> | `null` | no |
194194
| <a name="input_workingDirectory"></a> [workingDirectory](#input\_workingDirectory) | The working directory to run commands inside the container | `string` | `null` | no |
195195

196196
## Outputs

modules/container-definition/main.tf

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ locals {
3333
# tflint-ignore: terraform_naming_convention
3434
linuxParameters = var.enable_execute_command ? merge(local.trimedLinuxParameters, { "initProcessEnabled" : true }) : merge({ "initProcessEnabled" : false }, local.trimedLinuxParameters)
3535

36+
# tflint-ignore: terraform_naming_convention
37+
trimmedRestartPolicy = { for k, v in var.restartPolicy : k => v if v != null }
38+
3639
definition = {
3740
command = var.command
3841
cpu = var.cpu
@@ -42,9 +45,9 @@ locals {
4245
dnsServers = local.is_not_windows ? var.dnsServers : null
4346
dockerLabels = var.dockerLabels
4447
dockerSecurityOptions = var.dockerSecurityOptions
45-
entrypoint = var.entrypoint
46-
environment = var.environment
47-
environmentFiles = var.environmentFiles
48+
entrypoint = var.entrypoint != null ? var.entrypoint : null
49+
environment = var.environment != null ? var.environment : null
50+
environmentFiles = var.environmentFiles != null ? var.environmentFiles : null
4851
essential = var.essential
4952
extraHosts = local.is_not_windows ? var.extraHosts : null
5053
firelensConfiguration = var.firelensConfiguration != null ? { for k, v in var.firelensConfiguration : k => v if v != null } : null
@@ -57,23 +60,23 @@ locals {
5760
logConfiguration = length(local.logConfiguration) > 0 ? local.logConfiguration : null
5861
memory = var.memory
5962
memoryReservation = var.memoryReservation
60-
mountPoints = var.mountPoints
63+
mountPoints = var.mountPoints != null ? var.mountPoints : null
6164
name = var.name
6265
portMappings = var.portMappings != null ? [for p in var.portMappings : { for k, v in p : k => v if v != null }] : null
6366
privileged = local.is_not_windows ? var.privileged : null
6467
pseudoTerminal = var.pseudoTerminal
6568
readonlyRootFilesystem = local.is_not_windows ? var.readonlyRootFilesystem : null
6669
repositoryCredentials = var.repositoryCredentials
6770
resourceRequirements = var.resourceRequirements
68-
restartPolicy = { for k, v in var.restartPolicy : k => v if v != null }
71+
restartPolicy = local.trimmedRestartPolicy.enabled ? local.trimmedRestartPolicy : null
6972
secrets = var.secrets
7073
startTimeout = var.startTimeout
7174
stopTimeout = var.stopTimeout
72-
systemControls = var.systemControls
75+
systemControls = var.systemControls != null ? var.systemControls : null
7376
ulimits = local.is_not_windows ? var.ulimits : null
7477
user = local.is_not_windows ? var.user : null
7578
versionConsistency = var.versionConsistency
76-
volumesFrom = var.volumesFrom
79+
volumesFrom = var.volumesFrom != null ? var.volumesFrom : null
7780
workingDirectory = var.workingDirectory
7881
}
7982

modules/container-definition/variables.tf

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ variable "enable_execute_command" {
8989
variable "entrypoint" {
9090
description = "The entry point that is passed to the container"
9191
type = list(string)
92-
default = []
93-
nullable = false
92+
default = null
9493
}
9594

9695
variable "environment" {
@@ -99,8 +98,7 @@ variable "environment" {
9998
name = string
10099
value = string
101100
}))
102-
default = []
103-
nullable = false
101+
default = null
104102
}
105103

106104
# tflint-ignore: terraform_naming_convention
@@ -110,8 +108,7 @@ variable "environmentFiles" {
110108
value = string
111109
type = string
112110
}))
113-
default = []
114-
nullable = false
111+
default = null
115112
}
116113

117114
variable "essential" {
@@ -241,8 +238,7 @@ variable "mountPoints" {
241238
readOnly = optional(bool)
242239
sourceVolume = optional(string)
243240
}))
244-
default = []
245-
nullable = false
241+
default = null
246242
}
247243

248244
variable "name" {
@@ -311,13 +307,14 @@ variable "resourceRequirements" {
311307
variable "restartPolicy" {
312308
description = "Container restart policy; helps overcome transient failures faster and maintain task availability"
313309
type = object({
314-
enabled = optional(bool)
310+
enabled = optional(bool, true)
315311
ignoredExitCodes = optional(list(number))
316312
restartAttemptPeriod = optional(number)
317313
})
318314
default = {
319315
enabled = true
320316
}
317+
nullable = false
321318
}
322319

323320
variable "secrets" {
@@ -350,8 +347,7 @@ variable "systemControls" {
350347
namespace = optional(string)
351348
value = optional(string)
352349
}))
353-
default = []
354-
nullable = false
350+
default = null
355351
}
356352

357353
variable "ulimits" {
@@ -385,8 +381,7 @@ variable "volumesFrom" {
385381
readOnly = optional(bool)
386382
sourceContainer = optional(string)
387383
}))
388-
default = []
389-
nullable = false
384+
default = null
390385
}
391386

392387
# tflint-ignore: terraform_naming_convention

0 commit comments

Comments
 (0)