Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ 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).

## [1.17.0] - 2026-09-11
- Fix: turning off the health check on a k8s, azure or azure-aro scope now removes the liveness, readiness and startup probes from every container of the pod
- 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
Expand Down
12 changes: 7 additions & 5 deletions k8s/deployment/templates/blue-green-ingress.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
Expand Down
9 changes: 9 additions & 0 deletions k8s/deployment/templates/deployment.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
initialDelaySeconds: {{ .healthCheck.initial_delay_seconds }}
successThreshold: 1
{{- end }}
{{- $healthCheckEnabled := or (not (has .scope.capabilities.health_check "enabled")) .scope.capabilities.health_check.enabled }}

apiVersion: apps/v1
kind: Deployment
Expand Down Expand Up @@ -147,6 +148,7 @@ spec:
memory: {{ .container_memory_in_memory }}Mi
requests:
cpu: 31m
{{- if $healthCheckEnabled }}
livenessProbe:
{{- if and (has .scope.capabilities.health_check "type") (eq .scope.capabilities.health_check.type "TCP") }}
{{- template "probe.tcp" dict "healthCheck" .scope.capabilities.health_check "traffic_port" .main_traffic_manager_port "app_port" .main_http_port }}
Expand All @@ -171,6 +173,7 @@ spec:
{{- end }}
{{- template "probe.base" dict "healthCheck" .scope.capabilities.health_check }}
failureThreshold: 90
{{- end }}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
Expand Down Expand Up @@ -202,6 +205,7 @@ spec:
memory: {{ $.container_memory_in_memory }}Mi
requests:
cpu: 31m
{{- if $healthCheckEnabled }}
livenessProbe:
grpc:
port: {{ .traffic_manager_port }}
Expand All @@ -226,6 +230,7 @@ spec:
initialDelaySeconds: {{ $.scope.capabilities.health_check.initial_delay_seconds }}
successThreshold: 1
failureThreshold: 90
{{- end }}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
Expand Down Expand Up @@ -256,6 +261,7 @@ spec:
memory: {{ $.container_memory_in_memory }}Mi
requests:
cpu: 31m
{{- if $healthCheckEnabled }}
livenessProbe:
httpGet:
path: {{ $.scope.capabilities.health_check.path }}
Expand Down Expand Up @@ -283,6 +289,7 @@ spec:
initialDelaySeconds: {{ $.scope.capabilities.health_check.initial_delay_seconds }}
successThreshold: 1
failureThreshold: 90
{{- end }}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
Expand Down Expand Up @@ -323,6 +330,7 @@ spec:
requests:
cpu: {{ .scope.capabilities.cpu_millicores }}m
memory: {{ .scope.capabilities.ram_memory }}Mi
{{- if $healthCheckEnabled }}
livenessProbe:
{{- if and (has .scope.capabilities.health_check "type") (eq .scope.capabilities.health_check.type "TCP") }}
{{- template "probe.app_tcp" dict "port" .main_http_port }}
Expand All @@ -347,6 +355,7 @@ spec:
{{- end }}
{{- template "probe.base" dict "healthCheck" .scope.capabilities.health_check }}
failureThreshold: 90
{{- end }}
lifecycle:
preStop:
exec:
Expand Down
6 changes: 4 additions & 2 deletions k8s/deployment/templates/initial-ingress.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
130 changes: 130 additions & 0 deletions k8s/deployment/tests/health_check_disabled.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env bats

setup() {
export PROJECT_ROOT="$(cd "$BATS_TEST_DIRNAME/../../.." && pwd)"
source "$PROJECT_ROOT/testing/assertions.sh"
export DEPLOYMENT="$PROJECT_ROOT/k8s/deployment/templates/deployment.yaml.tpl"
}

_context() {
local health_check="$1"
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": [],
"dimensions": {"env": "dev"},
"capabilities": {
"cpu_millicores": 100,
"ram_memory": 128,
"cpu_millicores_limit": 200,
"ram_memory_limit": 256,
"main_http_port": 8080,
"additional_ports": [
{"port": 8081, "type": "HTTP", "traffic_manager_port": 18081},
{"port": 9012, "type": "GRPC", "traffic_manager_port": 19012}
],
"scaling_type": "fixed",
"autoscaling": {
"min_replicas": 1,
"max_replicas": 3,
"target_cpu_utilization": 80,
"target_memory_enabled": false,
"target_memory_utilization": 80
},
"health_check": $health_check
}
},
"deployment": {"id": "deploy-456"},
"k8s_namespace": "ns-test",
"k8s_modifiers": {},
"asset": {"url": "example.com/app:latest"},
"main_http_port": 8080,
"main_traffic_manager_port": 80,
"traffic_image": "example.com/traffic:latest",
"container_cpu_in_millicores": 50,
"container_memory_in_memory": 64,
"pull_secrets": {"ENABLED": false, "SECRETS": []},
"region": "us-east-1",
"component": "app",
"service_account_name": "",
"traffic_manager_config_map": "",
"replicas": 1,
"parameters": {"results": []}
}
JSON
}

_health_check() {
echo "{\"enabled\": $1, \"type\": \"$2\", \"path\": \"/health\", \"timeout_seconds\": 1, \"period_seconds\": 5, \"initial_delay_seconds\": 5}"
}

_render() {
local ctx="$BATS_TEST_TMPDIR/ctx.json"
_context "$1" > "$ctx"
gomplate -c .="$ctx" -f "$DEPLOYMENT"
}

_probe_count() {
local rendered="$1" container="$2"
echo "$rendered" | yq -N ".spec.template.spec.containers[] | select(.name == \"$container\") | [.livenessProbe, .readinessProbe, .startupProbe] | map(select(. != null)) | length"
}

_assert_probes_on_every_container() {
local rendered="$1" expected="$2"
local container actual
for container in http application http-8081 grpc-9012; do
actual=$(_probe_count "$rendered" "$container")
if [ "$actual" != "$expected" ]; then
echo "container '$container': expected $expected probes, got $actual"
return 1
fi
done
}

@test "health check disabled: no container gets liveness, readiness or startup probes" {
rendered=$(_render "$(_health_check false HTTP)")

_assert_probes_on_every_container "$rendered" 0
}

@test "health check disabled with TCP type: no container gets probes either" {
rendered=$(_render "$(_health_check false TCP)")

_assert_probes_on_every_container "$rendered" 0
}

@test "health check enabled: every container keeps its three probes" {
rendered=$(_render "$(_health_check true HTTP)")

_assert_probes_on_every_container "$rendered" 3
}

@test "scope predating the enabled flag: every container keeps its three probes" {
rendered=$(_render '{"type": "HTTP", "path": "/health", "timeout_seconds": 1, "period_seconds": 5, "initial_delay_seconds": 5}')

_assert_probes_on_every_container "$rendered" 3
}

@test "health check disabled: containers and their ports survive" {
rendered=$(_render "$(_health_check false HTTP)")

assert_equal "$(echo "$rendered" | yq -N '[.spec.template.spec.containers[].name] | join(",")')" \
"http,http-8081,grpc-9012,application"
assert_equal "$(echo "$rendered" | yq -N '[.spec.template.spec.containers[] | select(.name == "application") | .ports[].containerPort] | join(",")')" \
"8080,8081,9012"
assert_equal "$(echo "$rendered" | yq -N '.spec.template.spec.containers[] | select(.name == "http") | .ports[0].containerPort')" \
"80"
}

@test "health check disabled: the application keeps its graceful shutdown hook" {
rendered=$(_render "$(_health_check false HTTP)")

assert_equal "$(echo "$rendered" | yq -N '.spec.template.spec.containers[] | select(.name == "application") | .lifecycle.preStop.exec.command | join(" ")')" \
"/bin/sleep 16"
}
123 changes: 123 additions & 0 deletions k8s/deployment/tests/ingress_domains_additional_ports.bats
Original file line number Diff line number Diff line change
@@ -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
}
Loading