Skip to content

Operator Changes for K8s Gateway API Conformance#2168

Open
O-sura wants to merge 5 commits into
wso2:mainfrom
O-sura:gw-api-conformance
Open

Operator Changes for K8s Gateway API Conformance#2168
O-sura wants to merge 5 commits into
wso2:mainfrom
O-sura:gw-api-conformance

Conversation

@O-sura

@O-sura O-sura commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Purpose

This PR adds the changes required for making the current api-platform kubernetes operator compatible with the gateway-api

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Validation Results

Dependency name: k8s.io/api
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/apimachinery
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/client-go
Version: v0.35.1 (was v0.33.3)
Allowed range: >=v0.33.3
Approved: ✅ Yes

Dependency name: sigs.k8s.io/gateway-api
Version: v1.5.1 (was v1.3.0)
Approved: ❌ No - Module not found in dependency registry


Next Steps

  1. Review the validation failures listed above
  2. Check if dependencies are in the approved dependency list
  3. Options to resolve:
    • Remove the unapproved dependencies from this PR
    • OR submit a PR to add these dependencies to the approved list in engineering-governance
  4. Once resolved, push changes to re-run validation

This PR is blocked until all dependencies are approved.

⚠️ Please verify the scope of the dependencies usage is necessary

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary

This PR introduces comprehensive changes to make the api-platform Kubernetes operator compatible with the Gateway API specification. The work encompasses operator API extensions, controller implementations, runtime model enhancements, and xDS translation updates to support Gateway API-style routing constructs.

Key Changes

API and Schema Extensions

  • Management API (management-openapi.yaml): Added support for multiple virtual hosts via vhostList property and extended operation-level routing with path matching type (Exact/PathPrefix), header-based matching, and direct HTTP response definitions
  • RestApi CRD (restapi_types.go): Extended with vhost lists, upstream definitions, weighted upstream targets, optional path match types, header matchers, and direct responses
  • Generated Models: Updated Go models in management API and internal packages to reflect new routing primitives and enum types

Runtime and Translation Layer

  • Runtime Config Models (runtime_deploy_config.go): Added header match discriminators, direct-response definitions, weighted endpoints, and deterministic route ordering
  • RestAPI Transformer (restapi.go): Enhanced to build header-discriminated route keys, support expanded vhost lists, handle weighted upstreams, and parse all upstream URLs (not just the first) with optional weights
  • XDS Translator (translator.go): Implemented discriminator-aware deterministic route naming, header-based route disambiguation, direct-response rendering with redirect actions, and exact path matching via native Envoy matchers

Gateway Controller Implementation

Introduced multiple new controller modules for Gateway API conformance:

  • Address Resolution (gateway_addresses.go): Derives Gateway status addresses from fronting Kubernetes Services (LoadBalancer, NodePort, ExternalName, ClusterIP)
  • HTTPRoute Attachment (httproute_attachment.go): Validates HTTPRoute attachment to gateways with hostname intersection matching (including wildcard support)
  • HTTPRoute Backend Resolution (httproute_backends.go): Resolves backend service references with port computation, weight handling, and cross-namespace ReferenceGrant validation
  • HTTPRoute Compilation (httproute_compile.go): Builds APIConfigData from Gateway API HTTPRoute objects, including vhost derivation, operation generation, policy attachment, and weighted upstream handling
  • Listener Validation (gateway_listener_validation.go): Validates listener protocol support, TLS certificate references, and computes per-listener status conditions
  • Listener TLS Overlay (gateway_listeners_overlay.go): Applies HTTPS TLS configuration from listener certificate secrets to Helm values
  • Attached Routes Tracking (gateway_attached_routes.go): Counts accepted HTTPRoutes attached to each gateway listener

Gateway Reconciliation Enhancements

  • HTTPRoute Reconciler (httproute_controller.go): Extended to support multiple Gateway parentRefs with per-parent validation, backend resolution, and status patching; added deterministic RestApi payload hashing to prevent unnecessary gateway redeployments
  • Gateway Reconciler (k8s_gateway_controller.go): Enhanced with proactive status updates, parametersRef validation, TLS overlay application, gateway-runtime service discovery, and LoadBalancer address resolution
  • Controller Wiring: Added watchers for ReferenceGrant resources and TLS Certificate Secrets; extended RBAC to include namespace access

Additional Updates

  • Helm Release Naming (helm/client.go): Made deterministic and DNS-1123 compliant with fallback to hashed names for long gateway names
  • Gateway API CRDs: Consolidated standard CRD files into a single bundled manifest (standard-crds.yaml); removed separate enumerated CRD files
  • Dependencies: Updated Kubernetes API libraries to v0.35.1 and Gateway API to v1.5.1 with corresponding transitive dependency refreshes

Testing

Comprehensive test coverage added for:

  • Route key generation with header discriminators
  • Header match collision detection
  • RestAPI transformer cluster references
  • Gateway address resolution
  • HTTPRoute attachment logic with hostname matching
  • Backend reference resolution
  • TLS certificate validation
  • Listener evaluation
  • HTTPRoute compilation with weighted backends and policy resolution
  • Helm release naming determinism

Impact

The PR enables the operator to manage Kubernetes gateways using Gateway API HTTPRoute resources as the primary configuration mechanism, with full support for advanced routing features (header matching, path matching, direct responses, weighted backends) while maintaining backward compatibility through deterministic configuration hashing and dependency management.

Walkthrough

This PR extends the gateway stack with advanced routing primitives and a new Gateway API–native control plane. On the data plane side, the OpenAPI management schema, generated Go models, and Kubernetes CRD types gain vhostList, per-operation pathMatchType, matchHeaders, directResponse, and weighted upstreamDefinitions. The xDS translator is updated to emit discriminator-aware route keys (SHA-256 hash of canonically sorted header matchers), native Envoy exact-path matchers, header-match conditions, direct-response and redirect actions, and deterministic route ordering. On the operator side, new Gateway API controllers implement full HTTPRoute reconciliation with multi-parent validation, backend reference resolution, per-parent condition patching, payload-hash-based redeploy suppression, Gateway listener validation (TLS certificate resolution, ReferenceGrant checks), address derivation from the runtime Service, and Helm TLS overlay from listener certificateRefs. Individual CRD YAML files are consolidated into a single bundle, and Helm release name generation is made DNS-1123 compliant and length-bounded.

Suggested reviewers

  • RakhithaRR
  • Tharsanan1
  • VirajSalaka
  • renuka-fernando
  • Krishanx92
  • CrowleyRajapakse
  • HeshanSudarshana
  • HiranyaKavishani
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@O-sura O-sura changed the title Gw api conformance Operator Changes for K8s Gateway API Conformance Jun 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
kubernetes/gateway-operator/internal/controller/httproute_controller.go (1)

