Suggested GitHub discussion title:
Kubernetes Gateway API (Gateway + HTTPRoute) support in Gateway Operator— paste the body below into the api-platform repo Discussions (e.g. category Ideas or General).
The gateway-operator reconciles standard Kubernetes Gateway API resources (gateway.networking.k8s.io Gateway and HTTPRoute) alongside the existing APIGateway and RestApi CRDs.
- Gateway: Same infrastructure role as
APIGateway— deploy the platform gateway via Helm, discover the gateway-controller Service, register it in the in-memory GatewayRegistry (no dependency on anAPIGatewayCR for this path). - HTTPRoute: Same API role as
RestApi— build anapi.yaml-compatible payload (APIConfigData) and call gateway-controller REST (POST/PUT/api/management/v0.9/rest-apis,DELETE/api/management/v0.9/rest-apis/{handle}). - Service /
APIPolicy/ Secret / ConfigMap: HTTPRoute resolution plus watches on Service,APIPolicy, Secret, and ConfigMap enqueue routes when backends, policy CRs, or referenced Secret/ConfigMap data change. APIPolicyCR: Gateway API–only (gateway.api-platform.wso2.com/v1alpha1);spec.policiesarray; optionalspec.targetRef(HTTPRoute) for API-level merge; omittargetRefand attach via ruleExtensionReffor rule/resource scope.params.valueFrom(native k8s shape —secretKeyRef/configMapKeyRef) is resolved from Secrets or ConfigMaps before gateway-controller REST. Does not changeRestApi/APIGatewayreconciliation.
Teams adopting Gateway API as the cluster-native way to declare gateways and HTTP routes needed a first-class path to drive the API Platform gateway without requiring APIGateway / RestApi CRDs for the same flow. The operator previously only reconciled WSO2 custom resources; there was no bridge from Gateway + HTTPRoute to the existing gateway-controller deployment and API lifecycle.
- Platform engineers standardizing on Gateway API (GKE, EKS, shared gateways, GitOps).
- API teams who want HTTP routing and backend
Servicereferences expressed asHTTPRoutewhile reusing the same gateway runtime and controller APIs. - Operators already running
APIGatewaywho want parity for Gateway API–native manifests.
- One gateway stack, two API shapes: custom CRDs or standard Gateway API, both backed by the same Helm release and registry.
- Familiar primitives for Kubernetes users (
GatewayClass,Gateway,HTTPRoute) without giving up platform features (policies, xDS, gateway-controller REST). - Clear extension points via annotations for Helm values, API handle, context, and optional control-plane hints.
| Use case | What you do |
|---|---|
| Deploy platform gateway from Gateway API | Create a Gateway whose gatewayClassName is on the operator allowlist; optional per-Gateway Helm values via ConfigMap annotation. |
| Publish an API from a route | Create HTTPRoute with parentRefs → your Gateway; backendRefs → Service; operator builds and deploys RestApi–equivalent YAML to gateway-controller. |
| Refresh route when backends change | Operator watches Service; relevant HTTPRoutes re-queue when backend Services change. |
| Refresh route when policies, secrets, or configmaps change | Operator watches APIPolicy (target HTTPRoute), Secret, and ConfigMap (when referenced from APIPolicy params via valueFrom.secretKeyRef / valueFrom.configMapKeyRef). |
| Remove API with the route | Delete HTTPRoute; finalizer removes the REST API handle from gateway-controller. |
- Gateway (standard API): Same infrastructure role as
APIGateway— deploy the platform gateway via Helm, discover the gateway-controller Service, register it in GatewayRegistry. - HTTPRoute: Same API role as
RestApi— buildAPIConfigDataand sync over gateway-controller REST. - Watches:
HTTPRoutecontroller re-queues routes when referenced Services, relatedAPIPolicyobjects, or Secrets referenced from policy params change (see implementation notes).
The operator Helm chart ships Gateway API standard channel v1.3.0 YAML under files/gateway-api-standard/ and only applies them when gatewayApi.installStandardCRDs is true (templates/gateway-api-crds.yaml). The default is false because many clusters already have Gateway API (or a prior Helm release managed the same CRDs); installing again causes server-side apply conflicts on fields such as metadata.annotations.gateway.networking.k8s.io/bundle-version and spec.versions. The WSO2 CRDs (APIGateway, RestApi, APIPolicy) remain in crds/ and are always installed with the chart.
- Greenfield cluster with no Gateway API: install the operator with
gatewayApi.installStandardCRDs=true(exact flag depends on yourhelm install/ values). - CRDs already exist (including when another Helm release owns them, e.g. a
*crd*release inkube-system): keepinstallStandardCRDs=false. Turning it on can fail with cannot be imported into the current release / invalid ownership metadata because Helm will not adopt CRDs from another release. - CRDs already exist: keep the default
falseand use the cluster’s existing Gateway API version (ensure it is compatible with the operator’ssigs.k8s.io/gateway-apidependency ingo.mod). - Bundled CRD kinds include:
gatewayclasses,gateways,httproutes,referencegrants,grpcroutes(GRPCRoute is not reconciled by the operator today). - To upgrade bundled CRDs: replace files under
files/gateway-api-standard/from a newer upstreamstandard-install.yamland bump the operatorgo.moddependency to match.
Create a GatewayClass whose metadata.name matches operator configuration (default managed class: wso2-api-platform).
| Mechanism | Purpose |
|---|---|
gateway_api.gateway_class_names in operator config.yaml |
List of spec.gatewayClassName values the operator owns. The operator Helm chart writes this from gatewayApi.managedGatewayClassNames in values.yaml. |
| Code / merge default | wso2-api-platform when the key is absent after config merge (internal/config/config.go). |
GATEWAY_API_GATEWAY_CLASS_NAMES |
Comma-separated environment override of that list. |
Resolution: OperatorConfig.ManagedGatewayClass(name string) returns whether a class is managed.
Only Gateway objects whose spec.gatewayClassName is in this list are managed. HTTPRoute objects are processed when their parent Gateway uses one of these classes.
Example config.yaml fragment:
gateway_api:
gateway_class_names:
- wso2-api-platformEnvironment variable example: GATEWAY_API_GATEWAY_CLASS_NAMES=wso2-api-platform,my-class
Helm / registry for Kubernetes Gateway uses the same gateway.* Helm settings as APIGateway (internal/config → GatewayConfig: chart name, version, values file, registry credentials, etc.).
| Annotation | Meaning |
|---|---|
gateway.api-platform.wso2.com/helm-values-configmap |
Name of a ConfigMap in the Gateway namespace whose data includes values.yaml (Helm values), analogous to APIGateway.spec.configRef. |
gateway.api-platform.wso2.com/api-selector |
Optional JSON for APISelector (same shape as on APIGateway) — which RestApi CRs logically associate with this deployment. |
gateway.api-platform.wso2.com/control-plane-host |
Optional; stored on GatewayInfo.ControlPlaneHost in the registry. |
If the Helm values ConfigMap annotation is omitted, the operator uses the default Helm values file from config (same pattern as APIGateway without configRef).
| Annotation | Meaning |
|---|---|
gateway.api-platform.wso2.com/api-version |
APIConfigData.Version (default v1.0). |
gateway.api-platform.wso2.com/context |
API context path (default / when unset or blank after trim). |
gateway.api-platform.wso2.com/display-name |
Overrides display name (default: route metadata.name). |
gateway.api-platform.wso2.com/api-handle |
REST handle for /api/management/v0.9/rest-apis/{handle} (default: {namespace}-{name} with / stripped). |
| (no HTTPRoute policy annotations) | Policy attachment is via APIPolicy only (API-level when spec.targetRef is set; rule-scope via ExtensionRef when targetRef is omitted). |
APIPolicy CR: spec.policies → array of Policy-shaped entries. API-level: set spec.targetRef to the HTTPRoute. Rule-level: omit spec.targetRef; reference from spec.rules[].filters with ExtensionRef (group: gateway.api-platform.wso2.com, kind: APIPolicy).
External params: Same shape as PodSpec env[].valueFrom — exactly one of secretKeyRef / configMapKeyRef with {name, key, namespace?}:
params:
subscriptionKeyHeader:
valueFrom:
secretKeyRef: { name: demo-creds, key: subscriptionKey }
region:
valueFrom:
configMapKeyRef: { name: demo-config, key: region }Both are resolved to plain strings before DeployRestAPI so gateway-controller sees the same JSON as inline RestApi policies. Missing Secret/ConfigMap/key → transient reconcile error.
- Ignore resources whose
spec.gatewayClassNameis not in the managed list. - Finalizer:
gateway.api-platform.wso2.com/k8s-gateway-finalizer. - Install/upgrade Helm via
helmgateway.InstallOrUpgrade(release name{metadata.name}-gateway, same pattern asAPIGateway). - Register controller endpoint via
registerGatewayInRegistry(discovery by labelsapp.kubernetes.io/instance+component=controller). - Wait for Deployments ready (
evaluateGatewayDeploymentsReady); requeue on failure. - Patch
Gateway.status.conditions:AcceptedandProgrammed(Gateway API condition types). - Deletion:
registry.Unregister,helmgateway.Uninstall, remove finalizer.
- Resolve parent
Gatewayfromspec.parentRefs(KindGateway,Groupgateway.networking.k8s.ioor unset / default handling as implemented). - Load parent Gateway; confirm managed gatewayClassName.
- Finalizer:
gateway.api-platform.wso2.com/httproute-finalizer. - Registry lookup by parent
namespace/name(not label-basedRestApimatching). - Build
APIConfigData(APIPolicy, rule ExtensionRefs). - Resolve
params.valueFrom(secretKeyRef→ Secret.data,configMapKeyRef→ ConfigMap.data / binaryData); replace with string values before gateway-controller REST. - Serialize → YAML via
gatewayclient.BuildRestAPIYAML(apiVersiongateway.api-platform.wso2.com/v1alpha1,KindRestApi). - Auth:
GetAuthSettingsForRegistryGateway(Helm values ConfigMap onGatewayInfoif set, elseAPIGatewayCR with same name if present). RestAPIExists+DeployRestAPI; updatestatus.parentswithControllerNamegateway.api-platform.wso2.com/gateway-operator.- On success, info log:
HTTPRoute deployed to gateway(includes handle and endpoint, as implemented). - Deletion:
DeleteRestAPIfor the handle, then remove finalizer.
- Services: On create/update/delete, list
HTTPRoutes and enqueue those whose backendRefs reference that Service. APIPolicy: Enqueue the HTTPRoute inspec.targetRefwhen set; otherwise enqueue HTTPRoutes in the same namespace that reference this policy via ruleExtensionRef.- Secrets: When a Secret changes (except service-account token type), enqueue HTTPRoutes affected by any
APIPolicywhoseparamsreference that Secret viavalueFrom.secretKeyRef(target HTTPRoute or ExtensionRef-only policies). - ConfigMaps: Symmetric to Secrets — enqueue HTTPRoutes whose APIPolicy
paramsreference the ConfigMap viavalueFrom.configMapKeyRefso edits to non-sensitive policy inputs trigger redeploy.
ClusterRole rules include gateway.networking.k8s.io gateways and httproutes (including status and finalizers), core services, configmaps, secrets, and gateway.api-platform.wso2.com apipolicies as required for mapping, resolution, and watches (config/rbac/role.yaml).
- Registry key is
namespace/nameof the logical gateway CR (APIGatewayname or KubernetesGatewayname — not the Helm release name). - Collision risk: An
APIGatewayand a KubernetesGatewaywith the samemetadata.nameandmetadata.namespaceshare the same registry slot — avoid duplicate names if both models are used in one namespace.
- HTTPRoute → APIConfigData: Oriented to HTTP routes; single-backend assumptions in places (first resolving Service
backendRefdrivesupstream.main.url). - Advanced route filters (rewrite, redirect, weighted backends), GRPCRoute, and rich TLS/vhost mapping into
APIConfigDataare out of scope unless extended. - Cross-namespace backends without ReferenceGrant are not fully modelled; errors surface via reconcile / status where possible.
| Area | Location |
|---|---|
| Gateway API scheme registration | cmd/main.go (gatewayv1.AddToScheme) |
Kubernetes Gateway reconciler |
internal/controller/k8s_gateway_controller.go |
HTTPRoute reconciler |
internal/controller/httproute_controller.go |
| Service / APIPolicy / Secret / ConfigMap → HTTPRoute enqueue | internal/controller/httproute_enqueue.go |
HTTPRoute → APIConfigData mapping |
internal/controller/httproute_mapper.go |
| Policy loading | internal/controller/httproute_policies.go |
valueFrom Secret / ConfigMap resolution |
internal/controller/httproute_policy_params_resolve.go |
APIPolicy CRD |
api/v1alpha1/policy_types.go |
| Annotation keys | internal/controller/gateway_api_annotations.go |
| Shared Helm install/uninstall | internal/helmgateway/deploy.go |
| Shared manifest/registry helpers | internal/controller/gateway_infra.go |
| REST payload + HTTP calls | internal/gatewayclient/ |
Registry extensions (HelmValuesConfigMapName, FromGatewayAPI) |
internal/registry/gateway_registry.go |
Auth: ConfigMap vs APIGateway |
internal/auth/auth_helper.go (GetAuthSettingsForRegistryGateway, GetDeploymentAuthFromConfigMap) |
RestApi path (shared client) |
internal/controller/restapi_controller.go |