This is an automated email from the ASF dual-hosted git repository.
gerlowskija pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-operator.git
The following commit(s) were added to refs/heads/main by this push:
new 6bd0fce Migrate from zkcli.sh to 'solr zk' CLI tool (#738)
6bd0fce is described below
commit 6bd0fce03445dfffb66bd8d646b85864c287fe4a
Author: Michael <[email protected]>
AuthorDate: Mon Jan 6 14:44:01 2025 -0500
Migrate from zkcli.sh to 'solr zk' CLI tool (#738)
zkcli.sh is deprecated and will be removed in Solr 10. Our existing
use of it also wasn't quite flexible enough to gracefully handle
missing "security.json" nodes.
Migrating to "bin/solr zk" in our "setup-zk" initContainer fixes both
of these issues.
Closes #720
Closes #731
---------
Co-authored-by: Jason Gerlowski <[email protected]>
---
.../solrcloud_controller_basic_auth_test.go | 11 ++-
controllers/util/solr_security_util.go | 18 ++++-
docs/development.md | 5 ++
helm/solr-operator/Chart.yaml | 9 +++
tests/e2e/solrcloud_security_json_test.go | 69 +++++++++++++++++
tests/e2e/test_utils_test.go | 88 ++++++++++++++++++++++
6 files changed, 192 insertions(+), 8 deletions(-)
diff --git a/controllers/solrcloud_controller_basic_auth_test.go
b/controllers/solrcloud_controller_basic_auth_test.go
index cd3fbf5..c271393 100644
--- a/controllers/solrcloud_controller_basic_auth_test.go
+++ b/controllers/solrcloud_controller_basic_auth_test.go
@@ -351,10 +351,13 @@ func expectBasicAuthConfigOnPodTemplateWithGomega(g
Gomega, solrCloud *solrv1bet
func expectPutSecurityJsonInZkCmd(g Gomega, expInitContainer
*corev1.Container) {
g.Expect(expInitContainer).To(Not(BeNil()), "Didn't find the setup-zk
InitContainer in the sts!")
- expCmd :=
"ZK_SECURITY_JSON=$(/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost
${ZK_HOST} -cmd get /security.json || echo 'failed-to-get-security.json'); " +
- "if [ ${#ZK_SECURITY_JSON} -lt 3 ]; then " +
- "echo $SECURITY_JSON > /tmp/security.json; " +
- "/opt/solr/server/scripts/cloud-scripts/zkcli.sh -zkhost
${ZK_HOST} -cmd putfile /security.json /tmp/security.json; echo \"put
security.json in ZK\"; fi"
+ expCmd := "solr zk cp zk:/security.json /tmp/current_security.json
>/dev/null 2>&1; " +
+ "GET_CURRENT_SECURITY_JSON_EXIT_CODE=$?; if [
${GET_CURRENT_SECURITY_JSON_EXIT_CODE} -eq 0 ]; then " +
+ "if [ ! -s /tmp/current_security.json ] || grep -q '^{}$'
/tmp/current_security.json ]; then " +
+ "echo $SECURITY_JSON > /tmp/security.json; solr zk cp
/tmp/security.json zk:/security.json >/dev/null 2>&1; " +
+ " echo 'Blank security.json found. Put new security.json in
ZK'; fi; elif [ ${GET_CURRENT_SECURITY_JSON_EXIT_CODE} -eq 1 ]; then " +
+ " echo $SECURITY_JSON > /tmp/security.json; solr zk cp
/tmp/security.json zk:/security.json >/dev/null 2>&1; " +
+ " echo 'No security.json found. Put new security.json in ZK';
fi"
g.Expect(expInitContainer.Command[2]).To(ContainSubstring(expCmd),
"setup-zk initContainer not configured to bootstrap security.json!")
}
diff --git a/controllers/util/solr_security_util.go
b/controllers/util/solr_security_util.go
index 97f7e60..2eb8679 100644
--- a/controllers/util/solr_security_util.go
+++ b/controllers/util/solr_security_util.go
@@ -237,10 +237,20 @@ func addHostHeaderToProbe(httpGet *corev1.HTTPGetAction,
host string) {
}
func cmdToPutSecurityJsonInZk() string {
- scriptsDir := "/opt/solr/server/scripts/cloud-scripts"
- cmd := " ZK_SECURITY_JSON=$(%s/zkcli.sh -zkhost ${ZK_HOST} -cmd get
/security.json || echo 'failed-to-get-security.json'); "
- cmd += "if [ ${#ZK_SECURITY_JSON} -lt 3 ]; then echo $SECURITY_JSON >
/tmp/security.json; %s/zkcli.sh -zkhost ${ZK_HOST} -cmd putfile /security.json
/tmp/security.json; echo \"put security.json in ZK\"; fi"
- return fmt.Sprintf(cmd, scriptsDir, scriptsDir)
+ cmd := " solr zk cp zk:/security.json /tmp/current_security.json
>/dev/null 2>&1; " +
+ " GET_CURRENT_SECURITY_JSON_EXIT_CODE=$?; " +
+ "if [ ${GET_CURRENT_SECURITY_JSON_EXIT_CODE} -eq 0 ]; then " +
// JSON already exists
+ "if [ ! -s /tmp/current_security.json ] || grep -q '^{}$'
/tmp/current_security.json ]; then " + // File doesn't exist, is empty, or is
just '{}'
+ " echo $SECURITY_JSON > /tmp/security.json;" +
+ " solr zk cp /tmp/security.json zk:/security.json >/dev/null
2>&1; " +
+ " echo 'Blank security.json found. Put new security.json in
ZK'; " +
+ "fi; " + // TODO: Consider checking a diff and still applying
over the top
+ "elif [ ${GET_CURRENT_SECURITY_JSON_EXIT_CODE} -eq 1 ]; then "
+ // JSON doesn't exist, but not other error types
+ " echo $SECURITY_JSON > /tmp/security.json;" +
+ " solr zk cp /tmp/security.json zk:/security.json >/dev/null
2>&1; " +
+ " echo 'No security.json found. Put new security.json in ZK'; "
+
+ "fi"
+ return cmd
}
// Add auth data to the supplied Context using secrets already resolved
(stored in the SecurityConfig)
diff --git a/docs/development.md b/docs/development.md
index f8d005a..cf9907c 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -55,6 +55,11 @@ export PATH="$PATH:$GOPATH/bin" # You likely want to add
this line to your ~/.ba
make install-dependencies
```
+Note: if you have previously installed/older versions of dependencies, you can
first clear these dependencies with:
+```bash
+make clean
+```
+
## Build the Solr CRDs
If you have changed anything in the [APIs directory](/api/v1beta1), you will
need to run the following command to regenerate all Solr CRDs.
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index af4b173..44ca43e 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -103,6 +103,15 @@ annotations:
url: https://github.com/apache/solr-operator/issues/682
- name: Github PR
url: https://github.com/apache/solr-operator/pull/692
+ - kind: fixed
+ description: setup-zk initContainer now gracefully handles absent
security.json on initial upload.
+ links:
+ - name: Github Issue
+ url: https://github.com/apache/solr-operator/issues/720
+ - name: Additional Github Issue
+ url: https://github.com/apache/solr-operator/issues/731
+ - name: Github PR
+ url: https://github.com/apache/solr-operator/pull/738
artifacthub.io/images: |
- name: solr-operator
image: apache/solr-operator:v0.9.0-prerelease
diff --git a/tests/e2e/solrcloud_security_json_test.go
b/tests/e2e/solrcloud_security_json_test.go
new file mode 100644
index 0000000..1ceef5e
--- /dev/null
+++ b/tests/e2e/solrcloud_security_json_test.go
@@ -0,0 +1,69 @@
+/*
+ * 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/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+ "k8s.io/utils/pointer"
+)
+
+var _ = FDescribe("E2E - SolrCloud - Security JSON", func() {
+ var (
+ solrCloud *solrv1beta1.SolrCloud
+ )
+
+ BeforeEach(func() {
+ solrCloud = generateBaseSolrCloudWithSecurityJSON(1)
+ })
+
+ JustBeforeEach(func(ctx context.Context) {
+ By("generating the security.json secret and basic auth secret")
+ generateSolrSecuritySecret(ctx, solrCloud)
+ generateSolrBasicAuthSecret(ctx, solrCloud)
+
+ 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("Provided Zookeeper", func() {
+ BeforeEach(func() {
+ solrCloud.Spec.ZookeeperRef = &solrv1beta1.ZookeeperRef{
+ ProvidedZookeeper: &solrv1beta1.ZookeeperSpec{
+ Replicas: pointer.Int32(1),
+ Ephemeral: &solrv1beta1.ZKEphemeral{},
+ },
+ }
+ })
+
+ // All testing will be done in the "JustBeforeEach" logic, no
additional tests required here
+ FIt("Starts correctly", func(ctx context.Context) {})
+ })
+})
diff --git a/tests/e2e/test_utils_test.go b/tests/e2e/test_utils_test.go
index 95677d3..203b93e 100644
--- a/tests/e2e/test_utils_test.go
+++ b/tests/e2e/test_utils_test.go
@@ -581,6 +581,94 @@ func generateBaseSolrCloud(replicas int)
*solrv1beta1.SolrCloud {
}
}
+// Uses default password from docs : SolrRocks
+// The hash is generated as: base64(sha256(sha256(salt+password)))
base64(salt))
+// See
https://solr.apache.org/guide/solr/latest/deployment-guide/basic-authentication-plugin.html
+func generateSolrSecuritySecret(ctx context.Context, solrCloud
*solrv1beta1.SolrCloud) {
+ securityJsonSecret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: solrCloud.Name + "-security-secret",
+ Namespace: solrCloud.Namespace,
+ },
+ StringData: map[string]string{
+ "security.json": `{
+ "authentication": {
+ "blockUnknown": false,
+ "class": "solr.BasicAuthPlugin",
+ "credentials": {
+ "test-oper":
"IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0=
Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c="
+ },
+ "realm": "Solr Basic Auth",
+ "forwardCredentials": false
+ },
+ "authorization": {
+ "class": "solr.RuleBasedAuthorizationPlugin",
+ "user-role": {
+ "test-oper": "test-oper"
+ },
+ "permissions": [
+ {
+ "name": "cluster",
+ "role": null
+ },
+ {
+ "name": "collections",
+ "role": null,
+ "collection": "*"
+ }
+ ]
+ }
+ }`,
+ },
+ Type: corev1.SecretTypeOpaque,
+ }
+ Expect(k8sClient.Create(ctx, securityJsonSecret)).To(Succeed(), "Failed
to create secret for security json in namespace "+solrCloud.Namespace)
+
+ expectSecret(ctx, solrCloud, securityJsonSecret.Name)
+ return
+}
+
+func generateSolrBasicAuthSecret(ctx context.Context, solrCloud
*solrv1beta1.SolrCloud) {
+ basicAuthSecret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: solrCloud.Name + "-basic-auth-secret",
+ Namespace: solrCloud.Namespace,
+ },
+ // Using default creds
+ StringData: map[string]string{
+ "username": "test-oper",
+ "password": "SolrRocks",
+ },
+ Type: corev1.SecretTypeBasicAuth,
+ }
+ Expect(k8sClient.Create(ctx, basicAuthSecret)).To(Succeed(), "Failed to
create secret for basic auth in namespace "+solrCloud.Namespace)
+
+ expectSecret(ctx, solrCloud, basicAuthSecret.Name)
+ return
+}
+
+func generateBaseSolrCloudWithSecurityJSON(replicas int)
*solrv1beta1.SolrCloud {
+ solrCloud := generateBaseSolrCloud(replicas)
+
+ // Ensure SolrSecurity is initialized
+ if solrCloud.Spec.SolrSecurity == nil {
+ solrCloud.Spec.SolrSecurity = &solrv1beta1.SolrSecurityOptions{}
+ }
+
+ solrCloud.Spec.SolrSecurity.BootstrapSecurityJson =
&corev1.SecretKeySelector{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: solrCloud.Name + "-security-secret",
+ },
+ Key: "security.json",
+ }
+
+ solrCloud.Spec.SolrSecurity.AuthenticationType = "Basic"
+
+ solrCloud.Spec.SolrSecurity.BasicAuthSecret = solrCloud.Name +
"-basic-auth-secret"
+
+ return solrCloud
+}
+
func generateBaseSolrCloudWithPlacementPolicy(replicas int, placementPlugin
string) *solrv1beta1.SolrCloud {
solrCloud := generateBaseSolrCloud(replicas)
solrCloud.Spec.CustomSolrKubeOptions.PodOptions.EnvVariables =
[]corev1.EnvVar{