Copilot commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3352024468
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,273 @@ func (r *SolrCloudReconciler) Reconcile(ctx
context.Context, req ctrl.Request) (
}
}
+ // Reconcile Gateway API HTTPRoutes
+ if extAddressabilityOpts != nil && extAddressabilityOpts.Method ==
solrv1beta1.Gateway {
+ // Validate that gateway config is provided
+ if extAddressabilityOpts.Gateway == nil ||
len(extAddressabilityOpts.Gateway.ParentRefs) == 0 {
+ return requeueOrNot, fmt.Errorf("gateway.parentRefs is
required when using method=Gateway")
+ }
+
+ // Reconcile Common HTTPRoute (if not hidden)
+ if !extAddressabilityOpts.HideCommon {
+ commonHTTPRoute :=
util.GenerateCommonHTTPRoute(instance, solrNodeNames)
+ commonHTTPRouteLogger := logger.WithValues("httproute",
commonHTTPRoute.Name)
+ foundCommonHTTPRoute := &gatewayv1.HTTPRoute{}
+ err = r.Get(ctx, types.NamespacedName{Name:
commonHTTPRoute.Name, Namespace: commonHTTPRoute.Namespace},
foundCommonHTTPRoute)
+ if err != nil && errors.IsNotFound(err) {
+ commonHTTPRouteLogger.Info("Creating common
HTTPRoute")
+ if err =
controllerutil.SetControllerReference(instance, commonHTTPRoute, r.Scheme); err
== nil {
+ err = r.Create(ctx, commonHTTPRoute)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err =
util.OvertakeControllerRef(instance, foundCommonHTTPRoute, r.Scheme)
+ needsUpdate =
util.CopyHTTPRouteFields(commonHTTPRoute, foundCommonHTTPRoute,
commonHTTPRouteLogger) || needsUpdate
+
+ if needsUpdate && err == nil {
+ commonHTTPRouteLogger.Info("Updating
common HTTPRoute")
+ err = r.Update(ctx,
foundCommonHTTPRoute)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ } else {
+ // Delete common HTTPRoute if it exists but should be
hidden
+ foundCommonHTTPRoute := &gatewayv1.HTTPRoute{}
+ err := r.Get(ctx, types.NamespacedName{Name:
instance.CommonHTTPRouteName(), Namespace: instance.GetNamespace()},
foundCommonHTTPRoute)
+ if err == nil {
+ logger.Info("Deleting common HTTPRoute
(hideCommon=true)")
+ err = r.Delete(ctx, foundCommonHTTPRoute)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node HTTPRoutes (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodeHTTPRoute :=
util.GenerateNodeHTTPRoute(instance, nodeName)
+ nodeHTTPRouteLogger :=
logger.WithValues("httproute", nodeHTTPRoute.Name)
+ foundNodeHTTPRoute := &gatewayv1.HTTPRoute{}
+ err = r.Get(ctx, types.NamespacedName{Name:
nodeHTTPRoute.Name, Namespace: nodeHTTPRoute.Namespace}, foundNodeHTTPRoute)
+ if err != nil && errors.IsNotFound(err) {
+ nodeHTTPRouteLogger.Info("Creating node
HTTPRoute")
+ if err =
controllerutil.SetControllerReference(instance, nodeHTTPRoute, r.Scheme); err
== nil {
+ err = r.Create(ctx,
nodeHTTPRoute)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err =
util.OvertakeControllerRef(instance, foundNodeHTTPRoute, r.Scheme)
+ needsUpdate =
util.CopyHTTPRouteFields(nodeHTTPRoute, foundNodeHTTPRoute,
nodeHTTPRouteLogger) || needsUpdate
+
+ if needsUpdate && err == nil {
+
nodeHTTPRouteLogger.Info("Updating node HTTPRoute")
+ err = r.Update(ctx,
foundNodeHTTPRoute)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Cleanup orphaned node HTTPRoutes (when scaling down)
+ httpRouteList := &gatewayv1.HTTPRouteList{}
+ labelSelector := labels.SelectorFromSet(instance.SharedLabels())
+ listOps := &client.ListOptions{
+ Namespace: instance.Namespace,
+ LabelSelector: labelSelector,
+ }
+ err = r.List(ctx, httpRouteList, listOps)
+ if err == nil {
+ for _, httpRoute := range httpRouteList.Items {
+ // Skip the common HTTPRoute
+ if httpRoute.Name ==
instance.CommonHTTPRouteName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if httpRoute.Name ==
instance.NodeHTTPRouteName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ // Delete orphaned HTTPRoute
+ if !nodeExists {
+ logger.Info("Deleting orphaned node
HTTPRoute", "httproute", httpRoute.Name)
+ err = r.Delete(ctx, &httpRoute)
+ if err != nil &&
!errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+
+ // Reconcile BackendTLSPolicy resources if configured
+ if extAddressabilityOpts.HasBackendTLSPolicy() {
+ // Reconcile Common BackendTLSPolicy (if not hidden)
Review Comment:
`BackendTLSPolicy` creation is triggered purely by
`external.gateway.backendTLSPolicy` being set. If `spec.solrTLS` is not
configured, Solr backends will be plain HTTP but the Gateway will be instructed
to use TLS to the backend, which is guaranteed to break traffic. Since the PR
description/docs say BackendTLSPolicy support is for TLS-enabled Solr, consider
validating this and either rejecting the config (preferred) or ignoring
BackendTLSPolicy unless `spec.solrTLS` is set.
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,273 @@ func (r *SolrCloudReconciler) Reconcile(ctx
context.Context, req ctrl.Request) (
}
}
+ // Reconcile Gateway API HTTPRoutes
+ if extAddressabilityOpts != nil && extAddressabilityOpts.Method ==
solrv1beta1.Gateway {
+ // Validate that gateway config is provided
+ if extAddressabilityOpts.Gateway == nil ||
len(extAddressabilityOpts.Gateway.ParentRefs) == 0 {
+ return requeueOrNot, fmt.Errorf("gateway.parentRefs is
required when using method=Gateway")
+ }
+
+ // Reconcile Common HTTPRoute (if not hidden)
+ if !extAddressabilityOpts.HideCommon {
+ commonHTTPRoute :=
util.GenerateCommonHTTPRoute(instance, solrNodeNames)
+ commonHTTPRouteLogger := logger.WithValues("httproute",
commonHTTPRoute.Name)
+ foundCommonHTTPRoute := &gatewayv1.HTTPRoute{}
+ err = r.Get(ctx, types.NamespacedName{Name:
commonHTTPRoute.Name, Namespace: commonHTTPRoute.Namespace},
foundCommonHTTPRoute)
+ if err != nil && errors.IsNotFound(err) {
+ commonHTTPRouteLogger.Info("Creating common
HTTPRoute")
+ if err =
controllerutil.SetControllerReference(instance, commonHTTPRoute, r.Scheme); err
== nil {
+ err = r.Create(ctx, commonHTTPRoute)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err =
util.OvertakeControllerRef(instance, foundCommonHTTPRoute, r.Scheme)
+ needsUpdate =
util.CopyHTTPRouteFields(commonHTTPRoute, foundCommonHTTPRoute,
commonHTTPRouteLogger) || needsUpdate
+
+ if needsUpdate && err == nil {
+ commonHTTPRouteLogger.Info("Updating
common HTTPRoute")
+ err = r.Update(ctx,
foundCommonHTTPRoute)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ } else {
+ // Delete common HTTPRoute if it exists but should be
hidden
+ foundCommonHTTPRoute := &gatewayv1.HTTPRoute{}
+ err := r.Get(ctx, types.NamespacedName{Name:
instance.CommonHTTPRouteName(), Namespace: instance.GetNamespace()},
foundCommonHTTPRoute)
+ if err == nil {
+ logger.Info("Deleting common HTTPRoute
(hideCommon=true)")
+ err = r.Delete(ctx, foundCommonHTTPRoute)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node HTTPRoutes (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodeHTTPRoute :=
util.GenerateNodeHTTPRoute(instance, nodeName)
+ nodeHTTPRouteLogger :=
logger.WithValues("httproute", nodeHTTPRoute.Name)
+ foundNodeHTTPRoute := &gatewayv1.HTTPRoute{}
+ err = r.Get(ctx, types.NamespacedName{Name:
nodeHTTPRoute.Name, Namespace: nodeHTTPRoute.Namespace}, foundNodeHTTPRoute)
+ if err != nil && errors.IsNotFound(err) {
+ nodeHTTPRouteLogger.Info("Creating node
HTTPRoute")
+ if err =
controllerutil.SetControllerReference(instance, nodeHTTPRoute, r.Scheme); err
== nil {
+ err = r.Create(ctx,
nodeHTTPRoute)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err =
util.OvertakeControllerRef(instance, foundNodeHTTPRoute, r.Scheme)
+ needsUpdate =
util.CopyHTTPRouteFields(nodeHTTPRoute, foundNodeHTTPRoute,
nodeHTTPRouteLogger) || needsUpdate
+
+ if needsUpdate && err == nil {
+
nodeHTTPRouteLogger.Info("Updating node HTTPRoute")
+ err = r.Update(ctx,
foundNodeHTTPRoute)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Cleanup orphaned node HTTPRoutes (when scaling down)
+ httpRouteList := &gatewayv1.HTTPRouteList{}
+ labelSelector := labels.SelectorFromSet(instance.SharedLabels())
+ listOps := &client.ListOptions{
+ Namespace: instance.Namespace,
+ LabelSelector: labelSelector,
+ }
+ err = r.List(ctx, httpRouteList, listOps)
+ if err == nil {
+ for _, httpRoute := range httpRouteList.Items {
+ // Skip the common HTTPRoute
+ if httpRoute.Name ==
instance.CommonHTTPRouteName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if httpRoute.Name ==
instance.NodeHTTPRouteName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ // Delete orphaned HTTPRoute
+ if !nodeExists {
+ logger.Info("Deleting orphaned node
HTTPRoute", "httproute", httpRoute.Name)
+ err = r.Delete(ctx, &httpRoute)
+ if err != nil &&
!errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
Review Comment:
Node HTTPRoutes are only deleted when the node no longer exists
(scale-down). If the user switches `hideNodes` from false -> true, existing
per-node HTTPRoutes will not be removed because `nodeExists` stays true,
leaving stale routes behind (and the new e2e test expects them to be deleted).
Consider deleting all non-common HTTPRoutes when `hideNodes=true`, similar to
the BackendTLSPolicy cleanup logic below.
##########
controllers/solrcloud_controller.go:
##########
@@ -1191,6 +1463,7 @@ func (r *SolrCloudReconciler) SetupWithManager(mgr
ctrl.Manager) error {
Owns(&corev1.Service{}).
Owns(&corev1.Secret{}). /* for authentication */
Owns(&netv1.Ingress{}).
+ Owns(&gatewayv1.HTTPRoute{}).
Owns(&policyv1.PodDisruptionBudget{})
Review Comment:
The controller declares ownership of HTTPRoute objects but not
BackendTLSPolicy. Without `Owns(&gatewayv1.BackendTLSPolicy{})`,
deletions/updates to BackendTLSPolicy resources will not enqueue reconciles,
which can leave the cluster in a drifted state until some other event triggers
reconciliation.
##########
docs/solr-cloud/gateway-api.md:
##########
@@ -0,0 +1,264 @@
+<!--
+ 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.
+ -->
+
+# Gateway API
+
+## Overview
+
+The Solr Operator supports using the [Kubernetes Gateway
API](https://gateway-api.sigs.k8s.io/) 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:
+- Common service (load-balanced)
+- Headless service (for internal cluster communication)
+- Per-node services (when individual node access is enabled)
Review Comment:
This section implies the headless Service is always created in Gateway mode.
In the operator logic, the headless Service is only created when *not* using
per-node Services (see `UsesHeadlessService()`), and the new Gateway e2e test
in this PR explicitly expects the headless Service to not exist when per-node
Services are used. Please clarify this to reflect the actual behavior.
##########
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)
+
+ labels := solrCloud.SharedLabelsWith(solrCloud.GetLabels())
+ backendTLSConfig :=
solrCloud.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy
+
+ // Convert CA certificate refs
+ var caCertRefs []gatewayv1.LocalObjectReference
+ if backendTLSConfig.CACertificateRefs != nil {
+ caCertRefs = make([]gatewayv1.LocalObjectReference,
len(backendTLSConfig.CACertificateRefs))
+ for i, ref := range backendTLSConfig.CACertificateRefs {
+ // Default to ConfigMap if kind is not specified
+ kind := gatewayv1.Kind("ConfigMap")
+ if ref.Kind != nil {
+ kind = gatewayv1.Kind(*ref.Kind)
+ }
+ // Default to empty group (core API)
+ group := gatewayv1.Group("")
+ if ref.Group != nil {
+ group = gatewayv1.Group(*ref.Group)
+ }
+ certRef := gatewayv1.LocalObjectReference{
+ Group: group,
+ Kind: kind,
+ Name: gatewayv1.ObjectName(ref.Name),
+ }
+ caCertRefs[i] = certRef
+ }
+ }
+
+ policy := &gatewayv1.BackendTLSPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: solrCloud.CommonBackendTLSPolicyName(),
+ Namespace: solrCloud.GetNamespace(),
+ Labels: labels,
+ },
+ Spec: gatewayv1.BackendTLSPolicySpec{
+ TargetRefs:
[]gatewayv1.LocalPolicyTargetReferenceWithSectionName{
+ {
+ LocalPolicyTargetReference:
gatewayv1.LocalPolicyTargetReference{
+ Group: "",
+ Kind: "Service",
+ Name:
gatewayv1.ObjectName(solrCloud.CommonServiceName()),
+ },
+ },
+ },
+ Validation: gatewayv1.BackendTLSPolicyValidation{
+ Hostname: gatewayv1.PreciseHostname(fqdn),
+ },
+ },
+ }
+
+ // Set CA certificates or well-known CAs
+ if caCertRefs != nil {
+ policy.Spec.Validation.CACertificateRefs = caCertRefs
+ } else if backendTLSConfig.WellKnownCACertificates != nil {
+ wellKnown :=
gatewayv1.WellKnownCACertificatesType(*backendTLSConfig.WellKnownCACertificates)
+ policy.Spec.Validation.WellKnownCACertificates = &wellKnown
+ }
+
+ return policy
+}
+
+// GenerateNodeBackendTLSPolicy creates a BackendTLSPolicy for individual Solr
node service
+func GenerateNodeBackendTLSPolicy(solrCloud *solr.SolrCloud, nodeName string)
*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.ExternalNodeUrl(nodeName, domainName, false)
+
Review Comment:
BackendTLSPolicy `spec.validation.hostname` for node services is currently
set to the *external* Gateway hostname (`ExternalNodeUrl(...)`). The
BackendTLSPolicy targets the per-node Service; the hostname used for
validation/SNI should match that Service (and the e2e test in this PR expects
`nodeName`).
##########
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:
BackendTLSPolicy `spec.validation.hostname` is currently set to the
*external* Gateway hostname (`ExternalCommonUrl(...)`). BackendTLS is between
the Gateway and the in-cluster Service, and the docs/tests in this PR expect
the Service name to be used for SNI/hostname validation. Using the external
hostname here will cause the new e2e assertions to fail and can break
validation if Solr certs are issued for Service DNS names.
##########
docs/solr-cloud/gateway-api.md:
##########
@@ -0,0 +1,264 @@
+<!--
+ 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.
+ -->
+
+# Gateway API
+
+## Overview
+
+The Solr Operator supports using the [Kubernetes Gateway
API](https://gateway-api.sigs.k8s.io/) 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:
+- Common service (load-balanced)
+- Headless service (for internal cluster communication)
+- 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:
+
+1. **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)
+
+2. **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)
+
+3. **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).
+
+## Configuration
+
+Configure Gateway mode in your SolrCloud spec:
+
+```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 v1alpha3) when configured in the SolrCloud spec:
Review Comment:
The doc says BackendTLSPolicy is "Gateway API v1alpha3", but this PR
requires Gateway API v1.4.0+ and the examples below use `apiVersion:
gateway.networking.k8s.io/v1`. Please update this reference to avoid confusion.
##########
docs/solr-cloud/gateway-api.md:
##########
@@ -0,0 +1,264 @@
+<!--
+ 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
Review Comment:
The Apache license header is missing the opening parenthesis before `the
"License"` (it should be `Version 2.0 (the "License"); ...`).
--
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]