janhoy commented on code in PR #843:
URL: https://github.com/apache/solr-operator/pull/843#discussion_r4468575600


##########
controllers/util/gateway_util_backendtls.go:
##########
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util
+
+import (
+       solr "github.com/apache/solr-operator/api/v1beta1"
+       "github.com/go-logr/logr"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+// GenerateCommonBackendTLSPolicy creates a BackendTLSPolicy for the common 
Solr service
+func GenerateCommonBackendTLSPolicy(solrCloud *solr.SolrCloud) 
*gatewayv1.BackendTLSPolicy {
+       if solrCloud.Spec.SolrAddressability.External.Gateway == nil ||
+               
solrCloud.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy == nil {
+               return nil
+       }
+
+       // Get the full FQDN for hostname validation
+       domainName := solrCloud.Spec.SolrAddressability.External.DomainName
+       fqdn := solrCloud.ExternalCommonUrl(domainName, false)

Review Comment:
   Related to Copilot's comment at `tests/e2e/solrcloud_gateway_test.go:196`. 
Before the assertions are changed to match the code, this needs a decision, 
because three places in the PR disagree:
   
   - **Generators** emit the external hostname — `ExternalCommonUrl(domainName, 
false)` and `ExternalNodeUrl(nodeName, domainName, false)` at 
`controllers/util/gateway_util_backendtls.go:36` and `:107`, e.g. 
`solr-ns-example-solrcloud.solr.example.com`.
   - **Tests** expect the bare Service name 
(`tests/e2e/solrcloud_gateway_test.go:183` and `:196`).
   - **Docs** show the bare Service name 
(`docs/modules/solr-cloud/pages/gateway-api.adoc:186`).
   
   The value matters because the Gateway sends it as SNI and validates the Solr 
pod's certificate against it. The external hostname therefore only works when 
the Solr certificate carries the external names as SANs (the `tls.adoc` 
examples do, via wildcards); a certificate with only in-cluster names will fail 
the TLS handshake, and that failure shows up only in the Gateway controller's 
logs.
   
   Whichever is intended: align generator, tests and the docs example, and 
state the resulting certificate-SAN requirement in `gateway-api.adoc`, since it 
currently appears nowhere.



##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package e2e
+
+import (
+       "context"
+       solrv1beta1 "github.com/apache/solr-operator/api/v1beta1"
+       "github.com/apache/solr-operator/controllers/util"
+       . "github.com/onsi/ginkgo/v2"
+       . "github.com/onsi/gomega"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+var _ = Describe("E2E - SolrCloud - Gateway API", func() {

Review Comment:
   All line references in this comment are in 
`tests/e2e/solrcloud_gateway_test.go`.
   
   Flagging this because Copilot's findings at 
`tests/e2e/solrcloud_gateway_test.go:29`, `:115` and `:153` are static 
inferences, and I can confirm all three are real. I switched 
`tests/e2e/solrcloud_gateway_test.go:29` to `FDescribe` locally (nothing else 
changed) and ran against a fresh KinD cluster — Gateway API v1.6.1 standard 
channel, k8s v1.33.7, `solr:9.11.0`:
   
   ```
   Summarizing 3 Failures:
     [FAIL] ... Can Remove HTTPRoutes and Services when changing addressability 
[It] Can adapt to changing needs
     tests/e2e/solrcloud_gateway_test.go:115
     [FAIL] ... BackendTLSPolicy Management [It] Creates and manages 
BackendTLSPolicy resources
     tests/e2e/solrcloud_gateway_test.go:179
     [FAIL] ... BackendTLSPolicy Management [It] Cleans up BackendTLSPolicy 
when changing from Gateway method
     tests/e2e/solrcloud_gateway_test.go:256
   
   Ran 3 of 34 Specs in 351.952 seconds
   FAIL! -- 0 Passed | 3 Failed | 0 Pending | 31 Skipped
   ```
   
   Before that change, the dry run confirms the specs are excluded entirely, 
and the suite still reports success:
   
   ```
   Will run 31 of 34 specs
   •••••••••••••••••••••••••••••SSS••
   SUCCESS! -- 31 Passed | 0 Failed | 0 Pending | 3 Skipped
   ```
   
   For the TLS-fixture issue, the operator log is unambiguous — the policy is 
never created, so both specs time out after 180s rather than flaking:
   
   ```json
   {"level":"error","msg":"Reconciler 
error","SolrCloud":{"name":"foo","namespace":"solr-e2e-2"},
    "error":"invalid config: 
`spec.customSolrKubeOptions.addressability.external.gateway.backendTLSPolicy` 
requires `spec.solrTLS` to be configured, because BackendTLSPolicy instructs 
the gateway to use TLS when connecting to Solr backends"}
   ```
   
   Note this compounds with the e2e job being hard-disabled in CI — 
`.github/workflows/e2e-tests.yaml:44` is a literal `if: false`, which is why 
every e2e run shows `skipped` on `main` as well as on PRs, and its matrix has 
rotted meanwhile (`go-version: '1.26'` against `go.mod`'s 1.27, `kube-version: 
'v1.26.6'`). So even a correctly focused suite would not have run here; that is 
a separate problem from this PR, but it means a green CI tells us nothing about 
this feature. Worth confirming the specs pass in a real run — today that means 
locally, or with the workflow re-enabled — before merge.



##########
controllers/solr_gateway_util.go:
##########
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import (
+       "context"
+       "fmt"
+
+       solrv1beta1 "github.com/apache/solr-operator/api/v1beta1"
+       "github.com/apache/solr-operator/controllers/util"
+       "github.com/go-logr/logr"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/labels"
+       "k8s.io/apimachinery/pkg/types"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+func (r *SolrCloudReconciler) reconcileGatewayResources(ctx context.Context, 
cloud *solrv1beta1.SolrCloud, solrNodeNames []string, logger logr.Logger) error 
{
+       extAddressabilityOpts := cloud.Spec.SolrAddressability.External
+
+       // If not using Gateway method, clean up any existing HTTPRoutes and 
BackendTLSPolicy resources
+       if extAddressabilityOpts == nil || extAddressabilityOpts.Method != 
solrv1beta1.Gateway {
+               return r.cleanupGatewayResources(ctx, cloud, logger)
+       }
+
+       // Validate that gateway config is provided
+       if extAddressabilityOpts.Gateway == nil || 
len(extAddressabilityOpts.Gateway.ParentRefs) == 0 {
+               r.Recorder.Event(cloud, corev1.EventTypeWarning, 
"InvalidConfiguration", "gateway.parentRefs is required when using 
method=Gateway")
+               return fmt.Errorf("gateway.parentRefs is required when using 
method=Gateway")
+       }
+
+       // Validate that BackendTLSPolicy requires solrTLS to be configured
+       if extAddressabilityOpts.HasBackendTLSPolicy() && cloud.Spec.SolrTLS == 
nil {

Review Comment:
   Two things about `controllers/solr_gateway_util.go:50-54`.
   
   Returning an `error` from `Reconcile` for a static user misconfiguration 
puts the controller into controller-runtime's rate-limited requeue (exponential 
backoff) that can never succeed. The blast radius is much larger than the 
Gateway resources: the call site at 
`controllers/solrcloud_controller.go:468-470` returns straight out of 
`Reconcile`, which is before pod initialization (`:493`), status computation 
(`:501`), the managed scale-up/scale-down and rolling-update operations 
(`:525-622`) and unused-Service cleanup (`:708`). Within the Gateway code the 
`return` at `controllers/solr_gateway_util.go:51-54` also precedes 
`reconcileGatewayHTTPRoutes` at `:56`. So one wrong field freezes the cloud's 
reported status, blocks scaling and rolling updates, and stops HTTPRoute 
reconciliation, until the user happens to notice an Event. A terminal 
misconfiguration is better surfaced as a status condition alongside the warning 
Event you already emit, and then skipped, so the remaining Gateway 
 resources keep converging. Same argument for the `parentRefs` validation at 
`controllers/solr_gateway_util.go:45-48`.
   
   Separately, the returned error names a field path that does not exist:
   
   ```
   spec.customSolrKubeOptions.addressability.external.gateway.backendTLSPolicy
   ```
   
   The real path is 
`spec.solrAddressability.external.gateway.backendTLSPolicy`. The Event message 
on the line above uses the correct short form, so only the `fmt.Errorf` needs 
fixing.



##########
controllers/solr_gateway_util.go:
##########
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controllers
+
+import (
+       "context"
+       "fmt"
+
+       solrv1beta1 "github.com/apache/solr-operator/api/v1beta1"
+       "github.com/apache/solr-operator/controllers/util"
+       "github.com/go-logr/logr"
+       corev1 "k8s.io/api/core/v1"
+       "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/labels"
+       "k8s.io/apimachinery/pkg/types"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+       gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+func (r *SolrCloudReconciler) reconcileGatewayResources(ctx context.Context, 
cloud *solrv1beta1.SolrCloud, solrNodeNames []string, logger logr.Logger) error 
{

Review Comment:
   Grepping `controllers/*_test.go` and `api/v1beta1/*_test.go` for `HTTPRoute` 
or `BackendTLSPolicy` returns nothing. Combined with the de-focused e2e specs 
Copilot flagged, `controllers/solr_gateway_util.go`, 
`controllers/util/gateway_util.go` and 
`controllers/util/gateway_util_backendtls.go` currently have no executed 
coverage from any test.
   
   That matters here beyond the usual argument: the envtest-based controller 
tests would have caught most of what Copilot found by static reading. Nothing 
in this feature needs real traffic or a Gateway controller — the generated 
object shape, the `hideCommon`/`hideNodes` transitions, the 
cleanup-on-method-change path, the `backendTLSPolicy`-requires-`solrTLS` 
rejection, the hostname question in my comment on 
`controllers/util/gateway_util_backendtls.go:36`, and the default-normalization 
churn Copilot raised at `controllers/util/gateway_util.go:198` are all 
API-server-only assertions. `controllers/solrcloud_controller_ingress_test.go` 
is a good template, and a round-trip "unchanged route requires no update" test 
there would be cheaper than the e2e equivalent.



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.

Review Comment:
   This is my main concern, based on having set Gateway API up by hand for a 
production Solr cluster.
   
   `docs/modules/solr-cloud/pages/gateway-api.adoc:22` says "Gateway API is a 
vendor-neutral, Kubernetes-native API for managing ingress traffic and is the 
successor to the Ingress API." True upstream, but in this operator the Gateway 
path is strictly narrower than the Ingress path, and the page never says so. A 
reader has no way to discover that `method: Ingress` → `method: Gateway` loses 
capability.
   
   Things the page should state plainly and currently doesn't:
   
   - `ingressTLSTermination` does not apply to `method: Gateway` 
(`HasIngressTLSTermination()` at `api/v1beta1/solrcloud_types.go:1601` is gated 
to `Method == Ingress`). Edge TLS termination must be preconfigured on the 
Gateway listener; the operator cannot request it or name a Secret for it. See 
also my comment on `withDefaults` in `api/v1beta1/solrcloud_types.go`.
   - `backendTLSPolicy` is **upstream** TLS, Gateway → Solr. It is not 
client/edge mTLS and not a client-authentication mechanism. Worth stating 
explicitly because the name invites exactly that misreading — client 
certificate validation is a Gateway-level concern 
(`gateway.spec.tls.default.frontendValidation`, GEP-91), not something an 
HTTPRoute or BackendTLSPolicy can express.
   - There is no `customSolrKubeOptions` equivalent for HTTPRoutes, so the 
`annotations`/`labels`/`ingressClassName` escape hatch documented at 
`docs/modules/solr-cloud/pages/addressability.adoc:48` has no Gateway 
counterpart. See my comment on `SolrGatewayOptions` in 
`api/v1beta1/solrcloud_types.go` for why that matters in practice.



##########
api/v1beta1/solrcloud_types.go:
##########
@@ -626,6 +639,107 @@ type SolrIngressTLSTermination struct {
        TLSSecret string `json:"tlsSecret,omitempty"`
 }
 
+// SolrGatewayOptions defines how a SolrCloud should be exposed via Kubernetes 
Gateway API
+type SolrGatewayOptions struct {

Review Comment:
   Flagging this as a design question rather than a defect, because it's the 
thing that blocked a real deployment for me.
   
   With a service mesh and authentication delegated to the edge (Keycloak + 
oauth2-proxy), the HTTPRoute has to reference a preconfigured middleware. On 
Traefik that is an `ExtensionRef` filter, documented upstream as:
   
   ```yaml
   filters:
     - type: ExtensionRef
       extensionRef:
         group: traefik.io
         kind: Middleware
         name: edge-auth-http
   ```
   
   
(https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/gateway-api/#using-traefik-middleware-as-httproute-filter)
   
   On this branch that is not expressible, and there is no per-route workaround:
   
   - `SolrGatewayOptions` (`api/v1beta1/solrcloud_types.go:643`) exposes only 
`parentRefs`, `additionalHostnames`, `annotations`, `labels`, 
`backendTLSPolicy`. No `filters`, `matches`, `timeouts`, or raw-rule 
passthrough.
   - The generated route is hardcoded to one rule with one `backendRef` and no 
filters (`controllers/util/gateway_util.go:84-95` for the common route, 
`controllers/util/gateway_util.go:155-169` for node routes).
   - Editing the live HTTPRoute doesn't survive: `CopyHTTPRouteFields` does 
`to.Spec.Rules = from.Spec.Rules` on any difference 
(`controllers/util/gateway_util.go:207-210`).
   - Pre-creating the HTTPRoute yourself doesn't work either: the reconciler 
calls `util.OvertakeControllerRef` (`controllers/solr_gateway_util.go:77` and 
`controllers/solr_gateway_util.go:115`) and then overwrites it, so it adopts 
and rewrites a route you made rather than leaving it alone.
   
   Net effect: anyone doing edge auth, rate limiting, header manipulation or 
path rewriting — most of what people currently do with Ingress annotations on a 
Solr cloud — cannot express it on the route. The only remaining option is 
policy attached at Gateway or listener scope by the platform team, where the 
implementation supports that; it is coarser than per-route, and not what a team 
migrating a per-Ingress annotation setup expects to have to rebuild.
   
   Useful for scoping: there is no portable standard-channel answer upstream 
yet either. GEP-1494 ("HTTP Auth in Gateway API", which introduces the 
`ExternalAuth` HTTPRoute filter) is still **Experimental** and ships only in 
experimental-channel CRDs — https://gateway-api.sigs.k8s.io/geps/gep-1494/ — so 
an `ExtensionRef` passthrough is the only thing that helps users today.
   
   Two ways forward, either fine by me:
   
   1. Add a passthrough — e.g. `gateway.filters` typed as 
`[]gatewayv1.HTTPRouteFilter`, or an `httpRouteOptions` member under 
`customSolrKubeOptions` matching the existing `ingressOptions` precedent. Note 
this interacts with Copilot's comment at 
`controllers/util/gateway_util.go:198`: once user-supplied fields exist in the 
spec, the wholesale `Spec.Rules` replacement has to become a per-field merge 
like `CopyIngressFields` (`controllers/util/common.go:252-327`), and the 
default-normalization fix Copilot asks for becomes a prerequisite rather than 
just an efficiency win.
   2. Ship as-is but say so plainly in the docs: Gateway mode supports plain 
host-based routing only, and anything needing filters or 
implementation-specific policy should stay on `method: Ingress` for now.
   
   What I'd rather avoid is shipping without either, since the "successor to 
Ingress" framing I commented on at 
`docs/modules/solr-cloud/pages/gateway-api.adoc:22` will lead people to migrate 
and then discover this.



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.
+
+When you configure `spec.solrAddressability.external.method: Gateway`, the 
Solr Operator creates and manages HTTPRoute resources
+that route external traffic to your Solr nodes through an existing Gateway 
resource in your cluster.
+
+== What Gets Created
+
+When Gateway mode is enabled, the Solr Operator automatically creates the 
following Kubernetes resources:
+
+=== HTTPRoute Resources
+
+The operator creates HTTPRoute resources to route traffic to Solr:
+
+* **Common HTTPRoute**: Routes traffic to the common Solr service 
(load-balanced across all nodes)
+** Named: `<solrcloud-name>-solrcloud-common`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud.<domainName>`
+* **Per-Node HTTPRoutes**: Routes traffic directly to individual Solr nodes 
(when `hideNodes: false`)
+** Named: `<solrcloud-name>-solrcloud-<node-index>`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud-<node-index>.<domainName>`
+
+All HTTPRoutes are owned by the SolrCloud resource and will be automatically 
cleaned up when the SolrCloud is deleted.
+
+=== Services
+
+The same services are created as with other external addressability methods, 
depending on whether per-node Services are required:
+
+* Common service (load-balanced)
+* Headless service (when not using per-node services)
+* Per-node services (when individual node access is enabled)
+
+== What the Operator Assumes
+
+The Gateway mode assumes the following resources already exist in your cluster:
+
+. **Gateway API CRDs**: The Gateway API CRDs must be installed in your cluster
+** Minimum version: v1.4.0 (required for `BackendTLSPolicy` support)
+** Required CRDs: `Gateway`, `GatewayClass`, `HTTPRoute`, `BackendTLSPolicy` 
(optional, for TLS backends)
+. **Gateway Resource**: A Gateway resource must already exist and be managed 
by your platform team
+** The operator only manages HTTPRoute resources, not the Gateway itself
+** The Gateway must be configured with appropriate listeners and TLS 
termination (if needed)
+. **Gateway Controller**: A Gateway controller implementation must be running 
(e.g., NGINX Gateway Fabric, Istio, Envoy Gateway)
+
+The Solr Operator does **not** create or manage Gateway or GatewayClass 
resources - these are infrastructure-level resources
+typically managed by platform administrators and are specific to the Gateway 
implementation deployed in your cluster (e.g., NGINX Gateway Fabric, Istio, 
Envoy Gateway).
+
+=== CRD Detection
+
+Gateway support is enabled automatically; there is no flag to turn it on or 
off.
+At startup, the operator queries the Kubernetes API server to determine 
whether the Gateway API CRDs are installed.
+Gateway support is enabled only when **both** the `HTTPRoute` and 
`BackendTLSPolicy` types (API group `gateway.networking.k8s.io/v1`) are 
registered.
+If either is missing, the operator logs that Gateway support is disabled, does 
not watch these types, and ignores `method: Gateway` configurations.
+
+IMPORTANT: Detection happens only once, when the operator starts. If you 
install (or remove) the Gateway API CRDs while the operator is running, you 
must restart the operator pod for the change to take effect.
+
+== Configuration
+
+Configure Gateway mode in your SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        # Reference to existing Gateway resource(s)
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+          sectionName: https  # Optional: specific listener name
+
+        # Optional: annotations to add to HTTPRoute resources
+        annotations:
+          example.com/custom-annotation: "value"
+
+        # Optional: labels to add to HTTPRoute resources
+        labels:
+          app: solr
+          environment: production
+----
+
+=== Configuration Options
+
+* **`parentRefs`** (required): List of Gateway resources to attach HTTPRoutes 
to
+** `name`: Name of the Gateway resource
+** `namespace`: Namespace of the Gateway (can be different from SolrCloud 
namespace)
+** `sectionName`: Optional listener name within the Gateway
+* **`annotations`**: Optional annotations to add to all created HTTPRoute 
resources
+* **`labels`**: Optional labels to add to all created HTTPRoute resources
+* **`domainName`**: Base domain for constructing hostnames
+* **`useExternalAddress`**: When true, Solr nodes use external addresses for 
inter-node communication
+* **`hideCommon`**: Set to true to skip creating the common HTTPRoute 
(default: false)
+* **`hideNodes`**: Set to true to skip creating per-node HTTPRoutes (default: 
false)
+
+== Backend TLS Policy
+
+When using TLS-enabled Solr (`spec.solrTLS` is configured), the Solr Operator 
automatically sets `appProtocol: https` on all Services.
+The operator also supports creating `BackendTLSPolicy` resources to configure 
secure connections between the Gateway and Solr backend services.
+
+=== Configuring BackendTLSPolicy
+
+The Solr Operator can automatically create and manage `BackendTLSPolicy` 
resources (Gateway API v1) when configured in the SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  # Enable TLS for Solr
+  solrTLS:
+    pkcs12Secret:
+      name: solr-tls-cert
+      key: keystore.p12
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      gateway:
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+        # Configure BackendTLSPolicy for secure backend connections
+        backendTLSPolicy:
+          # Option 1: Reference CA certificate from a ConfigMap (default)
+          caCertificateRefs:
+          - name: solr-ca-cert
+            # kind: ConfigMap  # Optional, defaults to ConfigMap
+            # group: ""        # Optional, defaults to "" (core API)
+
+          # Option 2: Use well-known CA certificates
+          # wellKnownCACertificates: "System"
+----
+
+The generated `BackendTLSPolicy` will look like:
+
+[source,yaml]
+----
+apiVersion: gateway.networking.k8s.io/v1
+kind: BackendTLSPolicy
+metadata:
+  name: example-solrcloud-common
+  namespace: solr-ns
+spec:
+  targetRefs:
+  - name: example-solrcloud-common
+    kind: Service
+    group: ""
+  validation:
+    hostname: example-solrcloud-common
+    caCertificateRefs:
+    - group: ""
+      kind: ConfigMap
+      name: solr-ca-cert
+----
+
+=== BackendTLSPolicy Options
+
+The `backendTLSPolicy` field supports two mutually exclusive options:
+
+* **`caCertificateRefs`**: References to Kubernetes ConfigMaps or Secrets 
containing CA certificates
+** `name`: Name of the ConfigMap or Secret (required)
+** `kind`: Resource kind (optional, defaults to "ConfigMap")
+** `group`: API group (optional, defaults to "" for core API)
+** Maximum of 8 certificate references
+** **Note**: ConfigMaps must contain the CA certificate in a key named `ca.crt`
+* **`wellKnownCACertificates`**: Use system CA certificates (e.g., "System")
+** Only one of `caCertificateRefs` or `wellKnownCACertificates` can be 
specified
+
+When configured, the operator creates:
+
+* `BackendTLSPolicy` for the common service (if not hidden)
+* `BackendTLSPolicy` for each node service (if not hidden)
+
+These policies configure the Gateway to validate backend TLS certificates and 
establish secure connections to Solr pods.
+
+=== Gateway Implementation Support
+
+NOTE: `BackendTLSPolicy` is part of the Gateway API standard (GA in v1.4.0+), 
but support varies by implementation.
+
+[cols="1,1",options="header"]
+|===
+| Gateway Implementation | BackendTLSPolicy Support
+
+| **Standard Gateway API** | ✅ v1 (GA as of v1.4.0)
+| **Envoy Gateway** | ✅ Full support
+| **kgateway** | ✅ Full support
+| **Istio** | ⚠️ Use `DestinationRule` instead
+| **NGINX Gateway Fabric** | ✅ Supported
+| **GKE Gateway** | ⚠️ Automatic via `appProtocol`
+|===
+
+**Requirements**: Gateway API v1.4.0 or later is required for 
`BackendTLSPolicy` support.
+
+Refer to your Gateway implementation's documentation for specific backend TLS 
configuration requirements.
+
+== Complete Example
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+        annotations:
+          example.com/rate-limit: "1000"

Review Comment:
   The Complete Example at 
`docs/modules/solr-cloud/pages/gateway-api.adoc:255-256` shows:
   
   ```yaml
   annotations:
     example.com/rate-limit: "1000"
   ```
   
   This mirrors the Ingress annotation idiom and reads as working edge 
configuration, but HTTPRoute annotations are not a portable configuration 
surface in Gateway API — rate limiting, auth, rewrites and timeouts attach via 
`filters` or policy objects. I'd drop this example or replace it with something 
that is honestly just metadata. Same concern more mildly at 
`docs/modules/solr-cloud/pages/gateway-api.adoc:103-105` and `:119`.
   
   Related implementation asymmetry worth fixing in the same pass: 
`controllers/util/gateway_util.go:191` does `to.Annotations = 
from.Annotations`, replacing the whole map, so an annotation added out-of-band 
by a user or another controller is deleted on the next reconcile. The Ingress 
path merges and never deletes (`controllers/util/common.go:74-80`), and relies 
on that to let users override `nginx.ingress.kubernetes.io/backend-protocol` 
(`controllers/util/solr_util.go:1143-1167`).



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.
+
+When you configure `spec.solrAddressability.external.method: Gateway`, the 
Solr Operator creates and manages HTTPRoute resources
+that route external traffic to your Solr nodes through an existing Gateway 
resource in your cluster.
+
+== What Gets Created
+
+When Gateway mode is enabled, the Solr Operator automatically creates the 
following Kubernetes resources:
+
+=== HTTPRoute Resources
+
+The operator creates HTTPRoute resources to route traffic to Solr:
+
+* **Common HTTPRoute**: Routes traffic to the common Solr service 
(load-balanced across all nodes)
+** Named: `<solrcloud-name>-solrcloud-common`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud.<domainName>`
+* **Per-Node HTTPRoutes**: Routes traffic directly to individual Solr nodes 
(when `hideNodes: false`)
+** Named: `<solrcloud-name>-solrcloud-<node-index>`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud-<node-index>.<domainName>`
+
+All HTTPRoutes are owned by the SolrCloud resource and will be automatically 
cleaned up when the SolrCloud is deleted.
+
+=== Services
+
+The same services are created as with other external addressability methods, 
depending on whether per-node Services are required:
+
+* Common service (load-balanced)
+* Headless service (when not using per-node services)
+* Per-node services (when individual node access is enabled)
+
+== What the Operator Assumes
+
+The Gateway mode assumes the following resources already exist in your cluster:
+
+. **Gateway API CRDs**: The Gateway API CRDs must be installed in your cluster
+** Minimum version: v1.4.0 (required for `BackendTLSPolicy` support)
+** Required CRDs: `Gateway`, `GatewayClass`, `HTTPRoute`, `BackendTLSPolicy` 
(optional, for TLS backends)
+. **Gateway Resource**: A Gateway resource must already exist and be managed 
by your platform team
+** The operator only manages HTTPRoute resources, not the Gateway itself
+** The Gateway must be configured with appropriate listeners and TLS 
termination (if needed)
+. **Gateway Controller**: A Gateway controller implementation must be running 
(e.g., NGINX Gateway Fabric, Istio, Envoy Gateway)
+
+The Solr Operator does **not** create or manage Gateway or GatewayClass 
resources - these are infrastructure-level resources
+typically managed by platform administrators and are specific to the Gateway 
implementation deployed in your cluster (e.g., NGINX Gateway Fabric, Istio, 
Envoy Gateway).
+
+=== CRD Detection
+
+Gateway support is enabled automatically; there is no flag to turn it on or 
off.
+At startup, the operator queries the Kubernetes API server to determine 
whether the Gateway API CRDs are installed.
+Gateway support is enabled only when **both** the `HTTPRoute` and 
`BackendTLSPolicy` types (API group `gateway.networking.k8s.io/v1`) are 
registered.
+If either is missing, the operator logs that Gateway support is disabled, does 
not watch these types, and ignores `method: Gateway` configurations.
+
+IMPORTANT: Detection happens only once, when the operator starts. If you 
install (or remove) the Gateway API CRDs while the operator is running, you 
must restart the operator pod for the change to take effect.
+
+== Configuration
+
+Configure Gateway mode in your SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        # Reference to existing Gateway resource(s)
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+          sectionName: https  # Optional: specific listener name
+
+        # Optional: annotations to add to HTTPRoute resources
+        annotations:
+          example.com/custom-annotation: "value"
+
+        # Optional: labels to add to HTTPRoute resources
+        labels:
+          app: solr
+          environment: production
+----
+
+=== Configuration Options
+
+* **`parentRefs`** (required): List of Gateway resources to attach HTTPRoutes 
to
+** `name`: Name of the Gateway resource
+** `namespace`: Namespace of the Gateway (can be different from SolrCloud 
namespace)
+** `sectionName`: Optional listener name within the Gateway
+* **`annotations`**: Optional annotations to add to all created HTTPRoute 
resources
+* **`labels`**: Optional labels to add to all created HTTPRoute resources
+* **`domainName`**: Base domain for constructing hostnames
+* **`useExternalAddress`**: When true, Solr nodes use external addresses for 
inter-node communication
+* **`hideCommon`**: Set to true to skip creating the common HTTPRoute 
(default: false)
+* **`hideNodes`**: Set to true to skip creating per-node HTTPRoutes (default: 
false)
+
+== Backend TLS Policy
+
+When using TLS-enabled Solr (`spec.solrTLS` is configured), the Solr Operator 
automatically sets `appProtocol: https` on all Services.
+The operator also supports creating `BackendTLSPolicy` resources to configure 
secure connections between the Gateway and Solr backend services.
+
+=== Configuring BackendTLSPolicy
+
+The Solr Operator can automatically create and manage `BackendTLSPolicy` 
resources (Gateway API v1) when configured in the SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  # Enable TLS for Solr
+  solrTLS:
+    pkcs12Secret:
+      name: solr-tls-cert
+      key: keystore.p12
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      gateway:
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+        # Configure BackendTLSPolicy for secure backend connections
+        backendTLSPolicy:
+          # Option 1: Reference CA certificate from a ConfigMap (default)
+          caCertificateRefs:
+          - name: solr-ca-cert
+            # kind: ConfigMap  # Optional, defaults to ConfigMap
+            # group: ""        # Optional, defaults to "" (core API)
+
+          # Option 2: Use well-known CA certificates
+          # wellKnownCACertificates: "System"
+----
+
+The generated `BackendTLSPolicy` will look like:
+
+[source,yaml]
+----
+apiVersion: gateway.networking.k8s.io/v1
+kind: BackendTLSPolicy
+metadata:
+  name: example-solrcloud-common
+  namespace: solr-ns
+spec:
+  targetRefs:
+  - name: example-solrcloud-common
+    kind: Service
+    group: ""
+  validation:
+    hostname: example-solrcloud-common
+    caCertificateRefs:
+    - group: ""
+      kind: ConfigMap
+      name: solr-ca-cert
+----
+
+=== BackendTLSPolicy Options
+
+The `backendTLSPolicy` field supports two mutually exclusive options:
+
+* **`caCertificateRefs`**: References to Kubernetes ConfigMaps or Secrets 
containing CA certificates
+** `name`: Name of the ConfigMap or Secret (required)
+** `kind`: Resource kind (optional, defaults to "ConfigMap")
+** `group`: API group (optional, defaults to "" for core API)
+** Maximum of 8 certificate references
+** **Note**: ConfigMaps must contain the CA certificate in a key named `ca.crt`
+* **`wellKnownCACertificates`**: Use system CA certificates (e.g., "System")
+** Only one of `caCertificateRefs` or `wellKnownCACertificates` can be 
specified
+
+When configured, the operator creates:
+
+* `BackendTLSPolicy` for the common service (if not hidden)
+* `BackendTLSPolicy` for each node service (if not hidden)
+
+These policies configure the Gateway to validate backend TLS certificates and 
establish secure connections to Solr pods.
+
+=== Gateway Implementation Support
+
+NOTE: `BackendTLSPolicy` is part of the Gateway API standard (GA in v1.4.0+), 
but support varies by implementation.
+
+[cols="1,1",options="header"]

Review Comment:
   The table at `docs/modules/solr-cloud/pages/gateway-api.adoc:217-227` 
asserts "Full support" for five named implementations. That is a maintenance 
liability, it omits Traefik (plus Cilium, Kong, HAProxy), and this repo cannot 
test any of it. I'd replace it with one sentence and a link to the upstream 
conformance reports, which are maintained for us: 
https://gateway-api.sigs.k8s.io/implementations/
   
   For balance: the version claim in the same section **is** accurate. 
`backendtlspolicies.gateway.networking.k8s.io` carries 
`gateway.networking.k8s.io/channel: standard` on a live v1.6.1 install, and 
upstream confirms "Standard Channel since v1.4.0". Might be worth linking the 
canonical API-type page next to the GEP: 
https://gateway-api.sigs.k8s.io/reference/api-types/policy/backendtlspolicy/



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.
+
+When you configure `spec.solrAddressability.external.method: Gateway`, the 
Solr Operator creates and manages HTTPRoute resources
+that route external traffic to your Solr nodes through an existing Gateway 
resource in your cluster.
+
+== What Gets Created
+
+When Gateway mode is enabled, the Solr Operator automatically creates the 
following Kubernetes resources:
+
+=== HTTPRoute Resources
+
+The operator creates HTTPRoute resources to route traffic to Solr:
+
+* **Common HTTPRoute**: Routes traffic to the common Solr service 
(load-balanced across all nodes)
+** Named: `<solrcloud-name>-solrcloud-common`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud.<domainName>`
+* **Per-Node HTTPRoutes**: Routes traffic directly to individual Solr nodes 
(when `hideNodes: false`)
+** Named: `<solrcloud-name>-solrcloud-<node-index>`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud-<node-index>.<domainName>`
+
+All HTTPRoutes are owned by the SolrCloud resource and will be automatically 
cleaned up when the SolrCloud is deleted.
+
+=== Services
+
+The same services are created as with other external addressability methods, 
depending on whether per-node Services are required:
+
+* Common service (load-balanced)
+* Headless service (when not using per-node services)
+* Per-node services (when individual node access is enabled)
+
+== What the Operator Assumes
+
+The Gateway mode assumes the following resources already exist in your cluster:
+
+. **Gateway API CRDs**: The Gateway API CRDs must be installed in your cluster
+** Minimum version: v1.4.0 (required for `BackendTLSPolicy` support)
+** Required CRDs: `Gateway`, `GatewayClass`, `HTTPRoute`, `BackendTLSPolicy` 
(optional, for TLS backends)
+. **Gateway Resource**: A Gateway resource must already exist and be managed 
by your platform team
+** The operator only manages HTTPRoute resources, not the Gateway itself
+** The Gateway must be configured with appropriate listeners and TLS 
termination (if needed)
+. **Gateway Controller**: A Gateway controller implementation must be running 
(e.g., NGINX Gateway Fabric, Istio, Envoy Gateway)
+
+The Solr Operator does **not** create or manage Gateway or GatewayClass 
resources - these are infrastructure-level resources
+typically managed by platform administrators and are specific to the Gateway 
implementation deployed in your cluster (e.g., NGINX Gateway Fabric, Istio, 
Envoy Gateway).
+
+=== CRD Detection
+
+Gateway support is enabled automatically; there is no flag to turn it on or 
off.
+At startup, the operator queries the Kubernetes API server to determine 
whether the Gateway API CRDs are installed.
+Gateway support is enabled only when **both** the `HTTPRoute` and 
`BackendTLSPolicy` types (API group `gateway.networking.k8s.io/v1`) are 
registered.
+If either is missing, the operator logs that Gateway support is disabled, does 
not watch these types, and ignores `method: Gateway` configurations.
+
+IMPORTANT: Detection happens only once, when the operator starts. If you 
install (or remove) the Gateway API CRDs while the operator is running, you 
must restart the operator pod for the change to take effect.
+
+== Configuration

Review Comment:
   **`solr` chart doesn't document `gateway`.** `helm/solr/values.yaml` 
documents `addressability.external.method: "Ingress"` and 
`ingressTLSTermination` but has no `gateway:` key. It passes through via 
`helm/solr/templates/solrcloud.yaml`, so it works, but it's undiscoverable.
   
   *(Anchored here because `helm/solr/values.yaml` is not part of this PR's 
diff.)*



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.
+
+When you configure `spec.solrAddressability.external.method: Gateway`, the 
Solr Operator creates and manages HTTPRoute resources
+that route external traffic to your Solr nodes through an existing Gateway 
resource in your cluster.
+
+== What Gets Created
+
+When Gateway mode is enabled, the Solr Operator automatically creates the 
following Kubernetes resources:
+
+=== HTTPRoute Resources
+
+The operator creates HTTPRoute resources to route traffic to Solr:
+
+* **Common HTTPRoute**: Routes traffic to the common Solr service 
(load-balanced across all nodes)
+** Named: `<solrcloud-name>-solrcloud-common`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud.<domainName>`
+* **Per-Node HTTPRoutes**: Routes traffic directly to individual Solr nodes 
(when `hideNodes: false`)
+** Named: `<solrcloud-name>-solrcloud-<node-index>`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud-<node-index>.<domainName>`
+
+All HTTPRoutes are owned by the SolrCloud resource and will be automatically 
cleaned up when the SolrCloud is deleted.
+
+=== Services
+
+The same services are created as with other external addressability methods, 
depending on whether per-node Services are required:
+
+* Common service (load-balanced)
+* Headless service (when not using per-node services)
+* Per-node services (when individual node access is enabled)
+
+== What the Operator Assumes
+
+The Gateway mode assumes the following resources already exist in your cluster:
+
+. **Gateway API CRDs**: The Gateway API CRDs must be installed in your cluster
+** Minimum version: v1.4.0 (required for `BackendTLSPolicy` support)
+** Required CRDs: `Gateway`, `GatewayClass`, `HTTPRoute`, `BackendTLSPolicy` 
(optional, for TLS backends)
+. **Gateway Resource**: A Gateway resource must already exist and be managed 
by your platform team
+** The operator only manages HTTPRoute resources, not the Gateway itself
+** The Gateway must be configured with appropriate listeners and TLS 
termination (if needed)
+. **Gateway Controller**: A Gateway controller implementation must be running 
(e.g., NGINX Gateway Fabric, Istio, Envoy Gateway)
+
+The Solr Operator does **not** create or manage Gateway or GatewayClass 
resources - these are infrastructure-level resources
+typically managed by platform administrators and are specific to the Gateway 
implementation deployed in your cluster (e.g., NGINX Gateway Fabric, Istio, 
Envoy Gateway).
+
+=== CRD Detection
+
+Gateway support is enabled automatically; there is no flag to turn it on or 
off.
+At startup, the operator queries the Kubernetes API server to determine 
whether the Gateway API CRDs are installed.
+Gateway support is enabled only when **both** the `HTTPRoute` and 
`BackendTLSPolicy` types (API group `gateway.networking.k8s.io/v1`) are 
registered.
+If either is missing, the operator logs that Gateway support is disabled, does 
not watch these types, and ignores `method: Gateway` configurations.
+
+IMPORTANT: Detection happens only once, when the operator starts. If you 
install (or remove) the Gateway API CRDs while the operator is running, you 
must restart the operator pod for the change to take effect.
+
+== Configuration
+
+Configure Gateway mode in your SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        # Reference to existing Gateway resource(s)
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+          sectionName: https  # Optional: specific listener name
+
+        # Optional: annotations to add to HTTPRoute resources
+        annotations:
+          example.com/custom-annotation: "value"
+
+        # Optional: labels to add to HTTPRoute resources
+        labels:
+          app: solr
+          environment: production
+----
+
+=== Configuration Options
+
+* **`parentRefs`** (required): List of Gateway resources to attach HTTPRoutes 
to
+** `name`: Name of the Gateway resource
+** `namespace`: Namespace of the Gateway (can be different from SolrCloud 
namespace)
+** `sectionName`: Optional listener name within the Gateway
+* **`annotations`**: Optional annotations to add to all created HTTPRoute 
resources
+* **`labels`**: Optional labels to add to all created HTTPRoute resources
+* **`domainName`**: Base domain for constructing hostnames
+* **`useExternalAddress`**: When true, Solr nodes use external addresses for 
inter-node communication
+* **`hideCommon`**: Set to true to skip creating the common HTTPRoute 
(default: false)
+* **`hideNodes`**: Set to true to skip creating per-node HTTPRoutes (default: 
false)
+
+== Backend TLS Policy
+
+When using TLS-enabled Solr (`spec.solrTLS` is configured), the Solr Operator 
automatically sets `appProtocol: https` on all Services.
+The operator also supports creating `BackendTLSPolicy` resources to configure 
secure connections between the Gateway and Solr backend services.
+
+=== Configuring BackendTLSPolicy
+
+The Solr Operator can automatically create and manage `BackendTLSPolicy` 
resources (Gateway API v1) when configured in the SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  # Enable TLS for Solr
+  solrTLS:
+    pkcs12Secret:
+      name: solr-tls-cert
+      key: keystore.p12
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      gateway:
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+        # Configure BackendTLSPolicy for secure backend connections
+        backendTLSPolicy:
+          # Option 1: Reference CA certificate from a ConfigMap (default)
+          caCertificateRefs:
+          - name: solr-ca-cert
+            # kind: ConfigMap  # Optional, defaults to ConfigMap
+            # group: ""        # Optional, defaults to "" (core API)
+
+          # Option 2: Use well-known CA certificates
+          # wellKnownCACertificates: "System"
+----
+
+The generated `BackendTLSPolicy` will look like:
+
+[source,yaml]
+----
+apiVersion: gateway.networking.k8s.io/v1
+kind: BackendTLSPolicy
+metadata:
+  name: example-solrcloud-common
+  namespace: solr-ns
+spec:
+  targetRefs:
+  - name: example-solrcloud-common
+    kind: Service
+    group: ""
+  validation:
+    hostname: example-solrcloud-common
+    caCertificateRefs:
+    - group: ""
+      kind: ConfigMap
+      name: solr-ca-cert
+----
+
+=== BackendTLSPolicy Options
+
+The `backendTLSPolicy` field supports two mutually exclusive options:
+
+* **`caCertificateRefs`**: References to Kubernetes ConfigMaps or Secrets 
containing CA certificates
+** `name`: Name of the ConfigMap or Secret (required)
+** `kind`: Resource kind (optional, defaults to "ConfigMap")
+** `group`: API group (optional, defaults to "" for core API)
+** Maximum of 8 certificate references
+** **Note**: ConfigMaps must contain the CA certificate in a key named `ca.crt`
+* **`wellKnownCACertificates`**: Use system CA certificates (e.g., "System")
+** Only one of `caCertificateRefs` or `wellKnownCACertificates` can be 
specified
+
+When configured, the operator creates:
+
+* `BackendTLSPolicy` for the common service (if not hidden)
+* `BackendTLSPolicy` for each node service (if not hidden)
+
+These policies configure the Gateway to validate backend TLS certificates and 
establish secure connections to Solr pods.
+
+=== Gateway Implementation Support
+
+NOTE: `BackendTLSPolicy` is part of the Gateway API standard (GA in v1.4.0+), 
but support varies by implementation.
+
+[cols="1,1",options="header"]
+|===
+| Gateway Implementation | BackendTLSPolicy Support
+
+| **Standard Gateway API** | ✅ v1 (GA as of v1.4.0)
+| **Envoy Gateway** | ✅ Full support
+| **kgateway** | ✅ Full support
+| **Istio** | ⚠️ Use `DestinationRule` instead
+| **NGINX Gateway Fabric** | ✅ Supported
+| **GKE Gateway** | ⚠️ Automatic via `appProtocol`
+|===
+
+**Requirements**: Gateway API v1.4.0 or later is required for 
`BackendTLSPolicy` support.
+
+Refer to your Gateway implementation's documentation for specific backend TLS 
configuration requirements.
+
+== Complete Example
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+        annotations:
+          example.com/rate-limit: "1000"
+        labels:
+          app: solr
+----
+
+This configuration will create:
+
+* HTTPRoute: `example-solrcloud-common` → 
`solr-ns-example-solrcloud.solr.example.com`
+* HTTPRoute: `example-solrcloud-0` → 
`solr-ns-example-solrcloud-0.solr.example.com`
+* HTTPRoute: `example-solrcloud-1` → 
`solr-ns-example-solrcloud-1.solr.example.com`
+* HTTPRoute: `example-solrcloud-2` → 
`solr-ns-example-solrcloud-2.solr.example.com`
+
+== References

Review Comment:
   On whether this is documentable without covering every implementation: I 
think yes, provided we document **our** delta and delegate per-implementation 
detail upstream. Documenting Traefik vs Istio vs Envoy Gateway vs Cilium 
behaviour in the Solr docs would rot within a release; the upstream conformance 
reports already do that job.
   
   Proposed shape for a short section in `gateway-api.adoc`:
   
   **What does not carry over from `method: Ingress`**
   
   - `customSolrKubeOptions.ingressOptions.annotations` — no HTTPRoute 
equivalent, and annotations are not a portable configuration surface in Gateway 
API. Anything expressed as an ingress-controller annotation (auth, rate limits, 
rewrites, body size, timeouts) must be re-expressed as a `filters` entry or an 
implementation-specific policy object attached by your platform team. See my 
comment on `SolrGatewayOptions` in `api/v1beta1/solrcloud_types.go` — today the 
operator cannot emit filters at all.
   - `ingressTLSTermination` — edge TLS is configured on the Gateway listener, 
not by the operator, and leaving the block in place silently disables 
`useExternalAddress`.
   - `nodePortOverride` — Ingress-only (see Copilot's comment on 
`api/v1beta1/solrcloud_types.go:1478`; worth cross-referencing once that's 
resolved).
   - `ingressClassName` — replaced by the `GatewayClass` on the Gateway your 
`parentRefs` point at.
   
   **What to check before switching**
   
   - Confirm your Gateway controller is conformant for the features you need: 
https://gateway-api.sigs.k8s.io/implementations/ and 
https://gateway-api.sigs.k8s.io/docs/conformance/levels/ — the Core vs Extended 
vs Implementation-specific distinction is the one that matters.
   - If you terminate TLS at the edge with client certificates, that is 
Gateway-level `frontendValidation` (GEP-91, standard channel since Gateway API 
v1.5.0) and is not implemented everywhere — Traefik lists 
`GatewayFrontendClientCertificateValidation` under `unsupportedFeatures` in its 
v1.6 conformance report, so verify before migrating.
   - If you delegate authentication to the edge, there is no portable standard 
mechanism yet (GEP-1494 is experimental), so expect an implementation-specific 
`ExtensionRef`.
   
   **Useful links**
   
   - Upstream migration guide: 
https://gateway-api.sigs.k8s.io/guides/getting-started/migrating-from-ingress/ 
— has a feature-mapping table and is explicit that annotation-driven features 
are not portable.
   - For ingress-nginx users specifically: 
https://gateway-api.sigs.k8s.io/guides/getting-started/migrating-from-ingress-nginx/
   - `ingress2gateway` for a mechanical first pass: 
https://github.com/kubernetes-sigs/ingress2gateway — supports traefik, 
ingress-nginx, istio, kong, cilium, gce and others. Its own caveat is worth 
quoting: "Ingress2gateway is not intended to copy annotations from Ingress to 
Gateway API." It converts the routing, not the behaviour.
   - TLS configuration guide: 
https://gateway-api.sigs.k8s.io/guides/user-guides/tls/
   
   Four bullets of delta plus five links, and it commits us to tracking no 
implementation.



##########
docs/modules/solr-cloud/pages/gateway-api.adoc:
##########
@@ -0,0 +1,272 @@
+= Gateway API
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+== Overview
+
+The Solr Operator supports using the 
https://gateway-api.sigs.k8s.io/[Kubernetes Gateway API] for external 
addressability of SolrClouds.
+Gateway API is a vendor-neutral, Kubernetes-native API for managing ingress 
traffic and is the successor to the Ingress API.
+
+When you configure `spec.solrAddressability.external.method: Gateway`, the 
Solr Operator creates and manages HTTPRoute resources
+that route external traffic to your Solr nodes through an existing Gateway 
resource in your cluster.
+
+== What Gets Created
+
+When Gateway mode is enabled, the Solr Operator automatically creates the 
following Kubernetes resources:
+
+=== HTTPRoute Resources
+
+The operator creates HTTPRoute resources to route traffic to Solr:
+
+* **Common HTTPRoute**: Routes traffic to the common Solr service 
(load-balanced across all nodes)
+** Named: `<solrcloud-name>-solrcloud-common`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud.<domainName>`
+* **Per-Node HTTPRoutes**: Routes traffic directly to individual Solr nodes 
(when `hideNodes: false`)
+** Named: `<solrcloud-name>-solrcloud-<node-index>`
+** Hostname: `<namespace>-<solrcloud-name>-solrcloud-<node-index>.<domainName>`
+
+All HTTPRoutes are owned by the SolrCloud resource and will be automatically 
cleaned up when the SolrCloud is deleted.
+
+=== Services
+
+The same services are created as with other external addressability methods, 
depending on whether per-node Services are required:
+
+* Common service (load-balanced)
+* Headless service (when not using per-node services)
+* Per-node services (when individual node access is enabled)
+
+== What the Operator Assumes
+
+The Gateway mode assumes the following resources already exist in your cluster:
+
+. **Gateway API CRDs**: The Gateway API CRDs must be installed in your cluster
+** Minimum version: v1.4.0 (required for `BackendTLSPolicy` support)
+** Required CRDs: `Gateway`, `GatewayClass`, `HTTPRoute`, `BackendTLSPolicy` 
(optional, for TLS backends)
+. **Gateway Resource**: A Gateway resource must already exist and be managed 
by your platform team
+** The operator only manages HTTPRoute resources, not the Gateway itself
+** The Gateway must be configured with appropriate listeners and TLS 
termination (if needed)
+. **Gateway Controller**: A Gateway controller implementation must be running 
(e.g., NGINX Gateway Fabric, Istio, Envoy Gateway)
+
+The Solr Operator does **not** create or manage Gateway or GatewayClass 
resources - these are infrastructure-level resources
+typically managed by platform administrators and are specific to the Gateway 
implementation deployed in your cluster (e.g., NGINX Gateway Fabric, Istio, 
Envoy Gateway).
+
+=== CRD Detection
+
+Gateway support is enabled automatically; there is no flag to turn it on or 
off.
+At startup, the operator queries the Kubernetes API server to determine 
whether the Gateway API CRDs are installed.
+Gateway support is enabled only when **both** the `HTTPRoute` and 
`BackendTLSPolicy` types (API group `gateway.networking.k8s.io/v1`) are 
registered.
+If either is missing, the operator logs that Gateway support is disabled, does 
not watch these types, and ignores `method: Gateway` configurations.
+
+IMPORTANT: Detection happens only once, when the operator starts. If you 
install (or remove) the Gateway API CRDs while the operator is running, you 
must restart the operator pod for the change to take effect.
+
+== Configuration
+
+Configure Gateway mode in your SolrCloud spec:
+
+[source,yaml]
+----
+apiVersion: solr.apache.org/v1beta1
+kind: SolrCloud
+metadata:
+  name: example
+  namespace: solr-ns
+spec:
+  replicas: 3
+  solrImage:
+    tag: "9.7.0"
+  solrAddressability:
+    external:
+      method: Gateway
+      domainName: solr.example.com
+      useExternalAddress: true
+      gateway:
+        # Reference to existing Gateway resource(s)
+        parentRefs:
+        - name: my-gateway
+          namespace: gateway-ns
+          sectionName: https  # Optional: specific listener name
+
+        # Optional: annotations to add to HTTPRoute resources
+        annotations:
+          example.com/custom-annotation: "value"
+
+        # Optional: labels to add to HTTPRoute resources
+        labels:
+          app: solr
+          environment: production
+----
+
+=== Configuration Options

Review Comment:
   **`additionalHostnames` restriction isn't documented.** Separate from the 
count-overflow bug Copilot raised: `GenerateNodeHTTPRoute` ignores the field 
entirely (`controllers/util/gateway_util.go:113-121`), so it only ever affects 
the common route. The field's own doc comment says so, but the "Configuration 
Options" list in `docs/modules/solr-cloud/pages/gateway-api.adoc:113-124` 
doesn't.



##########
api/v1beta1/solrcloud_types.go:
##########
@@ -577,6 +587,9 @@ const (
        // Use ExternalDNS to make the Solr service(s) externally addressable
        ExternalDNS ExternalAddressabilityMethod = "ExternalDNS"
 
+       // Use Gateway API to make the Solr service(s) externally addressable
+       Gateway ExternalAddressabilityMethod = "Gateway"
+
        // Make Solr service(s) type:LoadBalancer to make them externally 
addressable
        // NOTE: This option is not currently supported.
        LoadBalancer ExternalAddressabilityMethod = "LoadBalancer"

Review Comment:
   *(The line in question is `api/v1beta1/solrcloud_types.go:600`, five lines 
below this one — it is outside the PR diff, so this is the closest line GitHub 
lets me anchor to.)*
   
   In `ExternalAddressability.withDefaults` (`api/v1beta1/solrcloud_types.go`):
   
   ```go
   if opts.UseExternalAddress && (opts.HideNodes || opts.IngressTLSTermination 
!= nil) {
       changed = true
       opts.UseExternalAddress = false
   }
   ```
   
   There is no method guard on `IngressTLSTermination != nil`, while 
`HasIngressTLSTermination()` at `api/v1beta1/solrcloud_types.go:1601` **is** 
gated to `Method == Ingress`. So a cloud with `method: Gateway` that still 
carries an `ingressTLSTermination` block from its Ingress days gets no TLS 
termination behaviour from it, but does silently get `useExternalAddress` 
flipped off — which changes the advertised Solr node names. That is an easy 
state to land in when switching methods, and it fails quietly.
   
   Suggest gating that condition on `Method == Ingress`, or rejecting 
`ingressTLSTermination` together with `method: Gateway` in validation. Either 
way it deserves a line in the migration notes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to