diff --git a/nullplatform/agent/locals.tf b/nullplatform/agent/locals.tf index e7bde312..14144903 100644 --- a/nullplatform/agent/locals.tf +++ b/nullplatform/agent/locals.tf @@ -55,14 +55,33 @@ locals { ) : k => v if v != null } + # Template paths per ingress stack. "alb" sends empty values so the k8s scope + # falls back to its own defaults (AWS Load Balancer Controller Ingress); + # "istio" points at the Gateway API templates the scopes/containers image + # bakes under /app/pkg/k8s/deployment/templates/istio. An explicit + # service_template / initial_ingress_path / blue_green_ingress_path wins. + worker_ingress_templates = { + alb = { + SERVICE_TEMPLATE = "" + INITIAL_INGRESS_PATH = "" + BLUE_GREEN_INGRESS_PATH = "" + } + istio = { + SERVICE_TEMPLATE = "/app/pkg/k8s/deployment/templates/istio/service.yaml.tpl" + INITIAL_INGRESS_PATH = "/app/pkg/k8s/deployment/templates/istio/initial-httproute.yaml.tpl" + BLUE_GREEN_INGRESS_PATH = "/app/pkg/k8s/deployment/templates/istio/blue-green-httproute.yaml.tpl" + } + } + worker_templates = local.worker_ingress_templates[var.worker_ingress] + worker_default_env = { DNS_TYPE = var.dns_type DOMAIN = var.domain USE_ACCOUNT_SLUG = var.use_account_slug K8S_NAMESPACE = var.namespace - SERVICE_TEMPLATE = var.service_template - INITIAL_INGRESS_PATH = var.initial_ingress_path - BLUE_GREEN_INGRESS_PATH = var.blue_green_ingress_path + SERVICE_TEMPLATE = var.service_template != "" ? var.service_template : local.worker_templates.SERVICE_TEMPLATE + INITIAL_INGRESS_PATH = var.initial_ingress_path != "" ? var.initial_ingress_path : local.worker_templates.INITIAL_INGRESS_PATH + BLUE_GREEN_INGRESS_PATH = var.blue_green_ingress_path != "" ? var.blue_green_ingress_path : local.worker_templates.BLUE_GREEN_INGRESS_PATH TRAFFIC_CONTAINER_IMAGE = "${var.agent_traffic_manager_repository}:${var.agent_traffic_manager_tag}" IMAGE_PULL_SECRETS = var.image_pull_secrets PRIVATE_GATEWAY_NAME = var.private_gateway_name diff --git a/nullplatform/agent/tests/agent_values.tftest.hcl b/nullplatform/agent/tests/agent_values.tftest.hcl index beeb54dc..ae5cc5ec 100644 --- a/nullplatform/agent/tests/agent_values.tftest.hcl +++ b/nullplatform/agent/tests/agent_values.tftest.hcl @@ -358,3 +358,78 @@ run "long_worker_patch_strings_survive_rendering" { error_message = "a worker patch string longer than the yamlencode fold width must not pick up a newline when the values are rendered" } } + +################################################################################ +# worker_ingress +################################################################################ + +# The k8s scope only knows which ingress stack to deploy through the three +# template paths; nothing reads an INGRESS_TYPE. The default keeps the scope's +# own (ALB Ingress) templates by sending empty values. +run "worker_ingress_defaults_to_alb_and_leaves_the_scope_templates" { + command = plan + + assert { + condition = alltrue([ + for key in ["SERVICE_TEMPLATE", "INITIAL_INGRESS_PATH", "BLUE_GREEN_INGRESS_PATH"] : + anytrue([ + for p in yamldecode(helm_release.agent.values[0]).worker.patches : + anytrue([for e in try(p.merge.spec.containers[0].env, []) : e.name == key && e.value == ""]) + if try(p.target.package, "") == "containers" + ]) + ]) + error_message = "with worker_ingress = alb the three template paths must render empty so the k8s scope uses its own templates" + } +} + +run "worker_ingress_istio_derives_the_gateway_api_template_paths" { + command = plan + + variables { + worker_ingress = "istio" + } + + assert { + condition = alltrue([ + for key, want in { + SERVICE_TEMPLATE = "/app/pkg/k8s/deployment/templates/istio/service.yaml.tpl" + INITIAL_INGRESS_PATH = "/app/pkg/k8s/deployment/templates/istio/initial-httproute.yaml.tpl" + BLUE_GREEN_INGRESS_PATH = "/app/pkg/k8s/deployment/templates/istio/blue-green-httproute.yaml.tpl" + } : + anytrue([ + for p in yamldecode(helm_release.agent.values[0]).worker.patches : + anytrue([for e in try(p.merge.spec.containers[0].env, []) : e.name == key && e.value == want]) + if try(p.target.package, "") == "containers" + ]) + ]) + error_message = "worker_ingress = istio must point the containers worker at the istio templates baked in the image" + } +} + +run "explicit_template_paths_override_worker_ingress" { + command = plan + + variables { + worker_ingress = "istio" + service_template = "/custom/service.yaml.tpl" + } + + assert { + condition = anytrue([ + for p in yamldecode(helm_release.agent.values[0]).worker.patches : + anytrue([for e in try(p.merge.spec.containers[0].env, []) : e.name == "SERVICE_TEMPLATE" && e.value == "/custom/service.yaml.tpl"]) + if try(p.target.package, "") == "containers" + ]) + error_message = "an explicit service_template must win over the path worker_ingress derives" + } +} + +run "worker_ingress_rejects_unknown_stacks" { + command = plan + + variables { + worker_ingress = "nginx" + } + + expect_failures = [var.worker_ingress] +} diff --git a/nullplatform/agent/variables.tf b/nullplatform/agent/variables.tf index b0ee81af..21028cbf 100644 --- a/nullplatform/agent/variables.tf +++ b/nullplatform/agent/variables.tf @@ -290,23 +290,39 @@ variable "image_pull_secrets" { # Ingress / Networking Configuration ################################################################################ -# Scope service template to use for deployment (required when extra_envs.INGRESS_TYPE is 'istio') + +# Which ingress stack the containers worker deploys scopes with. The k8s scope +# ships two template sets under /app/pkg/k8s/deployment/templates: its default +# (AWS Load Balancer Controller Ingress) and istio/ (Gateway API HTTPRoutes). +# Nothing else selects between them: the scope does not read INGRESS_TYPE, it +# just renders whatever SERVICE_TEMPLATE / INITIAL_INGRESS_PATH / +# BLUE_GREEN_INGRESS_PATH point at. This input derives the three paths so a +# root module states the decision instead of copying image paths around. +variable "worker_ingress" { + description = "Ingress stack the containers worker deploys scopes with: \"alb\" keeps the k8s scope's own templates (AWS Load Balancer Controller Ingress), \"istio\" points it at the Gateway API templates baked in the scopes/containers image. service_template, initial_ingress_path and blue_green_ingress_path override the derived paths when set." + type = string + default = "alb" + + validation { + condition = contains(["alb", "istio"], var.worker_ingress) + error_message = "worker_ingress must be \"alb\" or \"istio\"." + } +} + variable "service_template" { - description = "Specifies the name or reference of the scope service template to be used for deployment. Required when extra_envs.INGRESS_TYPE is 'istio' — the k8s scope's default template is AWS ALB Ingress and won't route traffic correctly through Istio, so it must be pointed at an Istio-compatible template instead." + description = "Path, inside the worker image, of the Service template the k8s scope renders. Empty (default) uses the template worker_ingress selects; set it only to point at a custom template." type = string default = "" } -# Initial ingress path used on first deploy (required when extra_envs.INGRESS_TYPE is 'istio') variable "initial_ingress_path" { - description = "Defines the initial ingress path used when deploying the application for the first time. Required when extra_envs.INGRESS_TYPE is 'istio' — the k8s scope's default template is AWS ALB Ingress and won't route traffic correctly through Istio, so it must be pointed at an Istio HTTPRoute template instead." + description = "Path, inside the worker image, of the ingress/route template used on a scope's first deployment. Empty (default) uses the template worker_ingress selects; set it only to point at a custom template." type = string default = "" } -# Blue-green ingress path used to route traffic to the new version (required when extra_envs.INGRESS_TYPE is 'istio') variable "blue_green_ingress_path" { - description = "Specifies the ingress path used for blue-green deployments to route traffic to the new version. Required when extra_envs.INGRESS_TYPE is 'istio' — the k8s scope's default template is AWS ALB Ingress and won't route traffic correctly through Istio, so it must be pointed at an Istio HTTPRoute template instead." + description = "Path, inside the worker image, of the ingress/route template used to shift traffic during a blue-green deployment. Empty (default) uses the template worker_ingress selects; set it only to point at a custom template." type = string default = "" }