Skip to content

Commit 9e0307e

Browse files
goruhacloudpossebotgithub-actions[bot]aknyshobataku
authored
fix: strengthen types, simplify logic (#154) (#164)
* fix: strengthen types, simplify logic (#154) * fix: strengthen types, simplify logic * enable & use optional attrs * strengthen types for log_configuration, repository_credentials, system_controls, container_definition * rm redundant lookups * simplify secret & environment var sorting * Auto Format * fix: address missing optional; update examples/complete * Auto Format --------- Co-authored-by: cloudpossebot <[email protected]> Co-authored-by: Igor Rodionov <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andriy Knysh <[email protected]> Co-authored-by: obataku <[email protected]> Co-authored-by: cloudpossebot <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andriy Knysh <[email protected]>
1 parent 720c6b7 commit 9e0307e

File tree

11 files changed

+1773
-441
lines changed

11 files changed

+1773
-441
lines changed

README.md

Lines changed: 18 additions & 17 deletions
Large diffs are not rendered by default.

docs/terraform.md

Lines changed: 18 additions & 17 deletions
Large diffs are not rendered by default.

examples/complete/fixtures.us-east-2.tfvars

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,18 @@ port_mappings = [
4949
log_configuration = {
5050
logDriver = "json-file"
5151
options = {
52-
"max-size" = "10m"
53-
"max-file" = "3"
52+
max-size = "10m"
53+
max-file = "3"
5454
}
5555
secretOptions = null
5656
}
5757

5858
privileged = false
5959

60-
extra_hosts = [{
61-
ipAddress = "127.0.0.1"
62-
hostname = "app.local"
60+
extra_hosts = [
61+
{
62+
ipAddress = "127.0.0.1"
63+
hostname = "app.local"
6364
},
6465
]
6566

examples/complete/variables.tf

Lines changed: 13 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,14 @@ variable "container_memory_reservation" {
2424
default = null
2525
}
2626

27-
variable "container_definition" {
28-
type = map(any)
29-
description = "Container definition overrides which allows for extra keys or overriding existing keys."
30-
default = {}
31-
}
32-
27+
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_PortMapping.html
3328
variable "port_mappings" {
3429
type = list(object({
3530
containerPort = number
36-
hostPort = number
37-
protocol = string
31+
hostPort = optional(number)
32+
protocol = optional(string)
3833
}))
39-
4034
description = "The port mappings to configure for the container. This is a list of maps. Each map should contain \"containerPort\", \"hostPort\", and \"protocol\", where \"protocol\" is one of \"tcp\" or \"udp\". If using containers in a task with the awsvpc or host network mode, the hostPort can either be left blank or set to the same value as the containerPort"
41-
42-
default = []
43-
}
44-
45-
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_HealthCheck.html
46-
variable "healthcheck" {
47-
type = object({
48-
command = list(string)
49-
retries = number
50-
timeout = number
51-
interval = number
52-
startPeriod = number
53-
})
54-
description = "A map containing command (string), timeout, interval (duration in seconds), retries (1-10, number of times to retry before marking container unhealthy), and startPeriod (0-300, optional grace period to wait, in seconds, before failed healthchecks count toward retries)"
5535
default = null
5636
}
5737

@@ -91,13 +71,14 @@ variable "container_environment" {
9171
value = string
9272
}))
9373
description = "The environment variables to pass to the container. This is a list of maps. map_environment overrides environment"
94-
default = []
74+
default = null
9575
}
9676

77+
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_HostEntry.html
9778
variable "extra_hosts" {
9879
type = list(object({
99-
ipAddress = string
10080
hostname = string
81+
ipAddress = string
10182
}))
10283
description = "A list of hostnames and IP address mappings to append to the /etc/hosts file on the container. This is a list of maps"
10384
default = null
@@ -109,154 +90,23 @@ variable "map_environment" {
10990
default = null
11091
}
11192

112-
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_EnvironmentFile.html
113-
variable "environment_files" {
114-
type = list(object({
115-
value = string
116-
type = string
117-
}))
118-
description = "One or more files containing the environment variables to pass to the container. This maps to the --env-file option to docker run. The file must be hosted in Amazon S3. This option is only available to tasks using the EC2 launch type. This is a list of maps"
119-
default = null
120-
}
121-
122-
variable "secrets" {
123-
type = list(object({
124-
name = string
125-
valueFrom = string
126-
}))
127-
description = "The secrets to pass to the container. This is a list of maps"
128-
default = null
129-
}
130-
13193
variable "readonly_root_filesystem" {
13294
type = bool
13395
description = "Determines whether a container is given read-only access to its root filesystem. Due to how Terraform type casts booleans in json it is required to double quote this value"
13496
default = false
13597
}
13698

137-
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LinuxParameters.html
138-
variable "linux_parameters" {
139-
type = object({
140-
capabilities = object({
141-
add = list(string)
142-
drop = list(string)
143-
})
144-
devices = list(object({
145-
containerPath = string
146-
hostPath = string
147-
permissions = list(string)
148-
}))
149-
initProcessEnabled = bool
150-
maxSwap = number
151-
sharedMemorySize = number
152-
swappiness = number
153-
tmpfs = list(object({
154-
containerPath = string
155-
mountOptions = list(string)
156-
size = number
157-
}))
158-
})
159-
description = "Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more details, see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LinuxParameters.html"
160-
default = null
161-
}
162-
16399
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html
164100
variable "log_configuration" {
165-
type = any
166-
description = "Log configuration options to send to a custom log driver for the container. For more details, see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html"
167-
default = null
168-
}
169-
170-
# https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_FirelensConfiguration.html
171-
variable "firelens_configuration" {
172101
type = object({
173-
type = string
174-
options = map(string)
102+
logDriver = string
103+
options = optional(map(string))
104+
secretOptions = optional(list(object({
105+
name = string
106+
valueFrom = string
107+
})))
175108
})
176-
description = "The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more details, see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_FirelensConfiguration.html"
177-
default = null
178-
}
179-
180-
variable "mount_points" {
181-
type = list(any)
182-
183-
description = "Container mount points. This is a list of maps, where each map should contain a `containerPath` and `sourceVolume`. The `readOnly` key is optional."
184-
default = []
185-
}
186-
187-
variable "dns_servers" {
188-
type = list(string)
189-
description = "Container DNS servers. This is a list of strings specifying the IP addresses of the DNS servers"
190-
default = null
191-
}
192-
193-
variable "dns_search_domains" {
194-
type = list(string)
195-
description = "Container DNS search domains. A list of DNS search domains that are presented to the container"
196-
default = null
197-
}
198-
199-
variable "ulimits" {
200-
type = list(object({
201-
name = string
202-
hardLimit = number
203-
softLimit = number
204-
}))
205-
description = "Container ulimit settings. This is a list of maps, where each map should contain \"name\", \"hardLimit\" and \"softLimit\""
206-
default = null
207-
}
208-
209-
variable "repository_credentials" {
210-
type = map(string)
211-
description = "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"
212-
default = null
213-
}
214-
215-
variable "volumes_from" {
216-
type = list(object({
217-
sourceContainer = string
218-
readOnly = bool
219-
}))
220-
description = "A list of VolumesFrom maps which contain \"sourceContainer\" (name of the container that has the volumes to mount) and \"readOnly\" (whether the container can write to the volume)"
221-
default = []
222-
}
223-
224-
variable "links" {
225-
type = list(string)
226-
description = "List of container names this container can communicate with without port mappings"
227-
default = null
228-
}
229-
230-
variable "user" {
231-
type = string
232-
description = "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."
233-
default = null
234-
}
235-
236-
variable "container_depends_on" {
237-
type = list(object({
238-
containerName = string
239-
condition = string
240-
}))
241-
description = "The dependencies defined for container startup and shutdown. A container can contain multiple dependencies. When a dependency is defined for container startup, for container shutdown it is reversed. The condition can be one of START, COMPLETE, SUCCESS or HEALTHY"
242-
default = null
243-
}
244-
245-
variable "docker_labels" {
246-
type = map(string)
247-
description = "The configuration options to send to the `docker_labels`"
248-
default = null
249-
}
250-
251-
variable "start_timeout" {
252-
type = number
253-
description = "Time duration (in seconds) to wait before giving up on resolving dependencies for a container"
254-
default = null
255-
}
256-
257-
variable "stop_timeout" {
258-
type = number
259-
description = "Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own"
109+
description = "Log configuration options to send to a custom log driver for the container. For more details, see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html"
260110
default = null
261111
}
262112

@@ -266,12 +116,6 @@ variable "privileged" {
266116
default = null
267117
}
268118

269-
variable "system_controls" {
270-
type = list(map(string))
271-
description = "A list of namespaced kernel parameters to set in the container, mapping to the --sysctl option to docker run. This is a list of maps: { namespace = \"\", value = \"\"}"
272-
default = null
273-
}
274-
275119
variable "hostname" {
276120
type = string
277121
description = "The hostname to use for your container."

examples/complete/versions.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
terraform {
2-
required_version = ">= 0.13.0"
2+
required_version = ">= 1.3.0"
33

44
required_providers {
55
local = {

main.tf

Lines changed: 26 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,26 @@
11
locals {
2-
# Sort environment variables so terraform will not try to recreate on each plan/apply
3-
env_vars_keys = var.map_environment != null ? keys(var.map_environment) : var.environment != null ? [for m in var.environment : lookup(m, "name")] : []
4-
env_vars_values = var.map_environment != null ? values(var.map_environment) : var.environment != null ? [for m in var.environment : lookup(m, "value")] : []
5-
env_vars_as_map = zipmap(local.env_vars_keys, local.env_vars_values)
6-
sorted_env_vars_keys = sort(local.env_vars_keys)
2+
# Sort environment variables & secrets so terraform will not try to recreate on each plan/apply
3+
env_as_map = var.map_environment != null ? var.map_environment : var.environment != null ? { for m in var.environment : m.name => m.value } : null
4+
secrets_as_map = var.map_secrets != null ? var.map_secrets : var.secrets != null ? { for m in var.secrets : m.name => m.valueFrom } : null
75

8-
sorted_environment_vars = [
9-
for key in local.sorted_env_vars_keys :
6+
# https://www.terraform.io/docs/configuration/expressions.html#null
7+
final_environment_vars = local.env_as_map != null ? [
8+
for k, v in local.env_as_map :
109
{
11-
name = key
12-
value = lookup(local.env_vars_as_map, key)
10+
name = k
11+
value = v
1312
}
14-
]
15-
16-
# Sort secrets so terraform will not try to recreate on each plan/apply
17-
secrets_keys = var.map_secrets != null ? keys(var.map_secrets) : var.secrets != null ? [for m in var.secrets : lookup(m, "name")] : []
18-
secrets_values = var.map_secrets != null ? values(var.map_secrets) : var.secrets != null ? [for m in var.secrets : lookup(m, "valueFrom")] : []
19-
secrets_as_map = zipmap(local.secrets_keys, local.secrets_values)
20-
sorted_secrets_keys = sort(local.secrets_keys)
21-
22-
sorted_secrets_vars = [
23-
for key in local.sorted_secrets_keys :
13+
] : null
14+
final_secrets_vars = local.secrets_as_map != null ? [
15+
for k, v in local.secrets_as_map :
2416
{
25-
name = key
26-
valueFrom = lookup(local.secrets_as_map, key)
17+
name = k
18+
valueFrom = v
2719
}
28-
]
29-
30-
mount_points = length(var.mount_points) > 0 ? [
31-
for mount_point in var.mount_points : {
32-
containerPath = lookup(mount_point, "containerPath")
33-
sourceVolume = lookup(mount_point, "sourceVolume")
34-
readOnly = tobool(lookup(mount_point, "readOnly", false))
35-
}
36-
] : var.mount_points
37-
38-
# https://www.terraform.io/docs/configuration/expressions.html#null
39-
final_environment_vars = length(local.sorted_environment_vars) > 0 ? local.sorted_environment_vars : []
40-
final_secrets_vars = length(local.sorted_secrets_vars) > 0 ? local.sorted_secrets_vars : null
20+
] : null
4121

42-
log_configuration_secret_options = var.log_configuration != null ? lookup(var.log_configuration, "secretOptions", null) : null
43-
log_configuration_with_null = var.log_configuration == null ? null : {
44-
logDriver = tostring(lookup(var.log_configuration, "logDriver"))
45-
options = tomap(lookup(var.log_configuration, "options"))
46-
secretOptions = local.log_configuration_secret_options == null ? null : [
47-
for secret_option in tolist(local.log_configuration_secret_options) : {
48-
name = tostring(lookup(secret_option, "name"))
49-
valueFrom = tostring(lookup(secret_option, "valueFrom"))
50-
}
51-
]
52-
}
53-
log_configuration_without_null = local.log_configuration_with_null == null ? null : {
54-
for k, v in local.log_configuration_with_null :
22+
log_configuration_without_null = var.log_configuration == null ? null : {
23+
for k, v in var.log_configuration :
5524
k => v
5625
if v != null
5726
}
@@ -65,7 +34,7 @@ locals {
6534
command = var.command
6635
workingDirectory = var.working_directory
6736
readonlyRootFilesystem = var.readonly_root_filesystem
68-
mountPoints = local.mount_points
37+
mountPoints = var.mount_points
6938
dnsServers = var.dns_servers
7039
dnsSearchDomains = var.dns_search_domains
7140
ulimits = var.ulimits
@@ -104,5 +73,13 @@ locals {
10473
k => v
10574
if v != null
10675
}
107-
json_map = jsonencode(merge(local.container_definition_without_null, var.container_definition))
76+
77+
container_definition_override_without_null = {
78+
for k, v in var.container_definition :
79+
k => v
80+
if v != null
81+
}
82+
83+
final_container_definition = merge(local.container_definition_without_null, local.container_definition_override_without_null)
84+
json_map = jsonencode(local.final_container_definition)
10885
}

outputs.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ output "json_map_encoded" {
1010

1111
output "json_map_object" {
1212
description = "JSON map encoded container definition"
13-
value = jsondecode(local.json_map)
13+
value = local.final_container_definition
1414
}
1515

1616
output "sensitive_json_map_encoded_list" {
@@ -27,6 +27,6 @@ output "sensitive_json_map_encoded" {
2727

2828
output "sensitive_json_map_object" {
2929
description = "JSON map encoded container definition (sensitive)"
30-
value = jsondecode(local.json_map)
30+
value = local.final_container_definition
3131
sensitive = true
3232
}

test/src/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module github.com/cloudposse/terraform-aws-ecs-container-definition
22

3-
go 1.14
3+
go 1.15
44

55
require (
6-
github.com/gruntwork-io/terratest v0.30.23
7-
github.com/stretchr/testify v1.6.1
6+
github.com/gruntwork-io/terratest v0.43.0
7+
github.com/stretchr/testify v1.8.1
88
)

0 commit comments

Comments
 (0)