diff --git a/bnk/cneinstance/bnkforge.pack.json b/bnk/cneinstance/bnkforge.pack.json index 17574a7..ec94610 100644 --- a/bnk/cneinstance/bnkforge.pack.json +++ b/bnk/cneinstance/bnkforge.pack.json @@ -203,6 +203,15 @@ "default": "", "source": "auto" }, + { + "name": "bnk_gateway_chassis", + "type": "object({name=optional(string),default_listener_networks=list(object({name=string,start_address=string,end_address=string}))})", + "description": "F5BnkGateway chassis CR — required on AWS/EKS for Gateway-API translation. Without it the CNE controller silently ignores all Gateway/HTTPRoute CRs even when Programmed=True. Empty default_listener_networks = skip (on-prem default).", + "default": { + "default_listener_networks": [] + }, + "source": "user" + }, { "name": "tmm_init_enabled", "type": "boolean", diff --git a/bnk/cneinstance/main.tf b/bnk/cneinstance/main.tf index 84523d0..a500ea2 100644 --- a/bnk/cneinstance/main.tf +++ b/bnk/cneinstance/main.tf @@ -376,6 +376,67 @@ MANIFEST } } +# ============================================================================= +# F5BNKGATEWAY CHASSIS CR (AWS/EKS — required for Gateway-API translation) +# ============================================================================= +# Despite F5 docs saying F5BnkGateway is optional, in f5ingress:v14.19.4-0.1.36 +# it's the trigger that activates the entire Gateway translation pipeline on +# AWS/EKS. Without it the controller logs "Watched application namespaces: []" +# and silently ignores all Gateway+HTTPRoute CRs even with Programmed=True. +# +# Recipe verified on aws-syd-test 2026-04-30 — see agent memory note +# project_aws_syd_test_tmm_kernelmode_break.md for the discovery trail. +# +# Created in the same namespace as the CNEInstance (controller's namespace). +# Empty default_listener_networks list = skip (preserves on-prem behavior). + +resource "null_resource" "f5_bnkgateway_chassis" { + count = length(var.bnk_gateway_chassis.default_listener_networks) > 0 ? 1 : 0 + + triggers = { + chassis_hash = sha256(jsonencode(var.bnk_gateway_chassis)) + namespace = var.instance_namespace + name = var.bnk_gateway_chassis.name + kubeconfig = local_file.kubeconfig.filename + } + + provisioner "local-exec" { + command = <<-EOT + echo "=== Creating F5BnkGateway chassis CR ===" + cat <<'MANIFEST' | ${local.kubectl} apply -f - +apiVersion: k8s.f5net.com/v1 +kind: F5BnkGateway +metadata: + name: ${var.bnk_gateway_chassis.name} + namespace: ${var.instance_namespace} +spec: + ingressConfig: + defaultListenerNetworks: +%{for net in var.bnk_gateway_chassis.default_listener_networks~} + - name: "${net.name}" + startAddress: "${net.start_address}" + endAddress: "${net.end_address}" +%{endfor~} +MANIFEST + echo "F5BnkGateway chassis ${var.bnk_gateway_chassis.name} created" + EOT + } + + provisioner "local-exec" { + when = destroy + command = <<-EOT + echo "=== Deleting F5BnkGateway chassis ${self.triggers.name} ===" + kubectl --kubeconfig ${self.triggers.kubeconfig} delete f5-bnkgateway ${self.triggers.name} \ + -n ${self.triggers.namespace} 2>/dev/null || \ + echo "F5BnkGateway already deleted or not found" + EOT + } + + depends_on = [ + null_resource.cneinstance, + ] +} + # ============================================================================= # WRITE MANIFEST TO FILE # ============================================================================= diff --git a/bnk/cneinstance/variables.tf b/bnk/cneinstance/variables.tf index 73a4467..e07a1ce 100644 --- a/bnk/cneinstance/variables.tf +++ b/bnk/cneinstance/variables.tf @@ -114,6 +114,34 @@ variable "cloud_az_subnet_mappings" { default = [] } +variable "bnk_gateway_chassis" { + description = <<-EOT + F5BnkGateway chassis CR config. Must be set for AWS/EKS deployments — + the CNE controller's Gateway-API translation pipeline depends on it. + Without this CR, the controller logs "Watched application namespaces: + []" and ignores all Gateway+HTTPRoute CRs even when CNEInstance shows + Programmed=True. + + Use explicit start_address + end_address per listener network. The + CRD's ipv4BaseCidr alternative is rejected by the controller runtime + ("IPRange error: start/end IP addresses do not match IPv4 family"). + + Empty default_listener_networks list = skip chassis CR creation + (preserves pre-existing on-prem behavior). + EOT + type = object({ + name = optional(string, "bnk-gateway-chassis") + default_listener_networks = list(object({ + name = string + start_address = string + end_address = string + })) + }) + default = { + default_listener_networks = [] + } +} + # ============================================================================= # DEPLOYMENT CONFIGURATION # =============================================================================