You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@solr.apache.org by "chinmoysahu (via GitHub)" <gi...@apache.org> on 2026/01/26 06:04:46 UTC
[PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
chinmoysahu opened a new pull request, #815:
URL: https://github.com/apache/solr-operator/pull/815
## Summary
This PR adds support for the Kubernetes Gateway API as a new external addressability method for SolrCloud instances. Gateway API is the successor to the Ingress API and provides a more flexible, vendor-neutral way to manage ingress traffic in Kubernetes.
## Features
### Gateway API Integration
- **New addressability method**: `spec.solrAddressability.external.method: Gateway`
- **Automatic HTTPRoute management** for common and per-node services
- **Cross-namespace Gateway references** with optional listener targeting via `sectionName`
- **Custom labels and annotations** for HTTPRoute resources
### BackendTLSPolicy Support
- **Automatic TLS policy creation** for secure backend connections when `spec.solrTLS` is enabled
- **Flexible CA configuration**: CA certificate references (ConfigMap/Secret) or well-known CAs
- **Per-service policies** for common and individual node services
## API Changes
**New Types** (`api/v1beta1/solrcloud_types.go`):
- `SolrGatewayOptions`, `GatewayParentReference`, `SolrBackendTLSPolicy`, `GatewayCertificateReference`
**New Utility Functions** (`controllers/util/`):
- `gateway_util.go`: HTTPRoute generation and management
- `gateway_util_backendtls.go`: BackendTLSPolicy generation and management
**RBAC**: Added permissions for `httproutes` and `backendtlspolicies` in `gateway.networking.k8s.io` API group
## Documentation
- `docs/solr-cloud/gateway-api.md`: Comprehensive usage guide with configuration examples, BackendTLSPolicy setup, and Gateway implementation support matrix (Envoy Gateway, kgateway, NGINX Gateway Fabric, etc.)
- `docs/solr-cloud/README.md`: Added Gateway API reference
## Dependency Updates
**Gateway API v1.4.0+** is required to use the stable `v1` API for `BackendTLSPolicy` (GA). This upgrade forced Go 1.24.0+ ([required by Gateway API v1.4.0](https://github.com/kubernetes-sigs/gateway-api/blob/v1.4.0/go.mod#L3)), which cascaded to Kubernetes libraries (v0.34.1) and controller-runtime (v0.22.1).
**CRD Changes**: Extensive changes in `config/crd/bases/*.yaml` include new Gateway API fields plus upstream schema updates from Kubernetes library upgrades (deprecation notices, field descriptions, etc.). These are auto-generated by `controller-gen`.
**References:**
- [Gateway API v1.4.0 Release](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0)
- [Gateway API v1.4.0 go.mod](https://github.com/kubernetes-sigs/gateway-api/blob/v1.4.0/go.mod)
- [Go 1.24 Release Notes](https://go.dev/doc/go1.24)
## Example Configuration
```yaml
apiVersion: solr.apache.org/v1beta1
kind: SolrCloud
metadata:
name: example
namespace: solr-ns
spec:
replicas: 3
solrImage:
tag: "9.7.0"
solrTLS:
pkcs12Secret:
name: solr-tls-cert
key: keystore.p12
solrAddressability:
external:
method: Gateway
domainName: solr.example.com
useExternalAddress: true
gateway:
parentRefs:
- name: my-gateway
namespace: gateway-ns
sectionName: https
backendTLSPolicy:
caCertificateRefs:
- name: solr-ca-cert
```
## Testing
**E2E Tests** (`tests/e2e/solrcloud_gateway_test.go`):
- HTTPRoute and BackendTLSPolicy lifecycle management
- CA certificate configuration switching
- Resource cleanup and orphan handling
**Manual Testing**:
- ✅ Tested with kgateway on Kubernetes 1.32
- ✅ Verified with both NGINX Ingress and Gateway modes to ensure backward compatibility
- ✅ Verified cross-namespace Gateway references
- ✅ Confirmed TLS backend connections with BackendTLSPolicy
## Compatibility
- **Gateway API**: v1.4.0+ required (BackendTLSPolicy GA support)
- **Kubernetes**: 1.23+ (Gateway API CRDs must be installed)
- **Backward compatible**: Existing Ingress and other addressability methods unchanged
- **Breaking changes**: None
## Migration Path
1. Install Gateway API CRDs (v1.4.0+)
2. Deploy a Gateway resource
3. Update SolrCloud spec to use `method: Gateway`
4. Operator automatically creates HTTPRoute resources
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728885036
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
+ var (
+ caCertConfigMapName = "solr-ca-cert"
+ )
+
+ FIt("Creates and manages BackendTLSPolicy resources", func(ctx context.Context) {
Review Comment:
Addressed.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
+ var (
+ caCertConfigMapName = "solr-ca-cert"
+ )
+
+ FIt("Creates and manages BackendTLSPolicy resources", func(ctx context.Context) {
+ By("verifying BackendTLSPolicy resources do not exist initially")
+ expectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ for _, nodeName := range nodeNames {
+ expectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName))
+ }
+
+ By("enabling BackendTLSPolicy with CA certificate reference")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = &solrv1beta1.SolrBackendTLSPolicy{
+ CACertificateRefs: []solrv1beta1.GatewayCertificateReference{
+ {
+ Name: caCertConfigMapName,
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add BackendTLSPolicy")
+ })
+
+ By("verifying Common BackendTLSPolicy was created correctly")
+ commonPolicy := expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName(), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.TargetRefs).To(HaveLen(1), "BackendTLSPolicy should have one target ref")
+ g.Expect(string(policy.Spec.TargetRefs[0].Name)).To(Equal(solrCloud.CommonServiceName()), "BackendTLSPolicy should target common service")
+ g.Expect(string(policy.Spec.TargetRefs[0].Kind)).To(Equal("Service"), "BackendTLSPolicy should target Service kind")
+ g.Expect(string(policy.Spec.Validation.Hostname)).To(Equal(solrCloud.CommonServiceName()), "BackendTLSPolicy hostname should match service name")
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(HaveLen(1), "BackendTLSPolicy should have one CA cert ref")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Name)).To(Equal(caCertConfigMapName), "CA cert ref name should match")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Kind)).To(Equal("ConfigMap"), "CA cert ref kind should default to ConfigMap")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Group)).To(Equal(""), "CA cert ref group should be empty (core API)")
+ })
+ Expect(commonPolicy).ToNot(BeNil())
+
+ By("verifying Node BackendTLSPolicy resources were created correctly")
+ for _, nodeName := range nodeNames {
+ nodePolicy := expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.TargetRefs).To(HaveLen(1), "BackendTLSPolicy should have one target ref")
+ g.Expect(string(policy.Spec.TargetRefs[0].Name)).To(Equal(nodeName), "BackendTLSPolicy should target node service")
+ g.Expect(string(policy.Spec.Validation.Hostname)).To(Equal(nodeName), "BackendTLSPolicy hostname should match node service name")
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(HaveLen(1), "BackendTLSPolicy should have one CA cert ref")
+ })
+ Expect(nodePolicy).ToNot(BeNil())
+ }
+
+ By("updating BackendTLSPolicy to use wellKnownCACertificates")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ wellKnown := "System"
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = &solrv1beta1.SolrBackendTLSPolicy{
+ WellKnownCACertificates: &wellKnown,
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud BackendTLSPolicy to use wellKnownCACertificates")
+ })
+
+ By("verifying BackendTLSPolicy was updated to use wellKnownCACertificates")
+ expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName(), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(BeNil(), "CACertificateRefs should be nil when using wellKnownCACertificates")
+ g.Expect(policy.Spec.Validation.WellKnownCACertificates).ToNot(BeNil(), "WellKnownCACertificates should be set")
+ g.Expect(string(*policy.Spec.Validation.WellKnownCACertificates)).To(Equal("System"), "WellKnownCACertificates should be 'System'")
+ })
+
+ By("disabling node external addressability and verifying node BackendTLSPolicy resources are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to hide nodes")
+ })
+
+ By("verifying node BackendTLSPolicy resources were deleted")
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName))
+ }
+
+ By("verifying common BackendTLSPolicy still exists")
+ expectBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+
+ By("removing BackendTLSPolicy configuration")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove BackendTLSPolicy")
+ })
+
+ By("verifying all BackendTLSPolicy resources were deleted")
+ eventuallyExpectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+ })
+
+ FIt("Cleans up BackendTLSPolicy when changing from Gateway method", func(ctx context.Context) {
Review Comment:
Addressed.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728894120
##########
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
+ hostname := solrCloud.ExternalCommonUrl(domainName, false)
Review Comment:
Addressed. used fqdn as the variable
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728900464
##########
api/v1beta1/solrcloud_types.go:
##########
@@ -626,6 +638,90 @@ type SolrIngressTLSTermination struct {
TLSSecret string `json:"tlsSecret,omitempty"`
}
+// SolrGatewayOptions defines how a SolrCloud should be exposed via Kubernetes Gateway API
+type SolrGatewayOptions struct {
+ // ParentRefs specifies the Gateway(s) to attach HTTPRoutes to.
+ // This is required when using method=Gateway.
+ //
+ // The referenced Gateway must already exist and be managed by your platform team.
+ // The Solr Operator only manages the HTTPRoute resources.
+ //
+ // +kubebuilder:validation:MinItems=1
+ ParentRefs []GatewayParentReference `json:"parentRefs"`
+
+ // Annotations to add to HTTPRoute resources
+ // +optional
+ Annotations map[string]string `json:"annotations,omitempty"`
+
+ // Labels to add to HTTPRoute resources
+ // +optional
+ Labels map[string]string `json:"labels,omitempty"`
+
+ // BackendTLSPolicy defines TLS configuration for backend connections from Gateway to Solr pods.
+ //
+ // This is used when Solr pods are running with TLS enabled (spec.solrTLS) and the Gateway
+ // needs to establish secure connections to the backend services.
+ //
+ // The Solr Operator will create BackendTLSPolicy resources for each HTTPRoute.
+ //
+ // +optional
+ BackendTLSPolicy *SolrBackendTLSPolicy `json:"backendTLSPolicy,omitempty"`
+}
+
+// GatewayParentReference identifies a parent Gateway resource to attach HTTPRoutes to
+type GatewayParentReference struct {
+ // Name of the Gateway resource
+ Name string `json:"name"`
+
+ // Namespace of the Gateway resource.
+ // If not specified, defaults to the HTTPRoute's namespace.
+ // +optional
+ Namespace *string `json:"namespace,omitempty"`
+
+ // SectionName refers to a specific listener on the Gateway.
+ // For example, "https" or "http".
+ // +optional
+ SectionName *string `json:"sectionName,omitempty"`
+}
+
+// SolrBackendTLSPolicy defines backend TLS configuration for Gateway API
+// +kubebuilder:validation:MaxProperties=1
+type SolrBackendTLSPolicy struct {
+ // CACertificateRefs contains one or more references to Kubernetes objects that contain
+ // TLS certificates of the Certificate Authorities that can be used as a trust anchor
+ // to validate the certificates presented by the backend.
+ //
+ // +optional
+ // +kubebuilder:validation:MaxItems=8
+ CACertificateRefs []GatewayCertificateReference `json:"caCertificateRefs,omitempty"`
+
+ // WellKnownCACertificates specifies whether system CA certificates may be used in the
+ // TLS handshake between the gateway and backend pod.
+ //
+ // If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be
+ // specified with at least one entry for a valid configuration.
+ //
+ // Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both.
+ //
+ // +optional
+ WellKnownCACertificates *string `json:"wellKnownCACertificates,omitempty"`
+}
Review Comment:
Updated docs and CRD description.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
Review Comment:
Addressed
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "DanielRaapDev (via GitHub)" <gi...@apache.org>.
DanielRaapDev commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3349470655
##########
config/crd/bases/solr.apache.org_solrclouds.yaml:
##########
@@ -9889,6 +10395,106 @@ spec:
For the LoadBalancer method, this field is optional and will only be used when useExternalAddress=true.
If used with the LoadBalancer method, you will need DNS routing to the LoadBalancer IP address through the url template given above.
type: string
+ gateway:
+ description: |-
+ Gateway defines settings for Kubernetes Gateway API routing.
+
+ This option is only available when Method=Gateway.
+ The referenced Gateway must already exist and be managed by your platform team.
+ The Solr Operator only manages the HTTPRoute resources.
+ properties:
Review Comment:
Why is `hostnames` missing of the [HTTPRouteSpec](https://gateway-api.sigs.k8s.io/reference/api-spec/main/spec/#httproutespec)? It is useful to filter listeners of the Gateway.
Suggestion:
```suggestion
properties:
hostnames:
description: |-
Hostnames defines a set of hostnames that should match against the HTTP Host
header to select a HTTPRoute used to process the request..
type: array
maxItems: 16
items:
description: Hostname is the fully qualified domain name of a network host.
Hostname can be “precise” which is a domain name without the terminating dot of a network host (e.g. “foo.example.com”) or “wildcard”, which is a domain name prefixed with a single wildcard label (e.g. *.example.com).
type: string
minLength: 1
maxLength: 253
pattern: '^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
```
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4616659989
Yea that’s a limitation for orgs. Thanks for following up. I’ll trigger another copilot review as well.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728892649
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,223 @@ 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)
+ if !extAddressabilityOpts.HideCommon {
+ commonPolicy := util.GenerateCommonBackendTLSPolicy(instance)
+ if commonPolicy != nil {
+ commonPolicyLogger := logger.WithValues("backendtlspolicy", commonPolicy.Name)
+ foundCommonPolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: commonPolicy.Name, Namespace: commonPolicy.Namespace}, foundCommonPolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ commonPolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, commonPolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, commonPolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundCommonPolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(commonPolicy, foundCommonPolicy, commonPolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ commonPolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundCommonPolicy)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node BackendTLSPolicies (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodePolicy := util.GenerateNodeBackendTLSPolicy(instance, nodeName)
+ if nodePolicy != nil {
+ nodePolicyLogger := logger.WithValues("backendtlspolicy", nodePolicy.Name)
+ foundNodePolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: nodePolicy.Name, Namespace: nodePolicy.Namespace}, foundNodePolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ nodePolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, nodePolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, nodePolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundNodePolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(nodePolicy, foundNodePolicy, nodePolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ nodePolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundNodePolicy)
+ }
+ }
+
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+
+ // Cleanup orphaned node BackendTLSPolicies (when scaling down)
+ backendTLSPolicyList := &gatewayv1.BackendTLSPolicyList{}
+ err = r.List(ctx, backendTLSPolicyList, listOps)
+ if err == nil {
+ for _, policy := range backendTLSPolicyList.Items {
+ // Skip the common BackendTLSPolicy
+ if policy.Name == instance.CommonBackendTLSPolicyName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if policy.Name == instance.NodeBackendTLSPolicyName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ if !nodeExists {
+ logger.Info("Deleting orphaned BackendTLSPolicy", "backendtlspolicy", policy.Name)
+ err = r.Delete(ctx, &policy)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+ }
Review Comment:
Good catch. Addressed.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
Review Comment:
Addressed.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-3802211611
Thanks. You may relive the discussion threads that are dealt with.
We’re a bit short on cycles for attending to the operator these days, if you continue contributing you may end up being nominated for committership.
I suppose now that Solr 10 release is imminent and this great new feature, we should consider a new operator release, and skip support for Solr 8.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355857334
##########
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:
Fixed in the latest commit - The cleanup now deletes all non-common HTTPRoutes when hideNodes=true, matching the BackendTLSPolicy cleanup pattern.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4010998956
@chinmoysahu There are some linting and test failures that can be fixed while waiting for additional review.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3357983539
##########
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:
This exact sceniaro was actually a bug with ingresses earlier, so ingresses were not cleaned up when re-configuring / disabling ingress. Easy to overlook.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355860266
##########
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:
Good catch. Added validation so that reconciliation now returns an error if backendTLSPolicy is configured without spec.solrTLS.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4594930301
Can this be salvaged and targeted for merge before 1.0 relase? If necessary we could label it as beta in the first release?
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728884483
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
Review Comment:
Addressed.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
Review Comment:
Addressed.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4616284970
@janhoy Since this fork is under my organization, I wasnt able to enable maintainer edits like a personal fork. I have updated the branch from main and pushed the changes to this PR.
Also responding to the other comments shortly. Thanks for the follow up,
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355864982
##########
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:
done
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "HoustonPutman (via GitHub)" <gi...@apache.org>.
HoustonPutman commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4745986028
@chinmoysahu It'll be very hard to move this forward without being able to make changes ourselves. This is a big PR and will need some edits from us. Is it possible y'all could move this to a non-organizational fork so that we could get write access to the PR?
For example I just migrated the new docs to Antora, which the operator uses now.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2726795855
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
Review Comment:
The test uses `FIt` (focused test marker) which should not be committed. This causes only the focused tests to run, skipping all other tests.
Please change all instances of `FIt` to `It` before merging.
```suggestion
It("Can adapt to changing needs", func(ctx context.Context) {
```
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,223 @@ 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)
+ if !extAddressabilityOpts.HideCommon {
+ commonPolicy := util.GenerateCommonBackendTLSPolicy(instance)
+ if commonPolicy != nil {
+ commonPolicyLogger := logger.WithValues("backendtlspolicy", commonPolicy.Name)
+ foundCommonPolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: commonPolicy.Name, Namespace: commonPolicy.Namespace}, foundCommonPolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ commonPolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, commonPolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, commonPolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundCommonPolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(commonPolicy, foundCommonPolicy, commonPolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ commonPolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundCommonPolicy)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node BackendTLSPolicies (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodePolicy := util.GenerateNodeBackendTLSPolicy(instance, nodeName)
+ if nodePolicy != nil {
+ nodePolicyLogger := logger.WithValues("backendtlspolicy", nodePolicy.Name)
+ foundNodePolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: nodePolicy.Name, Namespace: nodePolicy.Namespace}, foundNodePolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ nodePolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, nodePolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, nodePolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundNodePolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(nodePolicy, foundNodePolicy, nodePolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ nodePolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundNodePolicy)
+ }
+ }
+
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+
+ // Cleanup orphaned node BackendTLSPolicies (when scaling down)
+ backendTLSPolicyList := &gatewayv1.BackendTLSPolicyList{}
+ err = r.List(ctx, backendTLSPolicyList, listOps)
+ if err == nil {
+ for _, policy := range backendTLSPolicyList.Items {
+ // Skip the common BackendTLSPolicy
+ if policy.Name == instance.CommonBackendTLSPolicyName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if policy.Name == instance.NodeBackendTLSPolicyName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ if !nodeExists {
+ logger.Info("Deleting orphaned BackendTLSPolicy", "backendtlspolicy", policy.Name)
+ err = r.Delete(ctx, &policy)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // If not using Gateway method, clean up any existing HTTPRoutes
+ 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 && len(httpRouteList.Items) > 0 {
+ logger.Info("Cleaning up HTTPRoutes (method changed from Gateway)")
+ for _, httpRoute := range httpRouteList.Items {
+ err = r.Delete(ctx, &httpRoute)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
Review Comment:
Missing cleanup logic for BackendTLSPolicy resources when not using Gateway method or when BackendTLSPolicy is disabled. The code cleans up HTTPRoutes when not using Gateway method (lines 642-659), but there's no corresponding cleanup for BackendTLSPolicy resources that may exist from a previous configuration.
Consider adding cleanup logic similar to the HTTPRoute cleanup to delete any existing BackendTLSPolicy resources when either:
1. The method is changed from Gateway to another method
2. BackendTLSPolicy configuration is removed while still using Gateway method
This could lead to orphaned BackendTLSPolicy resources that are not automatically cleaned up.
```suggestion
}
// Also clean up any existing BackendTLSPolicies that may have been created when using Gateway
backendTLSPolicyList := &gatewayv1.BackendTLSPolicyList{}
err = r.List(ctx, backendTLSPolicyList, listOps)
if err == nil && len(backendTLSPolicyList.Items) > 0 {
logger.Info("Cleaning up BackendTLSPolicies (method changed from Gateway)")
for _, backendTLSPolicy := range backendTLSPolicyList.Items {
err = r.Delete(ctx, &backendTLSPolicy)
if err != nil && !errors.IsNotFound(err) {
return requeueOrNot, err
}
}
}
```
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
Review Comment:
The test suite uses `FDescribe` and `FIt` (focused test markers) which should not be committed to the main branch. These markers cause only the focused tests to run, skipping all other tests in the test suite. This can lead to other tests being accidentally ignored in CI/CD pipelines.
Please change `FDescribe` to `Describe` and all `FIt` to `It` before merging.
```suggestion
var _ = Describe("E2E - SolrCloud - Gateway API", func() {
```
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
+ var (
+ caCertConfigMapName = "solr-ca-cert"
+ )
+
+ FIt("Creates and manages BackendTLSPolicy resources", func(ctx context.Context) {
+ By("verifying BackendTLSPolicy resources do not exist initially")
+ expectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ for _, nodeName := range nodeNames {
+ expectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName))
+ }
+
+ By("enabling BackendTLSPolicy with CA certificate reference")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = &solrv1beta1.SolrBackendTLSPolicy{
+ CACertificateRefs: []solrv1beta1.GatewayCertificateReference{
+ {
+ Name: caCertConfigMapName,
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add BackendTLSPolicy")
+ })
+
+ By("verifying Common BackendTLSPolicy was created correctly")
+ commonPolicy := expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName(), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.TargetRefs).To(HaveLen(1), "BackendTLSPolicy should have one target ref")
+ g.Expect(string(policy.Spec.TargetRefs[0].Name)).To(Equal(solrCloud.CommonServiceName()), "BackendTLSPolicy should target common service")
+ g.Expect(string(policy.Spec.TargetRefs[0].Kind)).To(Equal("Service"), "BackendTLSPolicy should target Service kind")
+ g.Expect(string(policy.Spec.Validation.Hostname)).To(Equal(solrCloud.CommonServiceName()), "BackendTLSPolicy hostname should match service name")
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(HaveLen(1), "BackendTLSPolicy should have one CA cert ref")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Name)).To(Equal(caCertConfigMapName), "CA cert ref name should match")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Kind)).To(Equal("ConfigMap"), "CA cert ref kind should default to ConfigMap")
+ g.Expect(string(policy.Spec.Validation.CACertificateRefs[0].Group)).To(Equal(""), "CA cert ref group should be empty (core API)")
+ })
+ Expect(commonPolicy).ToNot(BeNil())
+
+ By("verifying Node BackendTLSPolicy resources were created correctly")
+ for _, nodeName := range nodeNames {
+ nodePolicy := expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.TargetRefs).To(HaveLen(1), "BackendTLSPolicy should have one target ref")
+ g.Expect(string(policy.Spec.TargetRefs[0].Name)).To(Equal(nodeName), "BackendTLSPolicy should target node service")
+ g.Expect(string(policy.Spec.Validation.Hostname)).To(Equal(nodeName), "BackendTLSPolicy hostname should match node service name")
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(HaveLen(1), "BackendTLSPolicy should have one CA cert ref")
+ })
+ Expect(nodePolicy).ToNot(BeNil())
+ }
+
+ By("updating BackendTLSPolicy to use wellKnownCACertificates")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ wellKnown := "System"
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = &solrv1beta1.SolrBackendTLSPolicy{
+ WellKnownCACertificates: &wellKnown,
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud BackendTLSPolicy to use wellKnownCACertificates")
+ })
+
+ By("verifying BackendTLSPolicy was updated to use wellKnownCACertificates")
+ expectBackendTLSPolicyWithChecks(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName(), func(g Gomega, policy *gatewayv1.BackendTLSPolicy) {
+ g.Expect(policy.Spec.Validation.CACertificateRefs).To(BeNil(), "CACertificateRefs should be nil when using wellKnownCACertificates")
+ g.Expect(policy.Spec.Validation.WellKnownCACertificates).ToNot(BeNil(), "WellKnownCACertificates should be set")
+ g.Expect(string(*policy.Spec.Validation.WellKnownCACertificates)).To(Equal("System"), "WellKnownCACertificates should be 'System'")
+ })
+
+ By("disabling node external addressability and verifying node BackendTLSPolicy resources are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to hide nodes")
+ })
+
+ By("verifying node BackendTLSPolicy resources were deleted")
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.NodeBackendTLSPolicyName(nodeName))
+ }
+
+ By("verifying common BackendTLSPolicy still exists")
+ expectBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+
+ By("removing BackendTLSPolicy configuration")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.Gateway.BackendTLSPolicy = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove BackendTLSPolicy")
+ })
+
+ By("verifying all BackendTLSPolicy resources were deleted")
+ eventuallyExpectNoBackendTLSPolicy(ctx, solrCloud, solrCloud.CommonBackendTLSPolicyName())
+ })
+
+ FIt("Cleans up BackendTLSPolicy when changing from Gateway method", func(ctx context.Context) {
Review Comment:
The test uses `FIt` (focused test marker) which should not be committed.
Please change all instances of `FIt` to `It` before merging.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
Review Comment:
The test context uses `FContext` (focused context marker) which should not be committed. This causes only the focused contexts to run, skipping all other test contexts.
Please change `FContext` to `Context` before merging.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
+ var (
+ caCertConfigMapName = "solr-ca-cert"
+ )
+
+ FIt("Creates and manages BackendTLSPolicy resources", func(ctx context.Context) {
Review Comment:
The test uses `FIt` (focused test marker) which should not be committed.
Please change all instances of `FIt` to `It` before merging.
##########
tests/e2e/solrcloud_gateway_test.go:
##########
@@ -0,0 +1,281 @@
+/*
+ * 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"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Gateway API", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ gatewayNamespace = "default"
+ gatewayName = "test-gateway"
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloud(1)
+ solrCloud.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("creating the SolrCloud")
+ Expect(k8sClient.Create(ctx, solrCloud)).To(Succeed())
+
+ DeferCleanup(func(ctx context.Context) {
+ cleanupTest(ctx, solrCloud)
+ })
+
+ By("Waiting for the SolrCloud to come up healthy")
+ solrCloud = expectSolrCloudToBeReady(ctx, solrCloud)
+
+ By("creating a first Solr Collection")
+ createAndQueryCollection(ctx, solrCloud, "basic", 1, 1)
+ })
+
+ FContext("Can Remove HTTPRoutes and Services when changing addressability", func() {
+
+ FIt("Can adapt to changing needs", func(ctx context.Context) {
+ By("testing the Solr StatefulSet")
+ statefulSet := expectStatefulSet(ctx, solrCloud, solrCloud.StatefulSetName())
+ // Pod Annotations test
+ Expect(statefulSet.Spec.Template.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.PerNodeServiceType), "Since external address is used for advertising, the perNode service should be specified in the pod annotations.")
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+
+ By("making sure the individual Solr Node Services exist and route correctly")
+ nodeNames := solrCloud.GetAllSolrPodNames()
+ Expect(nodeNames).To(HaveLen(1), "SolrCloud has incorrect number of nodeNames.")
+ for _, nodeName := range nodeNames {
+ expectService(ctx, solrCloud, nodeName, util.MergeLabelsOrAnnotations(statefulSet.Spec.Selector.MatchLabels, map[string]string{"statefulset.kubernetes.io/pod-name": nodeName}), false)
+ }
+
+ By("making sure Common HTTPRoute was created correctly")
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("making sure Node HTTPRoutes were created correctly")
+ for _, nodeName := range nodeNames {
+ expectHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+
+ By("Turning off node external addressability and making sure the node services are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External.HideNodes = true
+ found.Spec.SolrAddressability.External.UseExternalAddress = false
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to not advertise the nodes externally.")
+ })
+
+ // Since node external addressability is off, but common external addressability is on, the common HTTPRoute should exist, but the node HTTPRoutes should not
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ expectService(ctx, solrCloud, solrCloud.HeadlessServiceName(), statefulSet.Spec.Selector.MatchLabels, true)
+
+ for _, nodeName := range nodeNames {
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.NodeHTTPRouteName(nodeName))
+ }
+ pod := expectPodNow(ctx, solrCloud, solrCloud.GetSolrPodName(0))
+ Expect(pod.Annotations).To(HaveKeyWithValue(util.ServiceTypeAnnotation, util.HeadlessServiceType))
+
+ By("Turning off common external addressability and making sure the HTTPRoutes are deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability.External = nil
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to remove external addressability")
+ })
+ eventuallyExpectNoHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("Turning back on common external addressability and making sure the headless service is deleted")
+ expectSolrCloudWithChecks(ctx, solrCloud, func(g Gomega, found *solrv1beta1.SolrCloud) {
+ found.Spec.SolrAddressability = solrv1beta1.SolrAddressabilityOptions{
+ External: &solrv1beta1.ExternalAddressability{
+ Method: solrv1beta1.Gateway,
+ UseExternalAddress: true,
+ DomainName: testDomain,
+ Gateway: &solrv1beta1.SolrGatewayOptions{
+ ParentRefs: []solrv1beta1.GatewayParentReference{
+ {
+ Name: gatewayName,
+ Namespace: &gatewayNamespace,
+ },
+ },
+ },
+ },
+ }
+ g.Expect(k8sClient.Update(ctx, found)).To(Succeed(), "Couldn't update the solrCloud to add external addressability")
+ })
+ expectHTTPRoute(ctx, solrCloud, solrCloud.CommonHTTPRouteName())
+
+ By("testing the Solr Common Service")
+ expectService(ctx, solrCloud, solrCloud.CommonServiceName(), statefulSet.Spec.Selector.MatchLabels, false)
+
+ By("ensuring the Solr Headless Service does not exist")
+ expectNoService(ctx, solrCloud, solrCloud.HeadlessServiceName(), "Headless service shouldn't exist, but it does.")
+ })
+ })
+
+ FContext("BackendTLSPolicy Management", func() {
Review Comment:
The test context uses `FContext` (focused context marker) which should not be committed.
Please change `FContext` to `Context` before merging.
##########
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
+ hostname := solrCloud.ExternalCommonUrl(domainName, false)
Review Comment:
The hostname validation uses the full URL returned by `ExternalCommonUrl` and `ExternalNodeUrl`, but these functions include the hostname without the protocol scheme. However, the comment on line 34 says "Get the full FQDN for hostname validation", which is accurate. The `PreciseHostname` type expects just the hostname, which is what's being provided. No issue here, but the variable naming could be clearer (e.g., `fqdn` instead of `hostname` to distinguish from the field name).
##########
api/v1beta1/solrcloud_types.go:
##########
@@ -626,6 +638,90 @@ type SolrIngressTLSTermination struct {
TLSSecret string `json:"tlsSecret,omitempty"`
}
+// SolrGatewayOptions defines how a SolrCloud should be exposed via Kubernetes Gateway API
+type SolrGatewayOptions struct {
+ // ParentRefs specifies the Gateway(s) to attach HTTPRoutes to.
+ // This is required when using method=Gateway.
+ //
+ // The referenced Gateway must already exist and be managed by your platform team.
+ // The Solr Operator only manages the HTTPRoute resources.
+ //
+ // +kubebuilder:validation:MinItems=1
+ ParentRefs []GatewayParentReference `json:"parentRefs"`
+
+ // Annotations to add to HTTPRoute resources
+ // +optional
+ Annotations map[string]string `json:"annotations,omitempty"`
+
+ // Labels to add to HTTPRoute resources
+ // +optional
+ Labels map[string]string `json:"labels,omitempty"`
+
+ // BackendTLSPolicy defines TLS configuration for backend connections from Gateway to Solr pods.
+ //
+ // This is used when Solr pods are running with TLS enabled (spec.solrTLS) and the Gateway
+ // needs to establish secure connections to the backend services.
+ //
+ // The Solr Operator will create BackendTLSPolicy resources for each HTTPRoute.
+ //
+ // +optional
+ BackendTLSPolicy *SolrBackendTLSPolicy `json:"backendTLSPolicy,omitempty"`
+}
+
+// GatewayParentReference identifies a parent Gateway resource to attach HTTPRoutes to
+type GatewayParentReference struct {
+ // Name of the Gateway resource
+ Name string `json:"name"`
+
+ // Namespace of the Gateway resource.
+ // If not specified, defaults to the HTTPRoute's namespace.
+ // +optional
+ Namespace *string `json:"namespace,omitempty"`
+
+ // SectionName refers to a specific listener on the Gateway.
+ // For example, "https" or "http".
+ // +optional
+ SectionName *string `json:"sectionName,omitempty"`
+}
+
+// SolrBackendTLSPolicy defines backend TLS configuration for Gateway API
+// +kubebuilder:validation:MaxProperties=1
+type SolrBackendTLSPolicy struct {
+ // CACertificateRefs contains one or more references to Kubernetes objects that contain
+ // TLS certificates of the Certificate Authorities that can be used as a trust anchor
+ // to validate the certificates presented by the backend.
+ //
+ // +optional
+ // +kubebuilder:validation:MaxItems=8
+ CACertificateRefs []GatewayCertificateReference `json:"caCertificateRefs,omitempty"`
+
+ // WellKnownCACertificates specifies whether system CA certificates may be used in the
+ // TLS handshake between the gateway and backend pod.
+ //
+ // If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be
+ // specified with at least one entry for a valid configuration.
+ //
+ // Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both.
+ //
+ // +optional
+ WellKnownCACertificates *string `json:"wellKnownCACertificates,omitempty"`
+}
Review Comment:
The `SolrBackendTLSPolicy` type has a validation constraint `+kubebuilder:validation:MaxProperties=1` which ensures only one of `CACertificateRefs` or `WellKnownCACertificates` can be set. However, both fields are optional, so it's valid to have zero properties set (neither field configured). This means the BackendTLSPolicy generation functions could create a BackendTLSPolicy with neither `CACertificateRefs` nor `WellKnownCACertificates` set, which would be invalid according to Gateway API specifications.
The `HasBackendTLSPolicy()` function (line 1590) does check that at least one is configured before returning true, which prevents creation of invalid policies. However, the validation comment on line 688 could be clearer about this constraint. Consider adding a comment explaining that at least one field must be set for a valid BackendTLSPolicy configuration.
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,223 @@ 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)
+ if !extAddressabilityOpts.HideCommon {
+ commonPolicy := util.GenerateCommonBackendTLSPolicy(instance)
+ if commonPolicy != nil {
+ commonPolicyLogger := logger.WithValues("backendtlspolicy", commonPolicy.Name)
+ foundCommonPolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: commonPolicy.Name, Namespace: commonPolicy.Namespace}, foundCommonPolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ commonPolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, commonPolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, commonPolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundCommonPolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(commonPolicy, foundCommonPolicy, commonPolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ commonPolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundCommonPolicy)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node BackendTLSPolicies (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodePolicy := util.GenerateNodeBackendTLSPolicy(instance, nodeName)
+ if nodePolicy != nil {
+ nodePolicyLogger := logger.WithValues("backendtlspolicy", nodePolicy.Name)
+ foundNodePolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: nodePolicy.Name, Namespace: nodePolicy.Namespace}, foundNodePolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ nodePolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, nodePolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, nodePolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundNodePolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(nodePolicy, foundNodePolicy, nodePolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ nodePolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundNodePolicy)
+ }
+ }
+
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+
+ // Cleanup orphaned node BackendTLSPolicies (when scaling down)
+ backendTLSPolicyList := &gatewayv1.BackendTLSPolicyList{}
+ err = r.List(ctx, backendTLSPolicyList, listOps)
+ if err == nil {
+ for _, policy := range backendTLSPolicyList.Items {
+ // Skip the common BackendTLSPolicy
+ if policy.Name == instance.CommonBackendTLSPolicyName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if policy.Name == instance.NodeBackendTLSPolicyName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ if !nodeExists {
+ logger.Info("Deleting orphaned BackendTLSPolicy", "backendtlspolicy", policy.Name)
+ err = r.Delete(ctx, &policy)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+ }
Review Comment:
Missing cleanup logic for BackendTLSPolicy resources when:
1. The configuration changes from `hideCommon: false` to `hideCommon: true` - the common BackendTLSPolicy will not be deleted
2. The configuration changes from `hideNodes: false` to `hideNodes: true` - the node BackendTLSPolicy resources will not be deleted
3. The BackendTLSPolicy configuration is removed entirely (but method remains Gateway)
The current code only creates/updates BackendTLSPolicy resources when configured and not hidden (lines 550-640), and only deletes orphaned node policies during scale-down (lines 614-639). However, there's no explicit deletion when these resources should be removed due to configuration changes.
Consider adding cleanup logic similar to HTTPRoutes (lines 476-486) that explicitly deletes the common BackendTLSPolicy when `hideCommon` is true, and deletes node BackendTLSPolicy resources when `hideNodes` is true. Also add cleanup when `HasBackendTLSPolicy()` returns false but resources still exist.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r2728890251
##########
controllers/solrcloud_controller.go:
##########
@@ -436,6 +441,223 @@ 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)
+ if !extAddressabilityOpts.HideCommon {
+ commonPolicy := util.GenerateCommonBackendTLSPolicy(instance)
+ if commonPolicy != nil {
+ commonPolicyLogger := logger.WithValues("backendtlspolicy", commonPolicy.Name)
+ foundCommonPolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: commonPolicy.Name, Namespace: commonPolicy.Namespace}, foundCommonPolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ commonPolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, commonPolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, commonPolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundCommonPolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(commonPolicy, foundCommonPolicy, commonPolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ commonPolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundCommonPolicy)
+ }
+ }
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+
+ // Reconcile Node BackendTLSPolicies (if not hidden)
+ if !extAddressabilityOpts.HideNodes {
+ for _, nodeName := range solrNodeNames {
+ nodePolicy := util.GenerateNodeBackendTLSPolicy(instance, nodeName)
+ if nodePolicy != nil {
+ nodePolicyLogger := logger.WithValues("backendtlspolicy", nodePolicy.Name)
+ foundNodePolicy := &gatewayv1.BackendTLSPolicy{}
+ err = r.Get(ctx, types.NamespacedName{Name: nodePolicy.Name, Namespace: nodePolicy.Namespace}, foundNodePolicy)
+
+ if err != nil && errors.IsNotFound(err) {
+ nodePolicyLogger.Info("Creating BackendTLSPolicy")
+ if err = controllerutil.SetControllerReference(instance, nodePolicy, r.Scheme); err == nil {
+ err = r.Create(ctx, nodePolicy)
+ }
+ } else if err == nil {
+ var needsUpdate bool
+ needsUpdate, err = util.OvertakeControllerRef(instance, foundNodePolicy, r.Scheme)
+ if err == nil && util.CopyBackendTLSPolicyFields(nodePolicy, foundNodePolicy, nodePolicyLogger) {
+ needsUpdate = true
+ }
+ if needsUpdate {
+ nodePolicyLogger.Info("Updating BackendTLSPolicy")
+ err = r.Update(ctx, foundNodePolicy)
+ }
+ }
+
+ if err != nil {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+
+ // Cleanup orphaned node BackendTLSPolicies (when scaling down)
+ backendTLSPolicyList := &gatewayv1.BackendTLSPolicyList{}
+ err = r.List(ctx, backendTLSPolicyList, listOps)
+ if err == nil {
+ for _, policy := range backendTLSPolicyList.Items {
+ // Skip the common BackendTLSPolicy
+ if policy.Name == instance.CommonBackendTLSPolicyName() {
+ continue
+ }
+ // Check if this node still exists
+ nodeExists := false
+ for _, nodeName := range solrNodeNames {
+ if policy.Name == instance.NodeBackendTLSPolicyName(nodeName) {
+ nodeExists = true
+ break
+ }
+ }
+ if !nodeExists {
+ logger.Info("Deleting orphaned BackendTLSPolicy", "backendtlspolicy", policy.Name)
+ err = r.Delete(ctx, &policy)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // If not using Gateway method, clean up any existing HTTPRoutes
+ 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 && len(httpRouteList.Items) > 0 {
+ logger.Info("Cleaning up HTTPRoutes (method changed from Gateway)")
+ for _, httpRoute := range httpRouteList.Items {
+ err = r.Delete(ctx, &httpRoute)
+ if err != nil && !errors.IsNotFound(err) {
+ return requeueOrNot, err
+ }
+ }
+ }
Review Comment:
Good catch. Added cleanup logic for BackendTLSPolicy similar to HTTPRoute. And tested end-to-end again.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355873429
##########
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:
Good catch. Added validation so that reconciliation now returns an error if backendTLSPolicy is configured without spec.solrTLS.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "DanielRaapDev (via GitHub)" <gi...@apache.org>.
DanielRaapDev commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4628994312
No, we are not confident with testing development branches. We will use it when released.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355799100
##########
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:
The hostname used depends on cert issuance
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-3798499444
Thanks for a thorough contribution, with docs and tests. I tagged Houston and Copilot for review as I'm not fluent in Go. But I intend to test the feature in a customer environment as some point.
Should we perhaps have a way to publish a `-nightly` version of the operator for early testing?
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3351890775
##########
config/crd/bases/solr.apache.org_solrclouds.yaml:
##########
@@ -9889,6 +10395,106 @@ spec:
For the LoadBalancer method, this field is optional and will only be used when useExternalAddress=true.
If used with the LoadBalancer method, you will need DNS routing to the LoadBalancer IP address through the url template given above.
type: string
+ gateway:
+ description: |-
+ Gateway defines settings for Kubernetes Gateway API routing.
+
+ This option is only available when Method=Gateway.
+ The referenced Gateway must already exist and be managed by your platform team.
+ The Solr Operator only manages the HTTPRoute resources.
+ properties:
Review Comment:
@DanielRaapDev
The [hostnames](https://github.com/wwgrainger/solr-operator/blob/gtw-ingress-support/controllers/util/gateway_util.go#L42) field on the HTTPRoute is actually already being populated, auto-generated from [spec.solrAddressability.external.domainName](https://github.com/wwgrainger/solr-operator/blob/gtw-ingress-support/controllers/util/gateway_util.go#L28) and additionalDomainNames ( GenerateCommonHTTPRoute in gateway_util.go). This produces the hostname pattern like {prefix}-{cloud}-solrcloud. {domain} which is also used for status.externalCommonAddress, BackendTLSPolicy fqdn, and what Solr uses as its external URL.
I've added an additionalHostnames field to SolrGatewayOptions that would allow users to append extra alias hostnames to the common HTTPRoute so that we cover the use case of routing additional alias names to the same service.
I did not make this an override because the auto-generated hostnames are tied to status.externalCommonAddress and BackendTLSPolicy and so replacing them would create a mismatch between what Solr thinks its address is and what the Gateway actually routes to. Hope it made sense. Thx!
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4615615417
@chinmoysahu I tried to bring the branch up to date with main and push, but you have not allowed maintainers to push to your fork. Can you please either allow push or update the branch yourself? Hoping to get the last round of reviews and land this..
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355863315
##########
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:
done
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "DanielRaapDev (via GitHub)" <gi...@apache.org>.
DanielRaapDev commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3354028321
##########
config/crd/bases/solr.apache.org_solrclouds.yaml:
##########
@@ -9889,6 +10395,106 @@ spec:
For the LoadBalancer method, this field is optional and will only be used when useExternalAddress=true.
If used with the LoadBalancer method, you will need DNS routing to the LoadBalancer IP address through the url template given above.
type: string
+ gateway:
+ description: |-
+ Gateway defines settings for Kubernetes Gateway API routing.
+
+ This option is only available when Method=Gateway.
+ The referenced Gateway must already exist and be managed by your platform team.
+ The Solr Operator only manages the HTTPRoute resources.
+ properties:
Review Comment:
Thx for the quick response. You defintily got a better understanding of Solr addressabilty than I have ;)
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355856211
##########
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:
Fixed in the latest commit - The cleanup now deletes all non-common HTTPRoutes when hideNodes=true, matching the BackendTLSPolicy cleanup pattern.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3355791601
##########
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:
The hostname used depends on cert issuance
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "HoustonPutman (via GitHub)" <gi...@apache.org>.
HoustonPutman commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4722583696
> @HoustonPutman do you have some cycles to bless this, add changelog and merge, before main moves too much again?
Yeah I'll do it this week.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "DanielRaapDev (via GitHub)" <gi...@apache.org>.
DanielRaapDev commented on code in PR #815:
URL: https://github.com/apache/solr-operator/pull/815#discussion_r3349633016
##########
config/crd/bases/solr.apache.org_solrclouds.yaml:
##########
@@ -9889,6 +10395,106 @@ spec:
For the LoadBalancer method, this field is optional and will only be used when useExternalAddress=true.
If used with the LoadBalancer method, you will need DNS routing to the LoadBalancer IP address through the url template given above.
type: string
+ gateway:
+ description: |-
+ Gateway defines settings for Kubernetes Gateway API routing.
+
+ This option is only available when Method=Gateway.
+ The referenced Gateway must already exist and be managed by your platform team.
+ The Solr Operator only manages the HTTPRoute resources.
+ properties:
Review Comment:
Now I saw that routes will be created for common and per-node. So this could be `additionalCommonHostnames` that are used as alias for the common service. Or if set these hostnames are the only ones used for the `HTTPRoute` and if unset the names are generated like now with a pattern.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-5181263500
@chinmoysahu Are you available for following up on this PR and re-create it on a private github account?
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-6330197261
This PR has had no activity for 60 days and is now labeled as stale. Any new activity will remove the stale label. To attract more reviewers, please tag people who might be familiar with the code area and/or notify the dev@solr.apache.org mailing list. To exempt this PR from being marked as stale, make it a draft PR or add the label "exempt-stale". If left unattended, this PR will be closed after another 60 days of inactivity. Thank you for your contribution!
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-3801307317
Thanks @janhoy for the feedback and enabling Copilot review. I have addressed all the comments. Happy to address any further improvement suggestions or fixes.
Also "-nightly version" publish of the operator for early testing would be great!
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "HoustonPutman (via GitHub)" <gi...@apache.org>.
HoustonPutman commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-3802218817
Yeah I'll try to take a look by the end of the week.
And yeah less operator cycles nowadays unfortunately. But there will be a good amount of work required for Solr 10 I think. So that should make a push for improvements in the short-term!
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "janhoy (via GitHub)" <gi...@apache.org>.
janhoy commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4624845062
So we have now had some more eyes, and another round of AI review with fixes. And branch is up to date with main.
I have nothing further to add. I have not tested it in an actual cluster though, have you @DanielRaapDev ?
@HoustonPutman do you have some cycles to bless this, add changelog and merge, before main moves too much again?
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-4622024514
Addressed Copilot comments and also the Lint/Smoke test issues - Lint and Smoke tests pass locally.
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org
Re: [PR] Add Gateway API support for SolrCloud external addressability [solr-operator]
Posted by "chinmoysahu (via GitHub)" <gi...@apache.org>.
chinmoysahu commented on PR #815:
URL: https://github.com/apache/solr-operator/pull/815#issuecomment-5182452294
@janhoy As suggested, I've re-opened this from a personal fork so maintainers have edit access:
https://github.com/apache/solr-operator/pull/843
I can close this one if the new PR looks good on your end and that you're able to push to it, Thx!
--
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: issues-unsubscribe@solr.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscribe@solr.apache.org
For additional commands, e-mail: issues-help@solr.apache.org