215-239: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clean up the current gateway deployment on non-retryable build failures.

If BuildAPIConfigFromHTTPRoute starts failing after a route was already deployed, this branch only patches status and returns. The previously deployed RestApi stays active on the Gateway, so runtime state can drift from the HTTPRoute. For invalid/non-retryable config errors, delete the current deployment before exiting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/controller/httproute_controller.go`
around lines 215 - 239, On non-retryable build failures (i.e., when
BuildAPIConfigFromHTTPRoute returns an error and requeueAfter==0 — typically
IsHTTPRouteBackendRefError or IsInvalidHTTPRouteConfigError), delete the
currently deployed RestApi on the Gateway so runtime state doesn't drift: after
calling r.patchResolvedRefsForParents(...) and before returning (inside the same
error branch), invoke a deletion helper such as r.deleteGatewayRestAPI(ctx,
parentGW, route) (create this helper if it doesn't exist), log any deletion
error via log.Error, and then return the existing ctrl.Result{}; keep transient
errors (requeueAfter>0) behavior unchanged.
kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml (1)

2-19: ⚠️ Potential issue | 🟠 Major

Fix Helm CRD bundle gating to be per-CRD

  • Line 6 builds a single $crd from files/gateway-api-standard/standard-crds.yaml, and the template does only one lookup using $crd.metadata.name.
  • Line 19 then emits the entire standard-crds.yaml bundle (the bundle contains multiple CustomResourceDefinition documents), so the ownership/existence decision is applied to the whole bundle rather than each CRD document.
  • Split the bundle into individual CRD documents and perform the lookup/ownership check per CRD before emitting each one.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml` around
lines 2 - 19, The template currently parses the entire standard-crds.yaml into a
single $crd and runs lookup/ownership checks once, then emits the whole bundle;
instead, parse the file into individual CRD documents and iterate them so lookup
and the $ownedByThisRelease gating happen per document: use fromYaml
($.Files.Get $p) as a list (range over each item, e.g., $doc), perform lookup
"apiextensions.k8s.io/v1" "CustomResourceDefinition" "" $doc.metadata.name and
the same annotation/label checks for each $doc, and emit each CRD individually
(e.g., toYaml $doc) only when its per-CRD check passes. Ensure you keep the same
variables ($paths, $p) and preserve existing annotation/label checks but applied
inside the per-document loop.
🟡 Minor comments (2)
kubernetes/gateway-operator/api/v1alpha1/restapi_types.go-280-288 (1)

280-288: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Enforce the documented weight range.

Line 286 documents Weight as 0-100, but the CRD does not validate that range. Invalid weights would still be accepted and can produce unexpected routing behavior. Add the matching min/max validation.

