diff --git a/modules/aws-eks-addons/main.tf b/modules/aws-eks-addons/main.tf index 9d79644..9d5aec1 100644 --- a/modules/aws-eks-addons/main.tf +++ b/modules/aws-eks-addons/main.tf @@ -1,4 +1,31 @@ locals { + # Chart value section -> the name of the workload that section produces, given + # the release names this module pins ("argocd", "external-secrets"). Used to + # stamp a per-workload `Name` label, because a cost-allocation Name must be + # the Deployment/StatefulSet/DaemonSet name and `global.podLabels` would put + # the same Name on every component. Verified against a live argo-cd 8.1.3 / + # external-secrets 0.16.2 install; the section keys are unchanged in argo-cd + # 9.5.11. + # + # These are DEFAULTS, not a fixed list. The matching variable is merged over + # the map, so a caller can add a component the chart gained later (argo-cd's + # commitServer, say) or correct a name if a release is renamed, without a + # module release. See argocd_component_workload_names in variables.tf. + argocd_component_workload_names = merge({ + controller = "argocd-application-controller" + server = "argocd-server" + repoServer = "argocd-repo-server" + redis = "argocd-redis" + dex = "argocd-dex-server" + applicationSet = "argocd-applicationset-controller" + notifications = "argocd-notifications-controller" + }, var.argocd_component_workload_names) + + external_secrets_component_workload_names = merge({ + webhook = "external-secrets-webhook" + certController = "external-secrets-cert-controller" + }, var.external_secrets_component_workload_names) + can_connect_alb_to_nginx = var.deploy_aws_loadbalancer && (length(var.connect_hostnames_from_alb_to_nginx) > 0) can_connect_alb_to_istio = var.deploy_aws_loadbalancer && var.deploy_rancher_istio && (length(var.connect_hostnames_from_alb_to_istio) > 0) can_connect_nginx_to_argocd = var.deploy_aws_loadbalancer && var.deploy_argocd @@ -73,6 +100,10 @@ resource "helm_release" "aws_lb_controller" { name = "clusterName" value = var.cluster_name } + + values = length(var.aws_lb_controller_pod_labels) > 0 ? [yamlencode({ + podLabels = var.aws_lb_controller_pod_labels + })] : [] } resource "helm_release" "ingress_nginx" { @@ -217,7 +248,7 @@ resource "kubernetes_ingress_v1" "alb_ingress_connect_nginx_internal" { } resource "kubernetes_annotations" "alb_ingress_connect_internal_nginx_annotation" { - count = var.enable_internal_alb ? 1 : 0 + count = var.enable_internal_alb ? 1 : 0 api_version = "networking.k8s.io/v1" kind = "Ingress" force = true @@ -434,16 +465,24 @@ resource "helm_release" "argocd" { value = "" } - values = var.enable_sso || var.enable_template_file ? [templatefile("${path.module}/values.yaml.tpl", { - caData = local.caData, - ssoURL = local.ssoURL, - redirectURI = "${var.sso_callback_url}", - entityIssuer = "${var.sso_callback_url}", - currentEnvironment = "${var.current_environment}", - slackToken = local.slackToken, - argocdUrl = var.argocd_ingress_host - }) - ] : [] + # Helm merges the elements of `values` left-to-right, so the pod-label + # document is appended last and cannot be clobbered by the SSO template. + values = concat( + var.enable_sso || var.enable_template_file ? [templatefile("${path.module}/values.yaml.tpl", { + caData = local.caData, + ssoURL = local.ssoURL, + redirectURI = "${var.sso_callback_url}", + entityIssuer = "${var.sso_callback_url}", + currentEnvironment = "${var.current_environment}", + slackToken = local.slackToken, + argocdUrl = var.argocd_ingress_host + }) + ] : [], + length(var.argocd_pod_labels) > 0 ? [yamlencode({ + for component, workload in local.argocd_component_workload_names : + component => { podLabels = merge(var.argocd_pod_labels, { Name = workload }) } + })] : [], + ) // SSO Values // configmap url @@ -535,6 +574,19 @@ resource "helm_release" "external-secrets" { name = "serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn" value = var.eso_iam_role_arn } + + # The chart keeps a separate podLabels key per Deployment, so all three are set + # to cover external-secrets, -webhook and -cert-controller - each with its own + # workload name stamped into Name. + values = length(var.external_secrets_pod_labels) > 0 ? [yamlencode(merge( + { + podLabels = merge(var.external_secrets_pod_labels, { Name = "external-secrets" }) + }, + { + for component, workload in local.external_secrets_component_workload_names : + component => { podLabels = merge(var.external_secrets_pod_labels, { Name = workload }) } + }, + ))] : [] } @@ -563,17 +615,19 @@ resource "kubectl_manifest" "argocd_bootstrapper_application" { values : yamlencode({ certManager : merge({ enable : var.deploy_cert_manager - }, var.cert_manager_version == null ? {} : { + }, var.cert_manager_version == null ? {} : { targetRevision : var.cert_manager_version }) metricsServer : merge({ enable : var.deploy_metrics_server - }, var.metrics_server_version == null ? {} : { + }, var.metrics_server_version == null ? {} : { targetRevision : var.metrics_server_version + }, length(keys(var.metrics_server_values)) == 0 ? {} : { + values : var.metrics_server_values }) trivy : merge({ enable : var.deploy_trivy - }, var.trivy_version == null ? {} : { + }, var.trivy_version == null ? {} : { targetRevision : var.trivy_version }) rancher : merge({ @@ -581,22 +635,22 @@ resource "kubectl_manifest" "argocd_bootstrapper_application" { values : { hostname : var.rancher_hostname } - }, var.rancher_version == null ? {} : { + }, var.rancher_version == null ? {} : { targetRevision : var.rancher_version }) rancherMonitoringCrd : merge({ enable : local.deploy_rancher_monitoring - }, var.rancher_monitoring_crd_version == null ? {} : { + }, var.rancher_monitoring_crd_version == null ? {} : { targetRevision : var.rancher_monitoring_crd_version }) rancherMonitoring : merge({ enable : local.deploy_rancher_monitoring - }, var.rancher_monitoring_version == null ? {} : { + }, var.rancher_monitoring_version == null ? {} : { targetRevision : var.rancher_monitoring_version }) rancherIstio : merge({ enable : local.deploy_rancher_istio - }, var.rancher_istio_version == null ? {} : { + }, var.rancher_istio_version == null ? {} : { targetRevision : var.rancher_istio_version }) argoWorkflow : merge({ @@ -610,12 +664,12 @@ resource "kubectl_manifest" "argocd_bootstrapper_application" { extraArgs : var.argo_workflow_extra_args } } - }, var.argo_workflow_version == null ? {} : { + }, var.argo_workflow_version == null ? {} : { targetRevision : var.argo_workflow_version }) rancherLogging : merge({ enable : var.deploy_rancher_logging - values : { + values : merge({ fluentd : { resources : { limits : { @@ -629,8 +683,13 @@ resource "kubectl_manifest" "argocd_bootstrapper_application" { } } - } - }, var.rancher_logging_version == null ? {} : { + }, length(var.rancher_logging_pod_labels) == 0 ? {} : { + # Reaches the rancher-logging operator Deployment only. The + # fluentbit DaemonSet and fluentd StatefulSet are made by the + # operator from the Logging CR - label them there. + podLabels : var.rancher_logging_pod_labels + }) + }, var.rancher_logging_version == null ? {} : { targetRevision : var.rancher_logging_version }) }) @@ -749,6 +808,10 @@ resource "helm_release" "karpenter" { value = true } + values = length(var.karpenter_pod_labels) > 0 ? [yamlencode({ + podLabels = var.karpenter_pod_labels + })] : [] + } resource "kubectl_manifest" "karpenter_stateful_provisioner" { diff --git a/modules/aws-eks-addons/variables.tf b/modules/aws-eks-addons/variables.tf index 60380f7..df87252 100644 --- a/modules/aws-eks-addons/variables.tf +++ b/modules/aws-eks-addons/variables.tf @@ -580,4 +580,151 @@ variable "nginx_controller_pod_labels" { description = "Extra pod labels applied to the ingress-nginx controller pods (set as controller.podLabels in the ingress-nginx Helm release). Empty by default (no extra labels)." type = map(string) default = {} -} \ No newline at end of file +} +# --------------------------------------------------------------------------- +# Cost-allocation pod labels for the remaining platform workloads. +# +# Same shape and default as nginx_controller_pod_labels above: an empty map +# means "set nothing", so every existing caller is unaffected. Each variable +# maps onto a value key that was verified against the chart version this module +# pins (see the comment on each one). +# --------------------------------------------------------------------------- + +variable "aws_lb_controller_pod_labels" { + description = "Extra pod labels applied to the aws-load-balancer-controller pods (set as podLabels in the aws-load-balancer-controller Helm release). Empty by default (no extra labels)." + type = map(string) + default = {} +} + +variable "argocd_pod_labels" { + description = <<-EOT + Extra pod labels applied to every Argo CD component pod, set per component + (controller.podLabels, server.podLabels, repoServer.podLabels, + redis.podLabels, dex.podLabels, applicationSet.podLabels, + notifications.podLabels). Empty by default (no extra labels). + + DO NOT pass `Name` here: the module stamps a per-component Name so each + label set carries the name of the workload it lands on, which is what a + cost-allocation Name has to be. Anything passed as Name is overwritten with: + controller -> argocd-application-controller + server -> argocd-server + repoServer -> argocd-repo-server + redis -> argocd-redis + dex -> argocd-dex-server + applicationSet -> argocd-applicationset-controller + notifications -> argocd-notifications-controller + + global.podLabels is deliberately NOT used - it would put one Name on all + seven components. + EOT + type = map(string) + default = {} +} + +variable "external_secrets_pod_labels" { + description = <<-EOT + Extra pod labels applied to the external-secrets controller, webhook and + cert-controller pods (podLabels, webhook.podLabels and + certController.podLabels). Empty by default (no extra labels). + + DO NOT pass `Name` here - as with argocd_pod_labels the module stamps a + per-component Name and overwrites anything passed: + (root) -> external-secrets + webhook -> external-secrets-webhook + certController -> external-secrets-cert-controller + EOT + type = map(string) + default = {} +} + +variable "karpenter_pod_labels" { + description = "Extra pod labels applied to the Karpenter controller pods (set as podLabels in the karpenter Helm release). Only takes effect when deploy_karpenter is true. Empty by default (no extra labels)." + type = map(string) + default = {} +} + +variable "metrics_server_values" { + description = <<-EOT + Free-form Helm values forwarded to the metrics-server Application that the + ArgoCD bootstrapper renders (metricsServer.values in the argo-bootstrapper + Helm values). Empty by default, which keeps the bootstrapper's own defaults. + + This is a REPLACEMENT, not a merge: whatever is passed here becomes the + Application's entire values block. If the cluster already relies on values + that were set outside Terraform (hostNetwork.enabled=true is a common one), + those MUST be repeated here or they will be dropped on the next sync. Read + the live values first: + kubectl -n argocd get application metrics-server \ + -o jsonpath='{.spec.source.helm.values}' + + Example: + metrics_server_values = { + hostNetwork = { enabled = true } + podLabels = { Environment = "prod" } + } + EOT + type = any + default = {} +} + +variable "rancher_logging_pod_labels" { + description = <<-EOT + Extra pod labels applied to the rancher-logging OPERATOR pods (set as + podLabels in the rancher-logging chart values forwarded through the ArgoCD + bootstrapper). Empty by default (no extra labels). + + This does NOT reach the fluentbit DaemonSet or the fluentd StatefulSet: + those pods are created by the Logging operator from the Logging custom + resource, and their labels live in spec.fluentbit.labels / + spec.fluentd.labels on that CR, not in the chart values. + EOT + type = map(string) + default = {} +} + +variable "argocd_component_workload_names" { + description = <<-EOT + Overrides and additions to the argo-cd chart-section -> workload-name map used + to stamp the per-component `Name` label. Merged OVER the module's defaults, so + `{}` (the default) keeps current behaviour. + + Module defaults, for the release name "argocd": + controller = "argocd-application-controller" + server = "argocd-server" + repoServer = "argocd-repo-server" + redis = "argocd-redis" + dex = "argocd-dex-server" + applicationSet = "argocd-applicationset-controller" + notifications = "argocd-notifications-controller" + + Use this when the chart gains a component the defaults do not cover, or when a + release name makes the rendered workload name differ. Example - label the + commit-server the chart added in a later version, and nothing else: + + argocd_component_workload_names = { + commitServer = "argocd-commit-server" + } + + A key here must be a real argo-cd values section that supports `podLabels`, or + the value is silently ignored by Helm. + EOT + type = map(string) + default = {} +} + +variable "external_secrets_component_workload_names" { + description = <<-EOT + Overrides and additions to the external-secrets chart-section -> workload-name + map used to stamp the per-component `Name` label. Merged OVER the module's + defaults, so `{}` (the default) keeps current behaviour. + + Module defaults, for the release name "external-secrets": + webhook = "external-secrets-webhook" + certController = "external-secrets-cert-controller" + + The chart's root `podLabels` (the main controller Deployment) is always set and + is not part of this map. + EOT + type = map(string) + default = {} +}