diff --git a/CHANGELOG.md b/CHANGELOG.md index 948ece76..0e1dee2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +- Fix: k8s scopes that have both a custom domain and additional ports now deploy, instead of failing with "Failed to build ingress template" + ## [1.16.3] - 2026-09-08 - Add: remove resource level restriction for `elasticloadbalancing:Describe*` permissions as AWS does not support it. - Fix: k8s deployment logs no longer show a "np_trace_flush: command not found" error when tracing is disabled diff --git a/k8s/deployment/templates/blue-green-ingress.yaml.tpl b/k8s/deployment/templates/blue-green-ingress.yaml.tpl index c35f53fe..b937725f 100644 --- a/k8s/deployment/templates/blue-green-ingress.yaml.tpl +++ b/k8s/deployment/templates/blue-green-ingress.yaml.tpl @@ -83,6 +83,8 @@ spec: {{ if .scope.capabilities.additional_ports }} {{ range .scope.capabilities.additional_ports }} +{{- $port := .port }} +{{- $port_type := .type }} --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -192,20 +194,20 @@ spec: - host: {{ .name }} http: paths: - {{ if eq $.type "HTTP" }} - - path: /{{ $.port }} + {{ if eq $port_type "HTTP" }} + - path: /{{ $port }} pathType: Prefix backend: service: - name: bg-deployment-{{ if eq $.type "HTTP" }}http{{ else }}grpc{{ end }}-{{ $.port }} + name: bg-deployment-{{ if eq $port_type "HTTP" }}http{{ else }}grpc{{ end }}-{{ $port }} port: name: use-annotation - {{ else if eq $.type "GRPC" }} + {{ else if eq $port_type "GRPC" }} - path: / pathType: Prefix backend: service: - name: bg-deployment-{{ if eq $.type "HTTP" }}http{{ else }}grpc{{ end }}-{{ $.port }} + name: bg-deployment-{{ if eq $port_type "HTTP" }}http{{ else }}grpc{{ end }}-{{ $port }} port: name: use-annotation {{ end }} diff --git a/k8s/deployment/templates/initial-ingress.yaml.tpl b/k8s/deployment/templates/initial-ingress.yaml.tpl index 088a1eaf..1524b615 100644 --- a/k8s/deployment/templates/initial-ingress.yaml.tpl +++ b/k8s/deployment/templates/initial-ingress.yaml.tpl @@ -77,6 +77,8 @@ spec: {{- end }} {{ if .scope.capabilities.additional_ports }} {{ range .scope.capabilities.additional_ports }} +{{- $port := .port }} +{{- $port_type := .type }} --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -155,9 +157,9 @@ spec: pathType: Prefix backend: service: - name: d-{{ $.scope.id }}-{{ $.deployment.id }}-{{ if eq .type "HTTP" }}http{{ else }}grpc{{ end }}-{{ .port }} + name: d-{{ $.scope.id }}-{{ $.deployment.id }}-{{ if eq $port_type "HTTP" }}http{{ else }}grpc{{ end }}-{{ $port }} port: - number: {{ .port }} + number: {{ $port }} {{- end }} {{ end }} {{ end }} diff --git a/k8s/deployment/tests/ingress_domains_additional_ports.bats b/k8s/deployment/tests/ingress_domains_additional_ports.bats new file mode 100644 index 00000000..1cdf28b7 --- /dev/null +++ b/k8s/deployment/tests/ingress_domains_additional_ports.bats @@ -0,0 +1,123 @@ +#!/usr/bin/env bats + +setup() { + export PROJECT_ROOT="$(cd "$BATS_TEST_DIRNAME/../../.." && pwd)" + source "$PROJECT_ROOT/testing/assertions.sh" + export TPL_DIR="$PROJECT_ROOT/k8s/deployment/templates" + export INITIAL="$TPL_DIR/initial-ingress.yaml.tpl" + export BLUE_GREEN="$TPL_DIR/blue-green-ingress.yaml.tpl" +} + +_context() { + cat <<'JSON' +{ + "account": {"id": "acc1", "slug": "acct"}, + "namespace": {"id": "ns1", "slug": "nsps"}, + "application": {"id": "app1", "slug": "appslug"}, + "scope": { + "id": "scope-123", + "slug": "scopeslug", + "domain": "platform.example.com", + "domains": [ + {"id": "dom-1", "name": "custom-one.example.com", "status": "active", "type": "scope"}, + {"id": "dom-2", "name": "custom-two.example.com", "status": "active", "type": "scope"} + ], + "capabilities": { + "main_http_port": 8080, + "additional_ports": [ + {"port": 8081, "type": "HTTP", "traffic_manager_port": 18081}, + {"port": 9012, "type": "GRPC", "traffic_manager_port": 19012} + ] + } + }, + "deployment": {"id": "deploy-456", "strategy_data": {"desired_switched_traffic": 50}}, + "blue_deployment_id": "deploy-123", + "blue_additional_port_services": {"http-8081": true, "grpc-9012": true}, + "k8s_namespace": "ns-test", + "k8s_modifiers": {}, + "alb_name": "k8s-test-alb", + "ingress_visibility": "internal", + "main_http_port": 8080 +} +JSON +} + +_render() { + local tpl="$1" + local ctx="$BATS_TEST_TMPDIR/ctx.json" + _context > "$ctx" + gomplate -c .="$ctx" -f "$tpl" +} + +_backend() { + local rendered="$1" ingress="$2" host="$3" + echo "$rendered" | yq -N "select(.metadata.name == \"$ingress\") | .spec.rules[] | select(.host == \"$host\") | .http.paths[0].backend.service | .name + \":\" + (.port.number // .port.name | tostring)" +} + +_path() { + local rendered="$1" ingress="$2" host="$3" + echo "$rendered" | yq -N "select(.metadata.name == \"$ingress\") | .spec.rules[] | select(.host == \"$host\") | .http.paths[0].path" +} + +@test "initial-ingress: renders when a scope has both custom domains and additional ports" { + run _render "$INITIAL" + [ "$status" -eq 0 ] +} + +@test "initial-ingress: custom domains route each additional port to its own service" { + rendered=$(_render "$INITIAL") + + for host in custom-one.example.com custom-two.example.com; do + assert_equal "d-scope-123-deploy-456-http-8081:8081" \ + "$(_backend "$rendered" k-8-s-scopeslug-scope-123-http-8081-internal "$host")" + assert_equal "d-scope-123-deploy-456-grpc-9012:9012" \ + "$(_backend "$rendered" k-8-s-scopeslug-scope-123-grpc-9012-internal "$host")" + done +} + +@test "initial-ingress: custom domain rules match the platform domain rule" { + rendered=$(_render "$INITIAL") + + for ingress in k-8-s-scopeslug-scope-123-http-8081-internal k-8-s-scopeslug-scope-123-grpc-9012-internal; do + platform=$(_backend "$rendered" "$ingress" platform.example.com) + for host in custom-one.example.com custom-two.example.com; do + assert_equal "$platform" "$(_backend "$rendered" "$ingress" "$host")" + done + done +} + +@test "initial-ingress: every rendered document is valid yaml with a name" { + rendered=$(_render "$INITIAL") + + names=$(echo "$rendered" | yq -N '.metadata.name') + assert_equal 3 "$(echo "$names" | grep -c .)" + if echo "$names" | grep -q 'null'; then + echo "a rendered document lost its metadata.name: $rendered" + return 1 + fi +} + +@test "blue-green-ingress: renders when a scope has both custom domains and additional ports" { + run _render "$BLUE_GREEN" + [ "$status" -eq 0 ] +} + +@test "blue-green-ingress: custom domains route each additional port through its own annotation action" { + rendered=$(_render "$BLUE_GREEN") + + for host in custom-one.example.com custom-two.example.com; do + assert_equal "bg-deployment-http-8081:use-annotation" \ + "$(_backend "$rendered" k-8-s-scopeslug-scope-123-http-8081-internal "$host")" + assert_equal "bg-deployment-grpc-9012:use-annotation" \ + "$(_backend "$rendered" k-8-s-scopeslug-scope-123-grpc-9012-internal "$host")" + done +} + +@test "blue-green-ingress: each additional port keeps its own path shape on custom domains" { + rendered=$(_render "$BLUE_GREEN") + + for host in custom-one.example.com custom-two.example.com; do + assert_equal "/8081" "$(_path "$rendered" k-8-s-scopeslug-scope-123-http-8081-internal "$host")" + assert_equal "/" "$(_path "$rendered" k-8-s-scopeslug-scope-123-grpc-9012-internal "$host")" + done +}