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])


Reply via email to