Suggested change
 type WeightedUpstream struct {
 	// Url Backend URL (host and port; no path)
 	// +kubebuilder:validation:Required
 	Url string `json:"url"`

 	// Weight Load balancing weight (0-100)
 	// +optional
+	// +kubebuilder:validation:Minimum=0
+	// +kubebuilder:validation:Maximum=100
 	Weight *int `json:"weight,omitempty"`
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/api/v1alpha1/restapi_types.go` around lines 280 -
288, The Weight field in the WeightedUpstream struct lacks CRD validation for
the documented 0-100 range; add kubebuilder validation annotations to enforce
Minimum=0 and Maximum=100 on the Weight field (e.g., add
+kubebuilder:validation:Minimum=0 and +kubebuilder:validation:Maximum=100 above
the Weight declaration) so the CRD rejects values outside 0–100 while keeping
the existing pointer type and json tag.
kubernetes/gateway-operator/api/v1alpha1/restapi_types.go-176-185 (1)

176-185: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Constrain direct-response status codes to valid HTTP ranges.

Line 180 currently accepts any integer, so invalid directResponse.statusCode values can pass CRD validation. Add bounds such as 100-599 here to keep the contract aligned with HTTP semantics.

Suggested change
 type OperationDirectResponse struct {
 	// StatusCode HTTP status code
 	// +kubebuilder:validation:Required
+	// +kubebuilder:validation:Minimum=100
+	// +kubebuilder:validation:Maximum=599
 	StatusCode int `json:"statusCode"`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/api/v1alpha1/restapi_types.go` around lines 176 -
185, The StatusCode field on the OperationDirectResponse struct currently allows
any integer; update the kubebuilder validation tags on StatusCode (the
StatusCode int `json:"statusCode"`) to constrain values to the HTTP range by
adding +kubebuilder:validation:Minimum=100 and
+kubebuilder:validation:Maximum=599 (and keep the existing Required tag), so CRD
validation rejects values outside 100–599.
🧹 Nitpick comments (2)
gateway/gateway-controller/api/management-openapi.yaml (1)

4136-4140: ⚡ Quick win

Consider specifying a default value for pathMatchType.

The pathMatchType field lacks a default value. When omitted, the routing behavior should be explicitly documented either via a default value or in the description. If the intent is to match the Gateway API HTTPRoute default (which is typically Exact for path matching), consider adding default: Exact.

📝 Suggested default value
 pathMatchType:
   type: string
   description: Path matching semantics for the operation route
   enum: [Exact, PathPrefix]
+  default: Exact
   example: Exact
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gateway/gateway-controller/api/management-openapi.yaml` around lines 4136 -
4140, Add an explicit default for the pathMatchType schema entry in
management-openapi.yaml by setting default: Exact and update the description to
state that when omitted the default is Exact (matching the Gateway API HTTPRoute
default); modify the schema block for the property named pathMatchType so it
includes the default key and a brief note in its description about the default
behavior.
kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go (1)

788-863: ⚡ Quick win

Add a passing parametersRef case.

This suite only exercises nil and missing references. A success-path test with a registered object would cover the real lookup path and would catch regressions where valid parametersRef objects are rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go`
around lines 788 - 863, Add a positive test case to TestValidateParametersRef
that exercises the real lookup path: extend the test table with a case whose
Gateway.Spec.Infrastructure.ParametersRef points to a corev1.ConfigMap (Group ""
/ Kind "ConfigMap", Name e.g. "valid-config") and mark wantValid=true; to make
the fake client return that object, add a per-test field (e.g. objects
[]client.Object) and when building the client use
fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.objects...).Build();
ensure the scheme registers corev1 (corev1.AddToScheme) and include a
corev1.ConfigMap in tt.objects for the passing case so validateParametersRef
actually finds it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gateway/gateway-controller/api/management-openapi.yaml`:
- Around line 4179-4182: The statusCode schema currently allows any integer; add
OpenAPI numeric range constraints so only valid HTTP status codes (100–599) are
accepted by updating the statusCode property to include minimum: 100 and
maximum: 599. Locate the statusCode field in the OpenAPI spec (the integer
schema with description "HTTP status code returned directly without contacting
an upstream") and add the minimum and maximum keys to enforce the 100–599 range.
- Around line 4005-4010: The vhostList array items are missing pattern
validation; update the schema for vhostList.items to include the same
hostname/wildcard regex used by the existing vhost (and vhosts.main) definitions
so only valid hostnames or a leading wildcard (e.g. *.example.com) are allowed;
locate the vhostList schema and add a "pattern" property with the approved regex
(matching the vhost/vhosts.main patterns) and keep the example unchanged.
- Around line 4195-4197: The OperationResponseHeader.schema is missing RFC 7230
token validation for its name property; update the OperationResponseHeader
definition so the name field includes the same pattern/format validation used by
OperationHeaderMatch (i.e., the RFC 7230 token regex) and an example that still
conforms to that pattern, ensuring response header names are validated
consistently.
- Around line 4154-4167: Add RFC 7230 token validation to the
OperationHeaderMatch.name property in the OpenAPI spec: update
OperationHeaderMatch -> properties -> name to include a pattern that restricts
the header name to RFC7230 tchar characters (no spaces, only allowed
punctuation, letters and digits), for example using the token regex
^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$ and keep the existing description/example; this
ensures invalid header names are rejected at schema validation time before
translating to Envoy.

In `@gateway/gateway-controller/pkg/transform/restapi_test.go`:
- Around line 357-360: The test is asserting DefaultCluster against the internal
rdc.UpstreamClusters key (e.g. "upstream_sandbox_*") but the transform contract
exposes the Envoy-facing name; update the hard-coded expectedSandboxCluster and
the route-loop assertions to compute/compare against EnvoyClusterName instead of
the internal ClusterKey. Locate the expectations referencing
expectedSandboxCluster and any assertions that compare route.DefaultCluster (or
route.GetDefaultCluster()) and replace them to call or construct the
EnvoyClusterName for the matching upstream (using the same logic/function used
by the transform code that generates EnvoyClusterName) so the tests validate the
Envoy-facing cluster name rather than the rdc.UpstreamClusters map key. Ensure
both the sandbox/main route checks (around the previous expectedSandboxCluster
and the route-loop assertions) are changed consistently.

In `@gateway/gateway-controller/pkg/transform/restapi.go`:
- Around line 234-255: The code currently collapses per-endpoint schemes into a
single cluster-level TLS flag in the loop over def.Upstreams (parsedURL,
endpoints, tlsEnabled) and this loses mixed http/https information; update the
transformation to preserve scheme/TLS per endpoint by adding a scheme or tls
field to models.Endpoint (set ep.Scheme or ep.TLS based on parsedURL.Scheme) and
remove/avoid using the single tlsEnabled cluster flag, or alternatively detect
mixed schemes while iterating (using parsedURL.Scheme) and return an error
rejecting mixed-scheme upstreams before assigning
rdc.UpstreamClusters[defClusterKey]; update the construction of
models.UpstreamCluster and models.Endpoint accordingly so endpoint-level scheme
is retained.

In `@gateway/gateway-controller/pkg/xds/translator.go`:
- Around line 633-691: createWeightedCluster currently builds a STRICT_DNS
cluster without TLS even when tls.Enabled is true; mirror the single-endpoint
path in createCluster by attaching an Envoy TLS transport socket to the cluster
when tls != nil && tls.Enabled: construct an
envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext (set Sni to ep.Host
or the given name as appropriate and include any ca/cert configs from
models.UpstreamTLS), wrap it with anypb.New and assign it to the returned
cluster via Cluster.TransportSocket = &core.TransportSocket{ Name: "tls",
ConfigType: &core.TransportSocket_TypedConfig{ TypedConfig:
anypb.New(<UpstreamTlsContext>) } }; ensure you import and reuse the same TLS
construction logic used in createCluster (or centralize into a helper) so
createWeightedCluster sets transport socket when tls.Enabled.
- Around line 508-562: setDirectResponseMatch currently diverges from
createRouteFromRDC by converting parameterized paths into literal regexes and
downgrading RegularExpression header matchers to exact matches; update it to
reuse the same path and header matching logic as createRouteFromRDC so
direct-response routes match identically. Specifically, for path handling in
setDirectResponseMatch (function name: setDirectResponseMatch, symbols:
fullPath, operationPath, r.Match.PathSpecifier, route.RouteMatch_SafeRegex,
route.RouteMatch_Path) detect and handle parameterized paths using the same
logic as createRouteFromRDC (or extract that logic into a shared helper and call
it) instead of always falling back to QuoteMeta; and for header matching
(symbols: rdcRoute.MatchHeaders, route.HeaderMatcher, HeaderMatchSpecifier)
preserve the original match types by mapping RegularExpression header entries to
the regex/safe-regex header matcher variant (using matcher.RegexMatcher) rather
than converting them to StringMatcher exact matches. Ensure you either call
createRouteFromRDC or factor out and reuse its path/header-matching helpers so
behavior is identical.

In `@kubernetes/gateway-operator/api/v1alpha1/restapi_types.go`:
- Around line 235-253: The Upstream struct currently allows both Url and Ref to
be omitted or set together; update the CRD schema for Upstream so exactly one of
Url or Ref is required by adding an OpenAPI oneOf rule to the Upstream type (use
a kubebuilder marker to emit a oneOf on the Upstream schema referencing the
properties "url" and "ref") so the CRD enforces either Url (Upstream.Url) XOR
Ref (Upstream.Ref) but not both.

In `@kubernetes/gateway-operator/go.mod`:
- Around line 17-22: The k8s.io/* modules in go.mod are misaligned: update the
k8s.io/cli-runtime module entry to the same minor version as the others (e.g.,
change k8s.io/cli-runtime v0.33.3 → v0.35.1) so k8s.io/api, k8s.io/apimachinery,
k8s.io/client-go and cli-runtime share the same minor; after editing the module
line for k8s.io/cli-runtime, run go mod tidy to resolve transitive versions and
verify builds and tests that use client-go, apimachinery, api, and cli-runtime
(look for the k8s.io/cli-runtime, k8s.io/client-go, k8s.io/apimachinery,
k8s.io/api entries).

In `@kubernetes/gateway-operator/internal/controller/gateway_addresses.go`:
- Line 25: The code currently exposes the loopback constant
localClusterNodeAddressFallback ("127.0.0.1") for NodePort gateways which causes
incorrect Gateway status; update the address-resolution logic so NodePort-backed
Gateways never publish 127.0.0.1. In the function that computes gateway
addresses (the code path that uses localClusterNodeAddressFallback and is
consumed by k8s_gateway_controller.go when updating Gateway status), remove the
unconditional fallback for NodePort: either attempt to derive real node IPs
(e.g., from Node.Status.Addresses or the Service's nodePort endpoints) and
publish those, or leave the Gateway's address list empty/unset until a
non-loopback address can be resolved. Ensure any references to
localClusterNodeAddressFallback are not written into Gateway status for
NodePort-backed Gateways.

In `@kubernetes/gateway-operator/internal/controller/httproute_backends.go`:
- Around line 45-61: The helper serviceConnectionPort currently hides failures
by returning the Service port for headless Services when p.TargetPort is
non-numeric; change it to fail explicitly instead of silently falling back:
update serviceConnectionPort to return a resolvable numeric port or an
error/sentinel (e.g., (int32, error) or (int32, bool)) when svc.Spec.ClusterIP
== corev1.ClusterIPNone and p.TargetPort.Type != intstr.Int, and propagate that
error to callers so backend resolution can either query Endpoints/EndpointSlices
to resolve the numeric pod port or abort with a clear failure; ensure you
reference the p.TargetPort check in serviceConnectionPort and update all callers
to handle the new error/sentinel path.
- Around line 190-198: The current handler collapses all c.Get errors into a
permanent RouteReasonBackendNotFound; change the logic in the block where
c.Get(ctx, key, svc) is handled so only apierrors.IsNotFound(err) sets
ref.Reason = gatewayv1.RouteReasonBackendNotFound and the "backend Service ...
not found" message, while any other error preserves it as a transient read
failure (e.g., set a different ref.Reason such as an internal/read-failed reason
or leave ref.OK=false and set ref.Message to include the error) so callers like
firstBackendURL can distinguish retryable lookup failures from true missing
backends; update the error branch message to include the actual error details
(fmt.Sprintf("get backend Service %s: %v", key.String(), err)) and ensure ref
fields (ref.OK, ref.Reason, ref.Message) reflect transient vs permanent
outcomes.

In `@kubernetes/gateway-operator/internal/controller/httproute_compile.go`:
- Around line 60-65: The contextPath computed from
route.Annotations[AnnHTTPRouteContext] (variable contextPath in
httproute_compile.go) must be normalized to match the APIConfigData.Context
schema (no trailing slash, except root "/"); update the branch that sets
contextPath so that after trimming and adding a leading slash you also strip any
trailing slash when the path length > 1 (preserve "/" for empty), ensuring
APIConfigData.Context is produced as a non-trailing-slash path; locate the logic
around contextPath and replace the current else-if handling with this
normalization.

In `@kubernetes/gateway-operator/internal/controller/httproute_filters.go`:
- Around line 107-135: The handler for gatewayv1.HTTPRouteFilterRequestRedirect
currently synthesizes a Location using "localhost" and a default scheme when
redir.Hostname or redir.Scheme are missing; instead detect when either
redir.Hostname or redir.Scheme is nil/empty and do NOT synthesize an absolute
URL (remove the locHost/"localhost" fallback). For shapes the current
apiv1.OperationDirectResponse cannot represent (i.e., status-only redirects or
redirects missing hostname/scheme) either reject/return an unsupported-redirect
error or skip/apply a non-direct response path so we don't emit a misleading
Location header; update the gatewayv1.HTTPRouteFilterRequestRedirect handling
(variable redir and the creation of apiv1.OperationDirectResponse named direct)
to perform this validation and return/mark unsupported rather than constructing
a fabricated URL.

In `@kubernetes/gateway-operator/internal/controller/httproute_referencegrant.go`:
- Around line 88-103: Change crossNamespaceSecretReferenceGrantPermitted to
return (bool, error) and surface API list errors instead of conflating them with
"no grant". Specifically, update the function signature
crossNamespaceSecretReferenceGrantPermitted(ctx context.Context, c
client.Client, gwNS, secretNS, secretName string) to return (bool, error),
return (true, nil) when secretNS == gwNS, return (false, err) when c.List(ctx,
&list, client.InNamespace(secretNS)) returns an error, and otherwise return
(true, nil) or (false, nil) based on
referenceGrantAllowsGatewayToSecret(&list.Items[i].Spec, gwNS, secretName). Also
update all callers to handle the error return (requeue on transient lookup
errors and only reject when the boolean result is false with a nil error).

In `@kubernetes/gateway-operator/internal/controller/k8s_gateway_controller.go`:
- Around line 494-509: The reconcile path treats the gateway runtime Service as
required but discovery via discoverGatewayRuntimeService can fail while skipHelm
remains true, leaving reconcile stuck; update the self-heal/expected-resources
check to include the runtime Service (or trigger a Helm repair when discovery
fails) by adding the runtime Service to the resources validated before honoring
skipHelm, and/or when discoverGatewayRuntimeService or
resolveGatewayAddressesFromService returns missing/empty, call the existing Helm
repair flow (same place that handles Deployments/ controller Service) or clear
skipHelm so patchGatewayStatus can request a re-install; reference
discoverGatewayRuntimeService, resolveGatewayAddressesFromService and
patchGatewayStatus to locate where to add the runtime Service validation or
invoke the Helm repair path.

In `@kubernetes/gateway-operator/internal/helm/client.go`:
- Around line 395-410: The current GetReleaseName collapses long gateway names
to a fixed 8-hex suffix (helmReleaseHashHexChars), which raises collision risk;
change GetReleaseName to compute the hash-length dynamically from the 53-char
limit so more of the hex digest is used when the gateway name would overflow.
Specifically, replace the fixed helmReleaseHashHexChars usage in GetReleaseName
with a computed allowedHashChars = maxHelmReleaseNameLen -
len(helmReleaseNameSuffix) - len(helmReleaseHashPrefix) (clamped to a sensible
minimum like 8), then return helmReleaseHashPrefix +
hex.EncodeToString(sum[:])[:allowedHashChars]; update or remove the fixed
constant helmReleaseHashHexChars accordingly and keep references to
GetReleaseName, helmReleaseNameSuffix, helmReleaseHashPrefix, and
maxHelmReleaseNameLen to locate the change.

---

Outside diff comments:
In `@kubernetes/gateway-operator/internal/controller/httproute_controller.go`:
- Around line 215-239: On non-retryable build failures (i.e., when
BuildAPIConfigFromHTTPRoute returns an error and requeueAfter==0 — typically
IsHTTPRouteBackendRefError or IsInvalidHTTPRouteConfigError), delete the
currently deployed RestApi on the Gateway so runtime state doesn't drift: after
calling r.patchResolvedRefsForParents(...) and before returning (inside the same
error branch), invoke a deletion helper such as r.deleteGatewayRestAPI(ctx,
parentGW, route) (create this helper if it doesn't exist), log any deletion
error via log.Error, and then return the existing ctrl.Result{}; keep transient
errors (requeueAfter>0) behavior unchanged.

In `@kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml`:
- Around line 2-19: The template currently parses the entire standard-crds.yaml
into a single $crd and runs lookup/ownership checks once, then emits the whole
bundle; instead, parse the file into individual CRD documents and iterate them
so lookup and the $ownedByThisRelease gating happen per document: use fromYaml
($.Files.Get $p) as a list (range over each item, e.g., $doc), perform lookup
"apiextensions.k8s.io/v1" "CustomResourceDefinition" "" $doc.metadata.name and
the same annotation/label checks for each $doc, and emit each CRD individually
(e.g., toYaml $doc) only when its per-CRD check passes. Ensure you keep the same
variables ($paths, $p) and preserve existing annotation/label checks but applied
inside the per-document loop.

---

Minor comments:
In `@kubernetes/gateway-operator/api/v1alpha1/restapi_types.go`:
- Around line 280-288: The Weight field in the WeightedUpstream struct lacks CRD
validation for the documented 0-100 range; add kubebuilder validation
annotations to enforce Minimum=0 and Maximum=100 on the Weight field (e.g., add
+kubebuilder:validation:Minimum=0 and +kubebuilder:validation:Maximum=100 above
the Weight declaration) so the CRD rejects values outside 0–100 while keeping
the existing pointer type and json tag.
- Around line 176-185: The StatusCode field on the OperationDirectResponse
struct currently allows any integer; update the kubebuilder validation tags on
StatusCode (the StatusCode int `json:"statusCode"`) to constrain values to the
HTTP range by adding +kubebuilder:validation:Minimum=100 and
+kubebuilder:validation:Maximum=599 (and keep the existing Required tag), so CRD
validation rejects values outside 100–599.

---

Nitpick comments:
In `@gateway/gateway-controller/api/management-openapi.yaml`:
- Around line 4136-4140: Add an explicit default for the pathMatchType schema
entry in management-openapi.yaml by setting default: Exact and update the
description to state that when omitted the default is Exact (matching the
Gateway API HTTPRoute default); modify the schema block for the property named
pathMatchType so it includes the default key and a brief note in its description
about the default behavior.

In
`@kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go`:
- Around line 788-863: Add a positive test case to TestValidateParametersRef
that exercises the real lookup path: extend the test table with a case whose
Gateway.Spec.Infrastructure.ParametersRef points to a corev1.ConfigMap (Group ""
/ Kind "ConfigMap", Name e.g. "valid-config") and mark wantValid=true; to make
the fake client return that object, add a per-test field (e.g. objects
[]client.Object) and when building the client use
fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.objects...).Build();
ensure the scheme registers corev1 (corev1.AddToScheme) and include a
corev1.ConfigMap in tt.objects for the passing case so validateParametersRef
actually finds it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 03fa64e4-c9d9-4047-8fa7-a6fe98ed73b8

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca3744 and 6d7ded8.

⛔ Files ignored due to path filters (1)
  • kubernetes/gateway-operator/go.sum is excluded by !**/*.sum
📒 Files selected for processing (59)
  • gateway/gateway-controller/api/management-openapi.yaml
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/api/management/generated.go
  • gateway/gateway-controller/pkg/models/runtime_deploy_config.go
  • gateway/gateway-controller/pkg/transform/restapi.go
  • gateway/gateway-controller/pkg/transform/restapi_test.go
  • gateway/gateway-controller/pkg/xds/route_discriminator_test.go
  • gateway/gateway-controller/pkg/xds/translator.go
  • gateway/gateway-controller/pkg/xds/translator_test.go
  • gateway/gateway-runtime/policy-engine/internal/kernel/translator.go
  • gateway/gateway-runtime/policy-engine/internal/kernel/translator_test.go
  • gateway/gateway-runtime/policy-engine/pkg/engine/types.go
  • kubernetes/gateway-operator/api/v1alpha1/restapi_types.go
  • kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go
  • kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_apigateways.yaml
  • kubernetes/gateway-operator/config/rbac/role.yaml
  • kubernetes/gateway-operator/go.mod
  • kubernetes/gateway-operator/internal/controller/gateway_addresses.go
  • kubernetes/gateway-operator/internal/controller/gateway_addresses_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_api_annotations.go
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes.go
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_infra.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_attachment.go
  • kubernetes/gateway-operator/internal/controller/httproute_attachment_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_compile.go
  • kubernetes/gateway-operator/internal/controller/httproute_compile_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_controller.go
  • kubernetes/gateway-operator/internal/controller/httproute_enqueue.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_parents.go
  • kubernetes/gateway-operator/internal/controller/httproute_referencegrant.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller_test.go
  • kubernetes/gateway-operator/internal/controller/llmproxy_controller_test.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_enqueue.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint_test.go
  • kubernetes/gateway-operator/internal/gatewayclient/yaml_payload_test.go
  • kubernetes/gateway-operator/internal/helm/client.go
  • kubernetes/gateway-operator/internal/helm/client_test.go
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gatewayclasses.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gateways.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/grpcroutes.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/httproutes.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/referencegrants.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/standard-crds.yaml
  • kubernetes/helm/operator-helm-chart/templates/_helpers.tpl
  • kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml
  • sdk/core/policy/v1alpha2/action.go
💤 Files with no reviewable changes (6)
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint_test.go
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/referencegrants.gateway.networking.k8s.io.yaml
  • kubernetes/gateway-operator/internal/controller/llmproxy_controller_test.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_enqueue.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint.go
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gatewayclasses.gateway.networking.k8s.io.yaml

Comment thread gateway/gateway-controller/api/management-openapi.yaml
Comment thread gateway/gateway-controller/api/management-openapi.yaml
Comment thread gateway/gateway-controller/api/management-openapi.yaml
Comment thread gateway/gateway-controller/api/management-openapi.yaml
Comment on lines +357 to +360
// The default cluster must be the name Envoy knows the cluster by, which in the
// RDC path is the rdc.UpstreamClusters map key (ClusterKey), i.e.
// "upstream_sandbox_<host>_<port>" — not the sanitized "cluster_<scheme>_<host>" form.
const expectedSandboxCluster = "upstream_sandbox_sandbox-backend_9080"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the Envoy cluster name contract in these assertions.

These tests now pin DefaultCluster to the internal rdc.UpstreamClusters key (upstream_*), but the transform contract uses the Envoy-facing cluster name instead. Please update both the hard-coded sandbox expectation and the route-loop assertion to validate against the generated EnvoyClusterName, not the internal map key. Based on learnings: in gateway/gateway-controller/pkg/transform/, DefaultCluster for sandbox/main routes should use EnvoyClusterName rather than the internal ClusterKey.

Also applies to: 390-390, 395-430

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gateway/gateway-controller/pkg/transform/restapi_test.go` around lines 357 -
360, The test is asserting DefaultCluster against the internal
rdc.UpstreamClusters key (e.g. "upstream_sandbox_*") but the transform contract
exposes the Envoy-facing name; update the hard-coded expectedSandboxCluster and
the route-loop assertions to compute/compare against EnvoyClusterName instead of
the internal ClusterKey. Locate the expectations referencing
expectedSandboxCluster and any assertions that compare route.DefaultCluster (or
route.GetDefaultCluster()) and replace them to call or construct the
EnvoyClusterName for the matching upstream (using the same logic/function used
by the transform code that generates EnvoyClusterName) so the tests validate the
Envoy-facing cluster name rather than the rdc.UpstreamClusters map key. Ensure
both the sandbox/main route checks (around the previous expectedSandboxCluster
and the route-loop assertions) are changed consistently.

