147 lines
7.2 KiB
HCL
Executable File

locals {
defaults = {
label_order = ["namespace", "environment", "stage", "name", "attributes"]
regex_replace_chars = "/[^-a-zA-Z0-9]/"
delimiter = "-"
replacement = ""
id_length_limit = 0
id_hash_length = 5
label_key_case = "title"
label_value_case = "lower"
}
# So far, we have decided not to allow overriding replacement or id_hash_length
replacement = local.defaults.replacement
id_hash_length = local.defaults.id_hash_length
# The values provided by variables supersede the values inherited from the context object,
# except for tags and attributes which are merged.
input = {
# It would be nice to use coalesce here, but we cannot, because it
# is an error for all the arguments to coalesce to be empty.
enabled = var.enabled == null ? var.context.enabled : var.enabled
namespace = var.namespace == null ? var.context.namespace : var.namespace
environment = var.environment == null ? var.context.environment : var.environment
stage = var.stage == null ? var.context.stage : var.stage
name = var.name == null ? var.context.name : var.name
delimiter = var.delimiter == null ? var.context.delimiter : var.delimiter
# modules tack on attributes (passed by var) to the end of the list (passed by context)
attributes = compact(distinct(concat(coalesce(var.context.attributes, []), coalesce(var.attributes, []))))
tags = merge(var.context.tags, var.tags)
additional_tag_map = merge(var.context.additional_tag_map, var.additional_tag_map)
label_order = var.label_order == null ? var.context.label_order : var.label_order
regex_replace_chars = var.regex_replace_chars == null ? var.context.regex_replace_chars : var.regex_replace_chars
id_length_limit = var.id_length_limit == null ? var.context.id_length_limit : var.id_length_limit
label_key_case = var.label_key_case == null ? lookup(var.context, "label_key_case", null) : var.label_key_case
label_value_case = var.label_value_case == null ? lookup(var.context, "label_value_case", null) : var.label_value_case
}
enabled = local.input.enabled
regex_replace_chars = coalesce(local.input.regex_replace_chars, local.defaults.regex_replace_chars)
# string_label_names are names of inputs that are strings (not list of strings) used as labels
string_label_names = ["name", "namespace", "environment", "stage"]
normalized_labels = { for k in local.string_label_names : k =>
local.input[k] == null ? "" : replace(local.input[k], local.regex_replace_chars, local.replacement)
}
normalized_attributes = compact(distinct([for v in local.input.attributes : replace(v, local.regex_replace_chars, local.replacement)]))
formatted_labels = { for k in local.string_label_names : k => local.label_value_case == "none" ? local.normalized_labels[k] :
local.label_value_case == "title" ? title(lower(local.normalized_labels[k])) :
local.label_value_case == "upper" ? upper(local.normalized_labels[k]) : lower(local.normalized_labels[k])
}
attributes = compact(distinct([
for v in local.normalized_attributes : (local.label_value_case == "none" ? v :
local.label_value_case == "title" ? title(lower(v)) :
local.label_value_case == "upper" ? upper(v) : lower(v))
]))
name = local.formatted_labels["name"]
namespace = local.formatted_labels["namespace"]
environment = local.formatted_labels["environment"]
stage = local.formatted_labels["stage"]
delimiter = local.input.delimiter == null ? local.defaults.delimiter : local.input.delimiter
label_order = local.input.label_order == null ? local.defaults.label_order : coalescelist(local.input.label_order, local.defaults.label_order)
id_length_limit = local.input.id_length_limit == null ? local.defaults.id_length_limit : local.input.id_length_limit
label_key_case = local.input.label_key_case == null ? local.defaults.label_key_case : local.input.label_key_case
label_value_case = local.input.label_value_case == null ? local.defaults.label_value_case : local.input.label_value_case
additional_tag_map = merge(var.context.additional_tag_map, var.additional_tag_map)
tags = merge(local.generated_tags, local.input.tags)
tags_as_list_of_maps = flatten([
for key in keys(local.tags) : merge(
{
key = key
value = local.tags[key]
}, var.additional_tag_map)
])
tags_context = {
# For AWS we need `Name` to be disambiguated since it has a special meaning
name = local.id
namespace = local.namespace
environment = local.environment
stage = local.stage
attributes = local.id_context.attributes
}
generated_tags = {
for l in keys(local.tags_context) :
local.label_key_case == "upper" ? upper(l) : (
local.label_key_case == "lower" ? lower(l) : title(lower(l))
) => local.tags_context[l] if length(local.tags_context[l]) > 0
}
id_context = {
name = local.name
namespace = local.namespace
environment = local.environment
stage = local.stage
attributes = join(local.delimiter, local.attributes)
}
labels = [for l in local.label_order : local.id_context[l] if length(local.id_context[l]) > 0]
id_full = join(local.delimiter, local.labels)
# Create a truncated ID if needed
delimiter_length = length(local.delimiter)
# Calculate length of normal part of ID, leaving room for delimiter and hash
id_truncated_length_limit = local.id_length_limit - (local.id_hash_length + local.delimiter_length)
# Truncate the ID and ensure a single (not double) trailing delimiter
id_truncated = local.id_truncated_length_limit <= 0 ? "" : "${trimsuffix(substr(local.id_full, 0, local.id_truncated_length_limit), local.delimiter)}${local.delimiter}"
# Support usages that disallow numeric characters. Would prefer tr 0-9 q-z but Terraform does not support it.
id_hash_plus = "${md5(local.id_full)}qrstuvwxyz"
id_hash_case = local.label_value_case == "title" ? title(local.id_hash_plus) : local.label_value_case == "upper" ? upper(local.id_hash_plus) : local.label_value_case == "lower" ? lower(local.id_hash_plus) : local.id_hash_plus
id_hash = replace(local.id_hash_case, local.regex_replace_chars, local.replacement)
# Create the short ID by adding a hash to the end of the truncated ID
id_short = substr("${local.id_truncated}${local.id_hash}", 0, local.id_length_limit)
id = local.id_length_limit != 0 && length(local.id_full) > local.id_length_limit ? local.id_short : local.id_full
# Context of this label to pass to other label modules
output_context = {
enabled = local.enabled
name = local.name
namespace = local.namespace
environment = local.environment
stage = local.stage
delimiter = local.delimiter
attributes = local.attributes
tags = local.tags
additional_tag_map = local.additional_tag_map
label_order = local.label_order
regex_replace_chars = local.regex_replace_chars
id_length_limit = local.id_length_limit
label_key_case = local.label_key_case
label_value_case = local.label_value_case
}
}