This is an automated email from the ASF dual-hosted git repository.
Miretpl pushed a commit to branch chart/v1-2x-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/chart/v1-2x-test by this push:
new 0ce1bfa9a24 Improve API server HTTPRoute configuration (#68552)
(#69579)
0ce1bfa9a24 is described below
commit 0ce1bfa9a24806cfdee340421468516b3182bde2
Author: Steve Ahn <[email protected]>
AuthorDate: Thu Jul 9 00:29:12 2026 -0700
Improve API server HTTPRoute configuration (#68552) (#69579)
* Move API server HTTPRoute under apiServer and add config guards
* Require HTTPRoute parentRefs and document API server Ingress mutual
exclusivity
* Fail fast on empty HTTPRoute parentRefs via schema minItems
* Apply pretty-format-json formatting to values.schema.json
(cherry picked from commit d2d9f4c2d837146895cddf48f9debbbb49008ce3)
Co-authored-by: somaz <[email protected]>
---
chart/docs/production-guide.rst | 14 +-
.../templates/api-server/api-server-httproute.yaml | 29 ++-
chart/templates/api-server/api-server-ingress.yaml | 3 +
chart/values.schema.json | 196 +++++++++----------
chart/values.yaml | 94 ++++-----
.../tests/chart_utils/helm_template_generator.py | 3 +
.../apiserver/test_httproute_apiserver.py | 215 ++++++++++++++++-----
7 files changed, 344 insertions(+), 210 deletions(-)
diff --git a/chart/docs/production-guide.rst b/chart/docs/production-guide.rst
index 3b5b9781058..2c5994bc22d 100644
--- a/chart/docs/production-guide.rst
+++ b/chart/docs/production-guide.rst
@@ -406,8 +406,8 @@ the chart only creates the ``HTTPRoute`` and attaches it to
the Gateway via ``pa
.. code-block:: yaml
:caption: values.yaml
- httpRoute:
- apiServer:
+ apiServer:
+ httpRoute:
enabled: true
parentRefs:
- name: main-gateway
@@ -416,9 +416,17 @@ the chart only creates the ``HTTPRoute`` and attaches it
to the Gateway via ``pa
hostnames:
- airflow.example.com
-For fine-grained routing, supply ``httpRoute.apiServer.rules`` directly — the
entry mirrors the
+For fine-grained routing, supply ``apiServer.httpRoute.rules`` directly — the
entry mirrors the
upstream ``HTTPRouteRule`` schema and overrides the default rule generated
from ``path`` + ``pathType``.
+.. note::
+
+ ``HTTPRoute`` is an alternative to the API server ``Ingress``, so enable
only one of
+ ``ingress.apiServer`` or ``apiServer.httpRoute`` — enabling both at the
same time fails template
+ rendering. When ``apiServer.httpRoute.enabled`` is ``true``, the chart also
verifies (via Helm
+ ``Capabilities``) that the Gateway API CRDs are installed and fails with a
clear message if they
+ are not.
+
LoadBalancer Service
^^^^^^^^^^^^^^^^^^^^
diff --git a/chart/templates/api-server/api-server-httproute.yaml
b/chart/templates/api-server/api-server-httproute.yaml
index c30f26263d5..5bdb0103c13 100644
--- a/chart/templates/api-server/api-server-httproute.yaml
+++ b/chart/templates/api-server/api-server-httproute.yaml
@@ -20,7 +20,16 @@
##################################
## Airflow API Server HTTPRoute
##################################
-{{- if and .Values.apiServer.enabled .Values.httpRoute.apiServer.enabled }}
+{{- if and .Values.apiServer.enabled .Values.apiServer.httpRoute.enabled }}
+{{- if .Values.ingress.apiServer.enabled }}
+{{- fail "`apiServer.httpRoute.enabled` and `ingress.apiServer.enabled` are
both set to `true`. HTTPRoute (Gateway API) is an alternative to the API server
Ingress; enable only one of them." }}
+{{- end }}
+{{- if not (.Capabilities.APIVersions.Has "gateway.networking.k8s.io/v1") }}
+{{- fail "`apiServer.httpRoute.enabled` is `true` but the Gateway API
HTTPRoute CRD (`gateway.networking.k8s.io/v1`) is not installed in the cluster.
Install the Gateway API CRDs
(https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api) before
enabling `apiServer.httpRoute`." }}
+{{- end }}
+{{- if not .Values.apiServer.httpRoute.parentRefs }}
+{{- fail "`apiServer.httpRoute.enabled` is `true` but
`apiServer.httpRoute.parentRefs` is empty. An HTTPRoute must reference at least
one parent Gateway; set `apiServer.httpRoute.parentRefs`." }}
+{{- end }}
{{- $fullname := include "airflow.fullname" . }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
@@ -32,28 +41,28 @@ metadata:
release: {{ .Release.Name }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
heritage: {{ .Release.Service }}
- {{- if or .Values.labels .Values.apiServer.labels
.Values.httpRoute.apiServer.labels }}
- {{- mustMerge .Values.httpRoute.apiServer.labels
.Values.apiServer.labels .Values.labels | toYaml | nindent 4 }}
+ {{- if or .Values.labels .Values.apiServer.labels
.Values.apiServer.httpRoute.labels }}
+ {{- mustMerge .Values.apiServer.httpRoute.labels
.Values.apiServer.labels .Values.labels | toYaml | nindent 4 }}
{{- end }}
- {{- with .Values.httpRoute.apiServer.annotations }}
+ {{- with .Values.apiServer.httpRoute.annotations }}
annotations: {{- toYaml . | nindent 4 }}
{{- end }}
spec:
- parentRefs: {{- toYaml .Values.httpRoute.apiServer.parentRefs | nindent 4 }}
- {{- with .Values.httpRoute.apiServer.hostnames }}
+ parentRefs: {{- toYaml .Values.apiServer.httpRoute.parentRefs | nindent 4 }}
+ {{- with .Values.apiServer.httpRoute.hostnames }}
hostnames:
{{- range . }}
- {{ tpl . $ | quote }}
{{- end }}
{{- end }}
rules:
- {{- if .Values.httpRoute.apiServer.rules }}
- {{- toYaml .Values.httpRoute.apiServer.rules | nindent 4 }}
+ {{- if .Values.apiServer.httpRoute.rules }}
+ {{- toYaml .Values.apiServer.httpRoute.rules | nindent 4 }}
{{- else }}
- matches:
- path:
- type: {{ .Values.httpRoute.apiServer.pathType }}
- value: {{ .Values.httpRoute.apiServer.path | quote }}
+ type: {{ .Values.apiServer.httpRoute.pathType }}
+ value: {{ .Values.apiServer.httpRoute.path | quote }}
backendRefs:
- name: {{ $fullname }}-api-server
port: {{ .Values.ports.apiServer }}
diff --git a/chart/templates/api-server/api-server-ingress.yaml
b/chart/templates/api-server/api-server-ingress.yaml
index 9e530d74cd8..53d3b8fc2c0 100644
--- a/chart/templates/api-server/api-server-ingress.yaml
+++ b/chart/templates/api-server/api-server-ingress.yaml
@@ -21,6 +21,9 @@
## Airflow API Server Ingress
#################################
{{- if and .Values.apiServer.enabled (semverCompare ">=3.0.0"
.Values.airflowVersion) (or .Values.ingress.apiServer.enabled
.Values.ingress.enabled) }}
+{{- if .Values.apiServer.httpRoute.enabled }}
+{{- fail "`ingress.apiServer.enabled` and `apiServer.httpRoute.enabled` are
both set to `true`. The API server Ingress is an alternative to HTTPRoute
(Gateway API); enable only one of them." }}
+{{- end }}
{{- $fullname := include "airflow.fullname" . }}
apiVersion: networking.k8s.io/v1
kind: Ingress
diff --git a/chart/values.schema.json b/chart/values.schema.json
index 58cafd6ca05..ff6e7bdc2a5 100644
--- a/chart/values.schema.json
+++ b/chart/values.schema.json
@@ -764,106 +764,6 @@
}
}
},
- "httpRoute": {
- "description": "Kubernetes Gateway API (HTTPRoute) configuration.",
- "type": "object",
- "x-docsSection": "Ingress",
- "additionalProperties": false,
- "properties": {
- "apiServer": {
- "description": "Configuration for the HTTPRoute of the API
server.",
- "type": "object",
- "additionalProperties": false,
- "properties": {
- "enabled": {
- "description": "Enable API server HTTPRoute
resource.",
- "type": "boolean",
- "default": false
- },
- "labels": {
- "description": "Extra labels for the API server
HTTPRoute.",
- "type": "object",
- "default": {},
- "additionalProperties": {
- "type": "string"
- }
- },
- "annotations": {
- "description": "Annotations for the API server
HTTPRoute.",
- "type": "object",
- "default": {},
- "additionalProperties": {
- "type": "string"
- }
- },
- "parentRefs": {
- "description": "List of parent Gateway references
this HTTPRoute attaches to. Required when enabled.",
- "type": "array",
- "default": [],
- "items": {
- "type": "object",
- "properties": {
- "name": {
- "description": "Name of the referenced
Gateway.",
- "type": "string"
- },
- "namespace": {
- "description": "Namespace of the
referenced Gateway. Defaults to the local namespace.",
- "type": "string"
- },
- "sectionName": {
- "description": "Name of the Gateway
listener section this route attaches to.",
- "type": "string"
- },
- "port": {
- "description": "Port of the Gateway
listener this route attaches to.",
- "type": "integer"
- },
- "kind": {
- "description": "Kind of the referenced
resource. Defaults to `Gateway`.",
- "type": "string"
- },
- "group": {
- "description": "API group of the
referenced resource. Defaults to `gateway.networking.k8s.io`.",
- "type": "string"
- }
- },
- "required": [
- "name"
- ]
- }
- },
- "hostnames": {
- "description": "Hostnames this HTTPRoute should
match (templated). Empty matches all.",
- "type": "array",
- "default": [],
- "items": {
- "type": "string"
- }
- },
- "path": {
- "description": "Default routing rule path (used
only when `httpRoute.apiServer.rules` is empty).",
- "type": "string",
- "default": "/"
- },
- "pathType": {
- "description": "The pathType for the default path:
PathPrefix, Exact, or RegularExpression.",
- "type": "string",
- "default": "PathPrefix"
- },
- "rules": {
- "description": "Custom routing rules. When set,
overrides the default rule generated from `path` + `pathType`. Each entry
follows the upstream Gateway API `HTTPRouteRule` schema.",
- "type": "array",
- "default": [],
- "items": {
- "type": "object",
- "additionalProperties": {}
- }
- }
- }
- }
- }
- },
"networkPolicies": {
"description": "Network policy configuration.",
"type": "object",
@@ -7145,6 +7045,102 @@
"type": "boolean",
"default": true
},
+ "httpRoute": {
+ "description": "Kubernetes Gateway API (HTTPRoute)
configuration for the API server.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {
+ "description": "Enable API server HTTPRoute
resource.",
+ "type": "boolean",
+ "default": false
+ },
+ "labels": {
+ "description": "Extra labels for the API server
HTTPRoute.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "annotations": {
+ "description": "Annotations for the API server
HTTPRoute.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "parentRefs": {
+ "description": "List of parent Gateway references
this HTTPRoute attaches to. Required when enabled.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "default": null,
+ "minItems": 1,
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "description": "Name of the referenced
Gateway.",
+ "type": "string"
+ },
+ "namespace": {
+ "description": "Namespace of the
referenced Gateway. Defaults to the local namespace.",
+ "type": "string"
+ },
+ "sectionName": {
+ "description": "Name of the Gateway
listener section this route attaches to.",
+ "type": "string"
+ },
+ "port": {
+ "description": "Port of the Gateway
listener this route attaches to.",
+ "type": "integer"
+ },
+ "kind": {
+ "description": "Kind of the referenced
resource. Defaults to `Gateway`.",
+ "type": "string"
+ },
+ "group": {
+ "description": "API group of the
referenced resource. Defaults to `gateway.networking.k8s.io`.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ]
+ }
+ },
+ "hostnames": {
+ "description": "Hostnames this HTTPRoute should
match (templated). Empty matches all.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "string"
+ }
+ },
+ "path": {
+ "description": "Default routing rule path (used
only when `apiServer.httpRoute.rules` is empty).",
+ "type": "string",
+ "default": "/"
+ },
+ "pathType": {
+ "description": "The pathType for the default path:
PathPrefix, Exact, or RegularExpression.",
+ "type": "string",
+ "default": "PathPrefix"
+ },
+ "rules": {
+ "description": "Custom routing rules. When set,
overrides the default rule generated from `path` + `pathType`. Each entry
follows the upstream Gateway API `HTTPRouteRule` schema.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "additionalProperties": {}
+ }
+ }
+ }
+ },
"configMapAnnotations": {
"description": "Extra annotations to apply to the API
server configmap.",
"type": "object",
diff --git a/chart/values.yaml b/chart/values.yaml
index 5135433241d..ef45f9244cb 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -155,7 +155,8 @@ ingress:
# Configs for the Ingress of the API Server (Airflow 3+)
apiServer:
- # Enable API Server ingress resource
+ # Enable API Server ingress resource.
+ # Mutually exclusive with `apiServer.httpRoute.enabled` (Gateway API);
enable only one of them.
enabled: false
# Annotations for the API Server Ingress
@@ -333,50 +334,6 @@ ingress:
# The Ingress Class for the PgBouncer Ingress
ingressClassName: ""
-# Kubernetes Gateway API (HTTPRoute) configuration.
-# Requires the Gateway API CRDs to be installed in the cluster
-# (https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api).
-# The HTTPRoute resources reference an externally managed Gateway via
`parentRefs`.
-httpRoute:
- # Configs for the HTTPRoute of the API Server (Airflow 3+)
- apiServer:
- # Enable API Server HTTPRoute resource
- enabled: false
-
- # Extra labels for the API Server HTTPRoute
- labels: {}
-
- # Annotations for the API Server HTTPRoute
- annotations: {}
-
- # List of parent Gateway references this HTTPRoute attaches to. Required
when enabled.
- # See
https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
- parentRefs: []
- # - name: my-gateway
- # namespace: gateway-system
- # sectionName: https
-
- # Hostnames this HTTPRoute should match (templated). Empty matches all.
- hostnames: []
- # - "airflow.example.com"
-
- # Default routing rule path (used only when `httpRoute.apiServer.rules` is
empty)
- path: "/"
-
- # The pathType for the default path. PathPrefix | Exact | RegularExpression
- pathType: PathPrefix
-
- # Custom routing rules. When set, overrides the default rule generated
from `path` + `pathType`.
- # See
https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
- rules: []
- # - matches:
- # - path:
- # type: PathPrefix
- # value: /api/
- # backendRefs:
- # - name: my-release-api-server
- # port: 8080
-
# Network policy configuration
networkPolicies:
# Enabled network policies
@@ -2315,6 +2272,53 @@ migrateDatabaseJob:
apiServer:
enabled: true
+ # Kubernetes Gateway API (HTTPRoute) configuration for the API server.
+ # Requires the Gateway API CRDs to be installed in the cluster
+ # (https://gateway-api.sigs.k8s.io/guides/#installing-gateway-api).
+ # The HTTPRoute references an externally managed Gateway via `parentRefs`.
+ # This is an alternative to the API server Ingress (`ingress.apiServer`);
+ # the two are mutually exclusive and enabling both will fail rendering.
+ httpRoute:
+ # Enable API Server HTTPRoute resource.
+ # Mutually exclusive with `ingress.apiServer.enabled`; enable only one of
them.
+ enabled: false
+
+ # Extra labels for the API Server HTTPRoute
+ labels: {}
+
+ # Annotations for the API Server HTTPRoute
+ annotations: {}
+
+ # List of parent Gateway references this HTTPRoute attaches to. Required
when enabled.
+ # Defaults to `~` (null) so the schema's `minItems: 1` fails fast on an
explicitly
+ # empty list (`[]`); a still-null value while enabled is caught by the
template guard.
+ # See
https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.ParentReference
+ parentRefs: ~
+ # - name: my-gateway
+ # namespace: gateway-system
+ # sectionName: https
+
+ # Hostnames this HTTPRoute should match (templated). Empty matches all.
+ hostnames: []
+ # - "airflow.example.com"
+
+ # Default routing rule path (used only when `apiServer.httpRoute.rules` is
empty)
+ path: "/"
+
+ # The pathType for the default path. PathPrefix | Exact | RegularExpression
+ pathType: PathPrefix
+
+ # Custom routing rules. When set, overrides the default rule generated
from `path` + `pathType`.
+ # See
https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
+ rules: []
+ # - matches:
+ # - path:
+ # type: PathPrefix
+ # value: /api/
+ # backendRefs:
+ # - name: my-release-api-server
+ # port: 8080
+
# Number of Airflow API servers in the Deployment.
# Omitted from the Deployment, when HPA is enabled.
replicas: 1
diff --git a/helm-tests/tests/chart_utils/helm_template_generator.py
b/helm-tests/tests/chart_utils/helm_template_generator.py
index 91e1213542d..7891b1be3c6 100644
--- a/helm-tests/tests/chart_utils/helm_template_generator.py
+++ b/helm-tests/tests/chart_utils/helm_template_generator.py
@@ -141,6 +141,7 @@ def render_chart(
chart_dir=None,
kubernetes_version=DEFAULT_KUBERNETES_VERSION,
namespace=None,
+ api_versions=None,
):
"""
Function that renders a helm chart into dictionaries. For helm chart
testing only
@@ -164,6 +165,8 @@ def render_chart(
"--namespace",
namespace,
]
+ for api_version in api_versions or []:
+ command.extend(["--api-versions", api_version])
if show_only:
for i in show_only:
command.extend(["--show-only", i])
diff --git a/helm-tests/tests/helm_tests/apiserver/test_httproute_apiserver.py
b/helm-tests/tests/helm_tests/apiserver/test_httproute_apiserver.py
index 057cac40d82..3f0b826d6e6 100644
--- a/helm-tests/tests/helm_tests/apiserver/test_httproute_apiserver.py
+++ b/helm-tests/tests/helm_tests/apiserver/test_httproute_apiserver.py
@@ -18,24 +18,38 @@ from __future__ import annotations
import jmespath
import pytest
-from chart_utils.helm_template_generator import render_chart
+from chart_utils.helm_template_generator import HelmFailedError, render_chart
SHOW_ONLY = ["templates/api-server/api-server-httproute.yaml"]
+# The template is gated on the Gateway API CRD being installed
+# (`.Capabilities.APIVersions.Has`). Pass it explicitly so `helm template`
+# can render the resource offline.
+GATEWAY_API_VERSIONS = ["gateway.networking.k8s.io/v1"]
+
+# A minimal valid `parentRefs`; the HTTPRoute now requires at least one parent
+# Gateway reference when enabled, so rendering tests must supply it.
+MINIMAL_PARENT_REFS = [{"name": "main-gateway"}]
+
class TestHTTPRouteAPIServer:
"""Tests HTTPRoute API Server (Kubernetes Gateway API)."""
- def test_should_pass_validation_with_just_httproute_enabled(self):
- render_chart(
- values={"httpRoute": {"apiServer": {"enabled": True}}},
+ def test_should_pass_validation_with_minimal_config(self):
+ docs = render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": True, "parentRefs":
MINIMAL_PARENT_REFS}}},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
+ assert len(docs) == 1
+ assert jmespath.search("kind", docs[0]) == "HTTPRoute"
+ assert jmespath.search("metadata.name", docs[0]) ==
"release-name-api-server-httproute"
def test_should_set_api_version_and_kind(self):
docs = render_chart(
- values={"httpRoute": {"apiServer": {"enabled": True}}},
+ values={"apiServer": {"httpRoute": {"enabled": True, "parentRefs":
MINIMAL_PARENT_REFS}}},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search("apiVersion", docs[0]) ==
"gateway.networking.k8s.io/v1"
assert jmespath.search("kind", docs[0]) == "HTTPRoute"
@@ -43,28 +57,40 @@ class TestHTTPRouteAPIServer:
def test_should_allow_more_than_one_annotation(self):
docs = render_chart(
values={
- "httpRoute": {
- "apiServer": {"enabled": True, "annotations": {"aa": "bb",
"cc": "dd"}},
+ "apiServer": {
+ "httpRoute": {
+ "enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
+ "annotations": {"aa": "bb", "cc": "dd"},
+ },
},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search("metadata.annotations", docs[0]) == {"aa":
"bb", "cc": "dd"}
def test_should_add_extra_labels(self):
docs = render_chart(
values={
- "httpRoute": {"apiServer": {"enabled": True, "labels":
{"custom": "value"}}},
+ "apiServer": {
+ "httpRoute": {
+ "enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
+ "labels": {"custom": "value"},
+ },
+ },
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search('metadata.labels."custom"', docs[0]) == "value"
def test_should_pass_parent_refs_through(self):
docs = render_chart(
values={
- "httpRoute": {
- "apiServer": {
+ "apiServer": {
+ "httpRoute": {
"enabled": True,
"parentRefs": [
{"name": "main-gateway", "namespace":
"gateway-system"},
@@ -74,21 +100,26 @@ class TestHTTPRouteAPIServer:
},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
- assert jmespath.search("spec.parentRefs[*].name", docs[0]) ==
["main-gateway", "main-gateway"]
- assert jmespath.search("spec.parentRefs[1].sectionName", docs[0]) ==
"https"
+ assert jmespath.search("spec.parentRefs", docs[0]) == [
+ {"name": "main-gateway", "namespace": "gateway-system"},
+ {"name": "main-gateway", "namespace": "gateway-system",
"sectionName": "https"},
+ ]
def test_should_set_hostnames(self):
docs = render_chart(
values={
- "httpRoute": {
- "apiServer": {
+ "apiServer": {
+ "httpRoute": {
"enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
"hostnames": ["airflow.example.com",
"airflow2.example.com"],
},
},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search("spec.hostnames", docs[0]) == [
"airflow.example.com",
@@ -99,92 +130,165 @@ class TestHTTPRouteAPIServer:
docs = render_chart(
name="airflow",
values={
- "httpRoute": {
- "apiServer": {
+ "apiServer": {
+ "httpRoute": {
"enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
"hostnames": ["{{ .Release.Name }}.example.com"],
},
},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search("spec.hostnames", docs[0]) ==
["airflow.example.com"]
def test_should_default_to_path_prefix_and_api_server_backend(self):
docs = render_chart(
name="my-release",
- values={"httpRoute": {"apiServer": {"enabled": True}}},
+ values={"apiServer": {"httpRoute": {"enabled": True, "parentRefs":
MINIMAL_PARENT_REFS}}},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
- assert jmespath.search("spec.rules[0].matches[0].path.type", docs[0])
== "PathPrefix"
- assert jmespath.search("spec.rules[0].matches[0].path.value", docs[0])
== "/"
- assert jmespath.search("spec.rules[0].backendRefs[0].name", docs[0])
== "my-release-api-server"
- assert jmespath.search("spec.rules[0].backendRefs[0].port", docs[0])
== 8080
+ assert jmespath.search("spec.rules", docs[0]) == [
+ {
+ "matches": [{"path": {"type": "PathPrefix", "value": "/"}}],
+ "backendRefs": [{"name": "my-release-api-server", "port":
8080}],
+ },
+ ]
def test_custom_path_and_path_type_should_apply(self):
docs = render_chart(
values={
- "httpRoute": {
- "apiServer": {"enabled": True, "path": "/api", "pathType":
"Exact"},
+ "apiServer": {
+ "httpRoute": {
+ "enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
+ "path": "/api",
+ "pathType": "Exact",
+ },
},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert jmespath.search("spec.rules[0].matches[0].path.type", docs[0])
== "Exact"
assert jmespath.search("spec.rules[0].matches[0].path.value", docs[0])
== "/api"
def test_custom_rules_override_default_rule(self):
- custom_rules = [
+ docs = render_chart(
+ values={
+ "apiServer": {
+ "httpRoute": {
+ "enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
+ "rules": [
+ {
+ "matches": [{"path": {"type": "PathPrefix",
"value": "/admin"}}],
+ "backendRefs": [{"name": "external-admin",
"port": 8443}],
+ },
+ ],
+ },
+ },
+ },
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ assert jmespath.search("spec.rules", docs[0]) == [
{
"matches": [{"path": {"type": "PathPrefix", "value":
"/admin"}}],
"backendRefs": [{"name": "external-admin", "port": 8443}],
},
]
+
+ def test_httproute_not_created_when_unset(self):
docs = render_chart(
- values={
- "httpRoute": {
- "apiServer": {"enabled": True, "rules": custom_rules},
- },
- },
+ values={"apiServer": {"httpRoute": {}}},
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ assert docs == []
+
+ def test_httproute_not_created_when_disabled(self):
+ docs = render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": False}}},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
- assert jmespath.search("spec.rules[0].matches[0].path.value", docs[0])
== "/admin"
- assert jmespath.search("spec.rules[0].backendRefs[0].name", docs[0])
== "external-admin"
- assert jmespath.search("spec.rules[0].backendRefs[0].port", docs[0])
== 8443
-
- @pytest.mark.parametrize(
- ("api_server_value", "expected"),
- [
- (None, False),
- (False, False),
- (True, True),
- ],
- )
- def test_httproute_created(self, api_server_value, expected):
- values = {"httpRoute": {}}
- if api_server_value is not None:
- values["httpRoute"]["apiServer"] = {"enabled": api_server_value}
- docs = render_chart(values=values, show_only=SHOW_ONLY)
- assert bool(docs) is expected
+ assert docs == []
+
+ def test_httproute_created_when_enabled(self):
+ docs = render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": True, "parentRefs":
MINIMAL_PARENT_REFS}}},
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ assert len(docs) == 1
def test_should_not_render_when_api_server_disabled(self):
docs = render_chart(
values={
- "apiServer": {"enabled": False},
- "httpRoute": {"apiServer": {"enabled": True}},
+ "apiServer": {"enabled": False, "httpRoute": {"enabled":
True}},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert docs == []
+ def test_should_fail_when_gateway_api_crd_missing(self):
+ # No api_versions -> the Gateway API CRD is treated as not installed.
+ with pytest.raises(HelmFailedError) as exc_info:
+ render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": True}}},
+ show_only=SHOW_ONLY,
+ )
+ assert "Gateway API HTTPRoute CRD" in exc_info.value.stderr.decode()
+
+ def test_should_fail_when_parent_refs_empty(self):
+ # Gateway API CRD present, but parentRefs left at its `~` default ->
the template
+ # guard catches the still-null value while enabled.
+ with pytest.raises(HelmFailedError) as exc_info:
+ render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": True}}},
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ assert "parentRefs" in exc_info.value.stderr.decode()
+
+ def test_should_fail_schema_when_parent_refs_explicitly_empty(self):
+ # An explicitly empty `parentRefs: []` violates the schema's
`minItems: 1` and
+ # fails fast during values validation, before any template guard runs.
+ with pytest.raises(HelmFailedError) as exc_info:
+ render_chart(
+ values={"apiServer": {"httpRoute": {"enabled": True,
"parentRefs": []}}},
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ assert "parentRefs" in exc_info.value.stderr.decode()
+
+ def test_should_fail_when_ingress_and_httproute_both_enabled(self):
+ with pytest.raises(HelmFailedError) as exc_info:
+ render_chart(
+ values={
+ "ingress": {"apiServer": {"enabled": True}},
+ "apiServer": {"httpRoute": {"enabled": True}},
+ },
+ show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
+ )
+ # Either reciprocal guard (httproute-side or ingress-side) may fire
first;
+ # both share this closing phrase.
+ assert "enable only one of them" in exc_info.value.stderr.decode()
+
def test_backend_service_name_with_fullname_override(self):
docs = render_chart(
values={
"fullnameOverride": "airflow-fullname-override",
"useStandardNaming": True,
- "httpRoute": {"apiServer": {"enabled": True}},
+ "apiServer": {"httpRoute": {"enabled": True, "parentRefs":
MINIMAL_PARENT_REFS}},
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert (
jmespath.search("spec.rules[0].backendRefs[0].name", docs[0])
@@ -195,10 +299,17 @@ class TestHTTPRouteAPIServer:
docs = render_chart(
values={
"labels": {"label1": "value1", "label2": "value2"},
- "apiServer": {"labels": {"test_label": "test_label_value"}},
- "httpRoute": {"apiServer": {"enabled": True, "labels":
{"route_label": "route_value"}}},
+ "apiServer": {
+ "labels": {"test_label": "test_label_value"},
+ "httpRoute": {
+ "enabled": True,
+ "parentRefs": MINIMAL_PARENT_REFS,
+ "labels": {"route_label": "route_value"},
+ },
+ },
},
show_only=SHOW_ONLY,
+ api_versions=GATEWAY_API_VERSIONS,
)
assert "tier" in jmespath.search("metadata.labels", docs[0])
assert "release" in jmespath.search("metadata.labels", docs[0])