Source: Learnings

Comment on lines +340 to +365
for _, target := range parentTargets {
attached, _, _ := evaluateHTTPRouteAttachment(ctx, c, parentGW, route, target.ref)
if !attached {
continue
}
for _, listener := range parentGW.Spec.Listeners {
if !parentRefAttachesToListener(target.ref, listener) {
continue
}
listenerHost := ""
if listener.Hostname != nil {
listenerHost = string(*listener.Hostname)
}
if len(route.Spec.Hostnames) == 0 {
// Route matches all hostnames; constrain to the listener's hostname if it has one.
if listenerHost != "" {
addHost(listenerHost)
}
continue
}
for _, rh := range route.Spec.Hostnames {
if h, ok := intersectHostname(string(rh), listenerHost); ok {
addHost(h)
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Derive vhosts from the listeners that actually accepted the route.

evaluateHTTPRouteAttachment(..., target.ref) only tells you that at least one listener for that parent accepted the route. The inner loop then adds hostnames from every listener matched by parentRefAttachesToListener, even if that listener was filtered out by AllowedRoutes, namespace checks, or hostname mismatch. With a gateway-wide parentRef, this can emit vhosts for listeners the route never attached to. Please reuse the same per-listener acceptance predicate here and add a regression test for the mixed-listener case.

Comment on lines +107 to +135
case gatewayv1.HTTPRouteFilterRequestRedirect:
if f.RequestRedirect == nil {
continue
}
redir := f.RequestRedirect
status := 302
if redir.StatusCode != nil {
status = int(*redir.StatusCode)
}
host := ""
if redir.Hostname != nil {
host = string(*redir.Hostname)
}
scheme := "http"
if redir.Scheme != nil && *redir.Scheme != "" {
scheme = string(*redir.Scheme)
}
locHost := host
if locHost == "" {
locHost = "localhost"
}
headers := []apiv1.OperationResponseHeader{{
Name: "Location",
Value: fmt.Sprintf("%s://%s", scheme, locHost),
}}
direct = &apiv1.OperationDirectResponse{
StatusCode: status,
Headers: headers,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not synthesize redirect targets with http://localhost.

When RequestRedirect omits scheme or hostname, this code rewrites the redirect to a new absolute URL instead of preserving the original request authority/path. Status-only or partial redirects will point clients at the wrong location. Either preserve unspecified components, or reject redirect shapes the current direct-response model cannot represent faithfully.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/controller/httproute_filters.go` around
lines 107 - 135, The handler for gatewayv1.HTTPRouteFilterRequestRedirect
currently synthesizes a Location using "localhost" and a default scheme when
redir.Hostname or redir.Scheme are missing; instead detect when either
redir.Hostname or redir.Scheme is nil/empty and do NOT synthesize an absolute
URL (remove the locHost/"localhost" fallback). For shapes the current
apiv1.OperationDirectResponse cannot represent (i.e., status-only redirects or
redirects missing hostname/scheme) either reject/return an unsupported-redirect
error or skip/apply a non-direct response path so we don't emit a misleading
Location header; update the gatewayv1.HTTPRouteFilterRequestRedirect handling
(variable redir and the creation of apiv1.OperationDirectResponse named direct)
to perform this validation and return/mark unsupported rather than constructing
a fabricated URL.

Comment on lines +88 to +103
// crossNamespaceSecretReferenceGrantPermitted checks whether a Gateway may reference a Secret
// in another namespace per Gateway API ReferenceGrant rules. Grants must exist in the Secret namespace.
func crossNamespaceSecretReferenceGrantPermitted(ctx context.Context, c client.Client, gwNS, secretNS, secretName string) bool {
if secretNS == gwNS {
return true
}
var list gatewayv1beta1.ReferenceGrantList
if err := c.List(ctx, &list, client.InNamespace(secretNS)); err != nil {
return false
}
for i := range list.Items {
if referenceGrantAllowsGatewayToSecret(&list.Items[i].Spec, gwNS, secretName) {
return true
}
}
return false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Return lookup errors from cross-namespace Secret grant checks.

false currently means both “no grant permits this” and “listing ReferenceGrants failed.” That makes a temporary API read problem indistinguishable from a real deny. Change this helper to return (bool, error) so callers can requeue on lookup failures and only reject when the grant check actually completes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/controller/httproute_referencegrant.go`
around lines 88 - 103, Change crossNamespaceSecretReferenceGrantPermitted to
return (bool, error) and surface API list errors instead of conflating them with
"no grant". Specifically, update the function signature
crossNamespaceSecretReferenceGrantPermitted(ctx context.Context, c
client.Client, gwNS, secretNS, secretName string) to return (bool, error),
return (true, nil) when secretNS == gwNS, return (false, err) when c.List(ctx,
&list, client.InNamespace(secretNS)) returns an error, and otherwise return
(true, nil) or (false, nil) based on
referenceGrantAllowsGatewayToSecret(&list.Items[i].Spec, gwNS, secretName). Also
update all callers to handle the error return (requeue on transient lookup
errors and only reject when the boolean result is false with a nil error).

Comment on lines +494 to +509
runtimeSvc, err := discoverGatewayRuntimeService(ctx, r.Client, gw.Name, ns)
if err != nil {
return fmt.Errorf("discover gateway runtime service: %w", err)
}
addrs := resolveGatewayAddressesFromService(runtimeSvc)
if runtimeSvc.Spec.Type == corev1.ServiceTypeLoadBalancer && len(addrs) == 0 {
pendingMsg := "Waiting for LoadBalancer address to be assigned to gateway runtime Service"
_ = r.patchGatewayStatus(ctx, gw, listenerStatuses, nil, metav1.Condition{
Type: string(gatewayv1.GatewayConditionProgrammed),
Status: metav1.ConditionFalse,
Reason: string(gatewayv1.GatewayReasonPending),
Message: pendingMsg,
ObservedGeneration: gw.Generation,
LastTransitionTime: metav1.Now(),
},
})
return fmt.Errorf("pending: %s", pendingMsg)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the Helm self-heal check include the runtime Service.

This new path treats the gateway-runtime Service as required, but skipHelm can still stay true when that Service is missing because the earlier resource check only verifies Deployments and the controller Service. In that state, reconcile will keep returning pending/error here without ever re-running Helm to recreate the runtime Service. Fold the runtime Service into the expected-resource check, or force a repair path when discovery fails here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/controller/k8s_gateway_controller.go`
around lines 494 - 509, The reconcile path treats the gateway runtime Service as
required but discovery via discoverGatewayRuntimeService can fail while skipHelm
remains true, leaving reconcile stuck; update the self-heal/expected-resources
check to include the runtime Service (or trigger a Helm repair when discovery
fails) by adding the runtime Service to the resources validated before honoring
skipHelm, and/or when discoverGatewayRuntimeService or
resolveGatewayAddressesFromService returns missing/empty, call the existing Helm
repair flow (same place that handles Deployments/ controller Service) or clear
skipHelm so patchGatewayStatus can request a re-install; reference
discoverGatewayRuntimeService, resolveGatewayAddressesFromService and
patchGatewayStatus to locate where to add the runtime Service validation or
invoke the Helm repair path.

Comment on lines +395 to +410
const (
helmReleaseNameSuffix = "-gw"
maxHelmReleaseNameLen = 53
helmReleaseHashPrefix = "gw-"
helmReleaseHashHexChars = 8
)

// GetReleaseName generates a stable Helm release name from a gateway name.
// Helm release names must be DNS-1123 labels and at most 53 characters.
func GetReleaseName(gatewayName string) string {
// Helm release names must be DNS-compliant
// Gateway name is already validated as a Kubernetes resource name
return fmt.Sprintf("%s-gateway", gatewayName)
candidate := gatewayName + helmReleaseNameSuffix
if len(candidate) <= maxHelmReleaseNameLen {
return candidate
}
sum := sha256.Sum256([]byte(gatewayName))
return helmReleaseHashPrefix + hex.EncodeToString(sum[:])[:helmReleaseHashHexChars]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Increase the hashed suffix length to avoid release-name collisions.

Long gateway names now collapse into gw- plus 8 hex chars. That makes the Helm release mapping many-to-one, so two distinct long Gateway names can resolve to the same release and then share install/discovery state. Use a longer digest segment, or reserve more of the 53-character budget for the hash, so the mapping stays effectively unique.

Suggested direction
 const (
 	helmReleaseNameSuffix   = "-gw"
 	maxHelmReleaseNameLen   = 53
 	helmReleaseHashPrefix   = "gw-"
-	helmReleaseHashHexChars = 8
+	helmReleaseHashHexChars = 16
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/helm/client.go` around lines 395 - 410,
The current GetReleaseName collapses long gateway names to a fixed 8-hex suffix
(helmReleaseHashHexChars), which raises collision risk; change GetReleaseName to
compute the hash-length dynamically from the 53-char limit so more of the hex
digest is used when the gateway name would overflow. Specifically, replace the
fixed helmReleaseHashHexChars usage in GetReleaseName with a computed
allowedHashChars = maxHelmReleaseNameLen - len(helmReleaseNameSuffix) -
len(helmReleaseHashPrefix) (clamped to a sensible minimum like 8), then return
helmReleaseHashPrefix + hex.EncodeToString(sum[:])[:allowedHashChars]; update or
remove the fixed constant helmReleaseHashHexChars accordingly and keep
references to GetReleaseName, helmReleaseNameSuffix, helmReleaseHashPrefix, and
maxHelmReleaseNameLen to locate the change.

@O-sura O-sura force-pushed the gw-api-conformance branch from 6d7ded8 to 183a217 Compare June 15, 2026 05:54
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Validation Results

Dependency name: k8s.io/api
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/apimachinery
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/client-go
Version: v0.35.1 (was v0.33.3)
Allowed range: >=v0.33.3
Approved: ✅ Yes

Dependency name: sigs.k8s.io/gateway-api
Version: v1.5.1 (was v1.3.0)
Approved: ❌ No - Module not found in dependency registry


Next Steps

  1. Review the validation failures listed above
  2. Check if dependencies are in the approved dependency list
  3. Options to resolve:
    • Remove the unapproved dependencies from this PR
    • OR submit a PR to add these dependencies to the approved list in engineering-governance
  4. Once resolved, push changes to re-run validation

This PR is blocked until all dependencies are approved.

⚠️ Please verify the scope of the dependencies usage is necessary

@O-sura O-sura force-pushed the gw-api-conformance branch from 183a217 to 27f55a8 Compare June 16, 2026 08:47
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Validation Results

Dependency name: k8s.io/api
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/apimachinery
Version: v0.35.1 (was v0.33.3)
Approved: ❌ No - Module not found in dependency registry

Dependency name: k8s.io/client-go
Version: v0.35.1 (was v0.33.3)
Allowed range: >=v0.33.3
Approved: ✅ Yes

Dependency name: sigs.k8s.io/gateway-api
Version: v1.5.1 (was v1.3.0)
Approved: ❌ No - Module not found in dependency registry


Next Steps

  1. Review the validation failures listed above
  2. Check if dependencies are in the approved dependency list
  3. Options to resolve:
    • Remove the unapproved dependencies from this PR
    • OR submit a PR to add these dependencies to the approved list in engineering-governance
  4. Once resolved, push changes to re-run validation

This PR is blocked until all dependencies are approved.

⚠️ Please verify the scope of the dependencies usage is necessary

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
kubernetes/gateway-operator/internal/controller/gateway_addresses.go (1)

72-73: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid publishing loopback as the NodePort Gateway status address.

Line 73 currently emits 127.0.0.1, which is not an appropriate externally meaningful status address for NodePort. Return no address until a resolvable node address is available, or derive node addresses before setting status.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kubernetes/gateway-operator/internal/controller/gateway_addresses.go` around
lines 72 - 73, The nodePortGatewayAddresses function currently returns a gateway
address based on localClusterNodeAddressFallback, which resolves to the loopback
address 127.0.0.1 and is not appropriate for external NodePort Gateway
communication. Fix this by modifying the function to either return an empty
address slice when only the loopback address is available, or implement logic to
derive and use actual resolvable node addresses instead of falling back to the
loopback address. Ensure that the function gatewayIPAddress is only called with
a legitimate external node address, or return an empty slice if no such address
can be determined.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@kubernetes/gateway-operator/internal/controller/gateway_addresses.go`:
- Around line 72-73: The nodePortGatewayAddresses function currently returns a
gateway address based on localClusterNodeAddressFallback, which resolves to the
loopback address 127.0.0.1 and is not appropriate for external NodePort Gateway
communication. Fix this by modifying the function to either return an empty
address slice when only the loopback address is available, or implement logic to
derive and use actual resolvable node addresses instead of falling back to the
loopback address. Ensure that the function gatewayIPAddress is only called with
a legitimate external node address, or return an empty slice if no such address
can be determined.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdc79a68-37a7-447c-92d9-c8de2fd17fe9

📥 Commits

Reviewing files that changed from the base of the PR and between 183a217 and 27f55a8.

⛔ Files ignored due to path filters (2)
  • go.work.sum is excluded by !**/*.sum
  • kubernetes/gateway-operator/go.sum is excluded by !**/*.sum
📒 Files selected for processing (55)
  • gateway/gateway-controller/api/management-openapi.yaml
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/api/management/generated.go
  • gateway/gateway-controller/pkg/models/runtime_deploy_config.go
  • gateway/gateway-controller/pkg/transform/restapi.go
  • gateway/gateway-controller/pkg/transform/restapi_test.go
  • gateway/gateway-controller/pkg/xds/route_discriminator_test.go
  • gateway/gateway-controller/pkg/xds/translator.go
  • gateway/gateway-controller/pkg/xds/translator_test.go
  • kubernetes/gateway-operator/api/v1alpha1/restapi_types.go
  • kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go
  • kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_apigateways.yaml
  • kubernetes/gateway-operator/config/rbac/role.yaml
  • kubernetes/gateway-operator/go.mod
  • kubernetes/gateway-operator/internal/controller/gateway_addresses.go
  • kubernetes/gateway-operator/internal/controller/gateway_addresses_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_api_annotations.go
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes.go
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_infra.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_attachment.go
  • kubernetes/gateway-operator/internal/controller/httproute_attachment_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_compile.go
  • kubernetes/gateway-operator/internal/controller/httproute_compile_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_controller.go
  • kubernetes/gateway-operator/internal/controller/httproute_enqueue.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_parents.go
  • kubernetes/gateway-operator/internal/controller/httproute_referencegrant.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller_test.go
  • kubernetes/gateway-operator/internal/controller/llmproxy_controller_test.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_enqueue.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint_test.go
  • kubernetes/gateway-operator/internal/gatewayclient/yaml_payload_test.go
  • kubernetes/gateway-operator/internal/helm/client.go
  • kubernetes/gateway-operator/internal/helm/client_test.go
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gatewayclasses.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gateways.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/grpcroutes.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/httproutes.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/referencegrants.gateway.networking.k8s.io.yaml
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/standard-crds.yaml
  • kubernetes/helm/operator-helm-chart/templates/_helpers.tpl
  • kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml
💤 Files with no reviewable changes (6)
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/referencegrants.gateway.networking.k8s.io.yaml
  • kubernetes/gateway-operator/internal/controller/llmproxy_controller_test.go
  • kubernetes/helm/operator-helm-chart/files/gateway-api-standard/gatewayclasses.gateway.networking.k8s.io.yaml
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_enqueue.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint_test.go
  • kubernetes/gateway-operator/internal/controller/management_valuefrom_fingerprint.go
✅ Files skipped from review due to trivial changes (3)
  • kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_apigateways.yaml
  • kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go
  • gateway/gateway-controller/pkg/api/management/generated.go
🚧 Files skipped from review as they are similar to previous changes (40)
  • kubernetes/gateway-operator/internal/controller/httproute_parents.go
  • kubernetes/gateway-operator/internal/controller/gateway_api_annotations.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_infra.go
  • kubernetes/gateway-operator/config/rbac/role.yaml
  • kubernetes/helm/operator-helm-chart/templates/gateway-api-crds.yaml
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes_test.go
  • gateway/gateway-controller/pkg/models/runtime_deploy_config.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters_test.go
  • kubernetes/gateway-operator/internal/helm/client.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay.go
  • gateway/gateway-controller/cmd/controller/main.go
  • kubernetes/gateway-operator/internal/helm/client_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper.go
  • gateway/gateway-controller/pkg/xds/route_discriminator_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_referencegrant.go
  • kubernetes/helm/operator-helm-chart/templates/_helpers.tpl
  • kubernetes/gateway-operator/internal/controller/httproute_attachment_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_compile.go
  • gateway/gateway-controller/pkg/transform/restapi_test.go
  • kubernetes/gateway-operator/go.mod
  • kubernetes/gateway-operator/internal/controller/httproute_compile_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_filters.go
  • gateway/gateway-controller/pkg/transform/restapi.go
  • kubernetes/gateway-operator/internal/controller/httproute_enqueue.go
  • kubernetes/gateway-operator/internal/controller/gateway_attached_routes.go
  • kubernetes/gateway-operator/internal/controller/httproute_backends.go
  • gateway/gateway-controller/api/management-openapi.yaml
  • kubernetes/gateway-operator/internal/controller/gateway_addresses_test.go
  • kubernetes/gateway-operator/api/v1alpha1/restapi_types.go
  • kubernetes/gateway-operator/internal/controller/gateway_listeners_overlay_test.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation.go
  • kubernetes/gateway-operator/internal/controller/httproute_attachment.go
  • kubernetes/gateway-operator/internal/controller/k8s_gateway_controller.go
  • kubernetes/gateway-operator/internal/controller/gateway_listener_validation_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_mapper_test.go
  • gateway/gateway-controller/pkg/xds/translator.go
  • kubernetes/gateway-operator/internal/gatewayclient/yaml_payload_test.go
  • kubernetes/gateway-operator/internal/controller/httproute_controller.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant