Skip to content

Commit

Permalink
Merge pull request #2 from Wycliffe-USA/null_label
Browse files Browse the repository at this point in the history
Update Null Label to support terraform v.13.
  • Loading branch information
BarnumD authored Nov 16, 2020
2 parents 49659ee + 53a5a14 commit 91d000a
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 101 deletions.
4 changes: 2 additions & 2 deletions generic/null-label/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ However, if you have multiple different kinds of resources (e.g. instances, secu

NOTE The `null` refers to the primary Terraform [provider](httpswww.terraform.iodocsprovidersnullindex.html) used in this module.

Releases of this module from `0.12.0` onward support `HCL2` and only work with Terraform 0.12 or newer. Releases prior to this are compatible with earlier versions of terraform like Terraform 0.11.
Releases of this module from `1.1.0` only work with Terraform 0.13 or newer. Releases prior to this are compatible with earlier versions of terraform like Terraform 0.11.

Originally based on https://github.com/cloudposse/terraform-null-label, version 0.16.0.
Originally based on https://github.com/cloudposse/terraform-null-label, version 0.21.0.

---

Expand Down
93 changes: 67 additions & 26 deletions generic/null-label/main.tf
Original file line number Diff line number Diff line change
@@ -1,33 +1,62 @@
locals {

defaults = {
label_order = ["namespace", "name", "component", "environment", "stage", "attributes"]
delimiter = "-"
replacement = ""
label_order = ["namespace", "name", "component", "environment", "stage", "attributes"]
regex_replace_chars = "/[^-a-zA-Z0-9]/"
delimiter = "-"
replacement = ""
# The `sentinel` should match the `regex_replace_chars`, so it will be replaced with the `replacement` value
sentinel = "~"
attributes = [""]
sentinel = "\t"
attributes = []
id_length_limit = 0
id_hash_length = 5
}

# The values provided by variables superceed the values inherited from the context
# So far, we have decided not to allow overriding replacement, sentinel, or id_hash_length
replacement = local.defaults.replacement
sentinel = local.defaults.sentinel
id_hash_length = local.defaults.id_hash_length

enabled = var.enabled
regex_replace_chars = coalesce(var.regex_replace_chars, var.context.regex_replace_chars)
# The values provided by variables supersede the values inherited from the context object
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
component = var.component == null ? var.context.component : var.component
delimiter = var.delimiter == null ? var.context.delimiter : var.delimiter
attributes = compact(distinct(concat(var.attributes, var.context.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
}


enabled = local.input.enabled
regex_replace_chars = coalesce(local.input.regex_replace_chars, local.defaults.regex_replace_chars)

name = lower(replace(coalesce(local.input.name, local.sentinel), local.regex_replace_chars, local.replacement))
component = lower(replace(coalesce(local.input.component, local.sentinel), local.regex_replace_chars, local.replacement))
namespace = lower(replace(coalesce(local.input.namespace, local.sentinel), local.regex_replace_chars, local.replacement))
environment = lower(replace(coalesce(local.input.environment, local.sentinel), local.regex_replace_chars, local.replacement))
stage = lower(replace(coalesce(local.input.stage, local.sentinel), local.regex_replace_chars, local.replacement))
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

name = lower(replace(coalesce(var.name, var.context.name, local.defaults.sentinel), local.regex_replace_chars, local.defaults.replacement))
component = lower(replace(coalesce(var.component, var.context.component, local.defaults.sentinel), local.regex_replace_chars, local.defaults.replacement))
namespace = lower(replace(coalesce(var.namespace, var.context.namespace, local.defaults.sentinel), local.regex_replace_chars, local.defaults.replacement))
environment = lower(replace(coalesce(var.environment, var.context.environment, local.defaults.sentinel), local.regex_replace_chars, local.defaults.replacement))
stage = lower(replace(coalesce(var.stage, var.context.stage, local.defaults.sentinel), local.regex_replace_chars, local.defaults.replacement))

delimiter = coalesce(var.delimiter, var.context.delimiter, local.defaults.delimiter)
label_order = length(var.label_order) > 0 ? var.label_order : (length(var.context.label_order) > 0 ? var.context.label_order : local.defaults.label_order)
additional_tag_map = merge(var.context.additional_tag_map, var.additional_tag_map)

# Merge attributes
attributes = compact(distinct(concat(var.attributes, var.context.attributes, local.defaults.attributes)))
attributes = compact(distinct(concat(local.input.attributes, local.defaults.attributes)))

tags = merge(var.context.tags, local.generated_tags, var.tags)
tags = merge(local.generated_tags, local.input.tags)

tags_as_list_of_maps = flatten([
for key in keys(local.tags) : merge(
Expand All @@ -38,10 +67,10 @@ locals {
])

tags_context = {
# For AWS we need `Name` to be disambiguated sine it has a special meaning
namespace = local.namespace
# For AWS we need `Name` to be disambiguated since it has a special meaning
name = local.id
component = local.component
namespace = local.namespace
environment = local.environment
stage = local.stage
attributes = local.id_context.attributes
Expand All @@ -50,32 +79,44 @@ locals {
generated_tags = { for l in keys(local.tags_context) : title(l) => local.tags_context[l] if length(local.tags_context[l]) > 0 }

id_context = {
namespace = local.namespace
name = local.name
component = local.component
namespace = local.namespace
environment = local.environment
stage = local.stage
attributes = lower(replace(join(local.delimiter, local.attributes), local.regex_replace_chars, local.defaults.replacement))
attributes = lower(replace(join(local.delimiter, local.attributes), local.regex_replace_chars, local.replacement))
}

labels = [for l in local.label_order : local.id_context[l] if length(local.id_context[l]) > 0]

id = lower(join(local.delimiter, local.labels))
id_full = lower(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}"
id_hash = md5(local.id_full)
# 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
namespace = local.namespace
name = local.name
component = local.component
namespace = local.namespace
environment = local.environment
stage = local.stage
delimiter = local.delimiter
attributes = local.attributes
tags = local.tags
delimiter = local.delimiter
additional_tag_map = local.additional_tag_map
label_order = local.label_order
regex_replace_chars = local.regex_replace_chars
additional_tag_map = local.additional_tag_map
id_length_limit = local.id_length_limit
}

}
}
74 changes: 54 additions & 20 deletions generic/null-label/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,59 +1,93 @@
output "id" {
value = local.enabled ? local.id : ""
description = "Disambiguated ID"
description = "Disambiguated ID restricted to `id_length_limit` characters in total"
}

output "name" {
value = local.enabled ? local.name : ""
description = "Normalized name"
output "id_full" {
value = local.enabled ? local.id_full : ""
description = "Disambiguated ID not restricted in length"
}

output "component" {
value = local.enabled ? local.component : ""
description = "Normalized component name"
output "enabled" {
value = local.enabled
description = "True if module is enabled, false otherwise"
}

output "namespace" {
value = local.enabled ? local.namespace : ""
description = "Normalized namespace"
}

output "stage" {
value = local.enabled ? local.stage : ""
description = "Normalized stage"
}

output "environment" {
value = local.enabled ? local.environment : ""
description = "Normalized environment"
}

output "attributes" {
value = local.enabled ? local.attributes : []
description = "List of attributes"
output "name" {
value = local.enabled ? local.name : ""
description = "Normalized name"
}

output "component" {
value = local.enabled ? local.component : ""
description = "Normalized component"
}

output "stage" {
value = local.enabled ? local.stage : ""
description = "Normalized stage"
}

output "delimiter" {
value = local.enabled ? local.delimiter : ""
description = "Delimiter between `namespace`, `environment`, `stage`, `name` and `attributes`"
}

output "attributes" {
value = local.enabled ? local.attributes : []
description = "List of attributes"
}

output "tags" {
value = local.enabled ? local.tags : {}
description = "Normalized Tag map"
}

output "additional_tag_map" {
value = local.additional_tag_map
description = "The merged additional_tag_map"
}

output "label_order" {
value = local.label_order
description = "The naming order actually used to create the ID"
}

output "regex_replace_chars" {
value = local.regex_replace_chars
description = "The regex_replace_chars actually used to create the ID"
}

output "id_length_limit" {
value = local.id_length_limit
description = "The id_length_limit actually used to create the ID, with `0` meaning unlimited"
}

output "tags_as_list_of_maps" {
value = local.tags_as_list_of_maps
description = "Additional tags as a list of maps, which can be used in several AWS resources"
}

output "context" {
output "normalized_context" {
value = local.output_context
description = "Context of this module to pass to other label modules"
description = "Normalized context of this module"
}

output "label_order" {
value = local.label_order
description = "The naming order of the id output and Name tag"
output "context" {
value = local.input
description = <<-EOT
Merged but otherwise unmodified input to this module, to be used as context input to other modules.
Note: this version will have null values as defaults, not the values actually used as defaults.
EOT
}

Loading

0 comments on commit 91d000a

Please sign in to comment.