diff --git a/docs/parameters.md b/docs/parameters.md index 6c161126..f21cc3a6 100644 --- a/docs/parameters.md +++ b/docs/parameters.md @@ -65,6 +65,7 @@ | **MAX_HINTS_DELIVERY_THREADS** | The maximum number of delivery threads for hinted handoff. | 2 | | **BATCHLOG_REPLAY_THROTTLE_IN_KB** | The total maximum throttle for replaying failed logged batches in KBs per second. | 1024 | | **AUTHENTICATOR** | Authentication backend, implementing IAuthenticator; used to identify users. | AllowAllAuthenticator | +| **AUTHENTICATION_SECRET_NAME** | Name of the secret containing the credentials used by the operator when running 'nodetool' for its functionality. Only relevant if AUTHENTICATOR is set to 'PasswordAuthenticator'. The secret needs to have a 'username' and a 'password' entry. | | | **AUTHORIZER** | Authorization backend, implementing IAuthorizer; used to limit access/provide permissions. | AllowAllAuthorizer | | **ROLE_MANAGER** | Part of the Authentication & Authorization backend that implements IRoleManager to maintain grants and memberships between roles, By default, the value set is Apache Cassandra's out of the box Role Manager: CassandraRoleManager | CassandraRoleManager | | **ROLES_VALIDITY_IN_MS** | Validity period for roles cache; set to 0 to disable | 2000 | @@ -191,6 +192,7 @@ | **REPAIR_SESSION_MAX_TREE_DEPTH** | Limits the maximum Merkle tree depth to avoid consuming too much memory during repairs. | | | **ENABLE_SASI_INDEXES** | Enables SASI index creation on this node. SASI indexes are considered experimental and are not recommended for production use. | | | **CUSTOM_CASSANDRA_YAML_BASE64** | Base64-encoded Cassandra properties appended to cassandra.yaml. | | +| **KUBECTL_VERSION** | Version of 'bitnami/kubectl' image. This image is used for some functionality of the operator. | 1.18.2 | | **JVM_OPT_AVAILABLE_PROCESSORS** | In a multi-instance deployment, multiple Cassandra instances will independently assume that all CPU processors are available to it. This setting allows you to specify a smaller set of processors and perhaps have affinity. | | | **JVM_OPT_JOIN_RING** | Set to false to start Cassandra on a node but not have the node join the cluster. | | | **JVM_OPT_LOAD_RING_STATE** | Set to false to clear all gossip state for the node on restart. Use when you have changed node information in cassandra.yaml (such as listen_address). | | @@ -219,3 +221,4 @@ | **JVM_OPT_G1R_SET_UPDATING_PAUSE_TIME_PERCENT** | Have the JVM do less remembered set work during STW, instead preferring concurrent GC. Reduces p99.9 latency. | | | **CUSTOM_JVM_OPTIONS_BASE64** | Base64-encoded JVM options appended to jvm.options. | | | **POD_MANAGEMENT_POLICY** | podManagementPolicy of the Cassandra Statefulset | OrderedReady | +| **REPAIR_POD** | Name of the pod on which 'nodetool repair' should be run. | | diff --git a/docs/security.md b/docs/security.md index 39715f93..fe22b122 100644 --- a/docs/security.md +++ b/docs/security.md @@ -82,3 +82,34 @@ kubectl kudo install cassandra \ Check out the [parameters reference](./parameters.md) for a complete list of all configurable settings available for KUDO Cassandra security. + +## Authentication and Authorization + +The KUDO Cassandra operator can be configured to authenticate and authorize access to the Cassandra cluster. The `AUTHENTICATOR` parameter sets the [authenticator](http://cassandra.apache.org/doc/3.11/operating/security.html#authentication), the `AUTHORIZER` parameter sets the [authorizer](http://cassandra.apache.org/doc/3.11/operating/security.html#authorization). + +### Authentication credentials + +Some functionality of the operator use `nodetool`, thus these calls need to be authenticated as well. With enabled password authentication, create a [secret](https://kubernetes.io/docs/concepts/configuration/secret/) that contains the credentials of the user the operator should use and set the `AUTHENTICATION_SECRET_NAME` parameter accordingly. + +Here's an example of a secret that uses the default cassandra/cassandra credentials: + +``` +apiVersion: v1 +kind: Secret +metadata: + name: cassandra-credential +type: Opaque +data: + username: Y2Fzc2FuZHJh + password: Y2Fzc2FuZHJh +``` + +Reference this when installing the Cassandra operator with authentication. + +``` +kubectl kudo install cassandra \ + --instance=cassandra \ + --namespace=kudo-cassandra \ + -p AUTHENTICATOR=PasswordAuthenticator \ + -p AUTHENTICATION_SECRET_NAME=cassandra-credential +``` diff --git a/operator/params.yaml b/operator/params.yaml index 829a3413..fb54e434 100644 --- a/operator/params.yaml +++ b/operator/params.yaml @@ -277,6 +277,10 @@ parameters: description: "Authentication backend, implementing IAuthenticator; used to identify users." default: "AllowAllAuthenticator" + - name: AUTHENTICATION_SECRET_NAME + description: "Name of the secret containing the credentials used by the operator when running 'nodetool' for its functionality. Only relevant if AUTHENTICATOR is set to 'PasswordAuthenticator'. The secret needs to have a 'username' and a 'password' entry." + default: "" + - name: AUTHORIZER description: "Authorization backend, implementing IAuthorizer; used to limit access/provide permissions." default: "AllowAllAuthorizer" @@ -781,6 +785,10 @@ parameters: description: "Base64-encoded Cassandra properties appended to cassandra.yaml." default: "" + - name: KUBECTL_VERSION + description: "Version of 'bitnami/kubectl' image. This image is used for some functionality of the operator." + default: "1.18.2" + ################################################################################ ################################ JVM Options ################################### ################################################################################ diff --git a/operator/templates/node-scripts.yaml b/operator/templates/node-scripts.yaml index c8e99d46..298a7315 100644 --- a/operator/templates/node-scripts.yaml +++ b/operator/templates/node-scripts.yaml @@ -1,20 +1,30 @@ +{{ $auth_params := "" }} +{{ if .Params.AUTHENTICATION_SECRET_NAME }} +{{ $auth_params = "-u $(cat /etc/cassandra/authentication/username) -pwf <(paste -d ' ' /etc/cassandra/authentication/username /etc/cassandra/authentication/password)" }} +{{ end }} apiVersion: v1 kind: ConfigMap metadata: name: {{ .Name }}-node-scripts namespace: {{ .Namespace }} data: + node-drain.sh: | + {{ if ne $.Params.JMX_LOCAL_ONLY "true" }} + nodetool {{ $auth_params }} --ssl drain + {{ else }} + nodetool {{ $auth_params }} drain + {{ end }} node-readiness-probe.sh: | {{ if ne $.Params.JMX_LOCAL_ONLY "true" }} - nodetool --ssl status -p {{ .Params.JMX_PORT }} | grep -q "UN ${POD_IP}" + nodetool {{ $auth_params }} --ssl status -p {{ .Params.JMX_PORT }} | grep -q "UN ${POD_IP}" {{ else }} - nodetool status -p {{ .Params.JMX_PORT }} | grep -q "UN ${POD_IP}" + nodetool {{ $auth_params }} status -p {{ .Params.JMX_PORT }} | grep -q "UN ${POD_IP}" {{ end }} node-liveness-probe.sh: | {{ if ne $.Params.JMX_LOCAL_ONLY "true" }} - nodetool --ssl info + nodetool {{ $auth_params }} --ssl info {{ else }} - nodetool info + nodetool {{ $auth_params }} info {{ end }} generate-rackdc-properties.sh: | # Generate the rackdc-properties diff --git a/operator/templates/repair-job.yaml b/operator/templates/repair-job.yaml index 124aa660..2869df65 100644 --- a/operator/templates/repair-job.yaml +++ b/operator/templates/repair-job.yaml @@ -1,3 +1,7 @@ +{{ $auth_params := "" }} +{{ if $.Params.AUTHENTICATION_SECRET_NAME }} +{{ $auth_params = "-u ${SECRET_USERNAME} -pw ${SECRET_PASSWORD}" }} +{{ end }} --- apiVersion: batch/v1 kind: Job @@ -12,7 +16,21 @@ spec: spec: containers: - name: repair-job - image: bitnami/kubectl:1.18.0 - command: [ "kubectl", "exec", "{{ $.Params.REPAIR_POD }}", "--", "nodetool", "repair" ] + image: bitnami/kubectl:{{ $.Params.KUBECTL_VERSION }} + command: ["/bin/bash"] + args: [ "-c", "kubectl exec {{ $.Params.REPAIR_POD }} -- nodetool {{ $auth_params }} repair"] + {{ if $.Params.AUTHENTICATION_SECRET_NAME }} + env: + - name: SECRET_USERNAME + valueFrom: + secretKeyRef: + name: {{ $.Params.AUTHENTICATION_SECRET_NAME }} + key: username + - name: SECRET_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $.Params.AUTHENTICATION_SECRET_NAME }} + key: password + {{ end }} restartPolicy: Never serviceAccountName: {{ .Name }}-node-repairer diff --git a/operator/templates/stateful-set.yaml b/operator/templates/stateful-set.yaml index e9e40631..ddc7b5f7 100644 --- a/operator/templates/stateful-set.yaml +++ b/operator/templates/stateful-set.yaml @@ -121,8 +121,8 @@ spec: preStop: exec: command: - - nodetool - - drain + - /bin/bash + - /etc/cassandra/node-drain.sh readinessProbe: exec: command: @@ -225,6 +225,9 @@ spec: - name: jvm-options mountPath: /etc/cassandra/jvm.options subPath: jvm.options + - name: node-scripts + mountPath: /etc/cassandra/node-drain.sh + subPath: node-drain.sh - name: node-scripts mountPath: /etc/cassandra/node-readiness-probe.sh subPath: node-readiness-probe.sh @@ -246,6 +249,11 @@ spec: - name: nodetool-ssl-properties mountPath: /nodetool-ssl-properties {{ end }} + {{ if $.Params.AUTHENTICATION_SECRET_NAME }} + - name: authentication-secret + mountPath: /etc/cassandra/authentication + readOnly: true + {{ end }} {{ if eq $.Params.PROMETHEUS_EXPORTER_ENABLED "true" }} - name: prometheus-exporter image: {{ $.Params.PROMETHEUS_EXPORTER_DOCKER_IMAGE }} @@ -306,7 +314,7 @@ spec: {{ if $.Params.NODE_TOPOLOGY }} initContainers: - name: node-resolver - image: bitnami/kubectl:latest + image: bitnami/kubectl:{{ $.Params.KUBECTL_VERSION }} command: - "sh" - "-c" @@ -384,6 +392,11 @@ spec: configMap: name: {{ $.Name }}-nodetool-ssl-properties {{ end }} + {{ if $.Params.AUTHENTICATION_SECRET_NAME }} + - name: authentication-secret + secret: + secretName: {{ $.Params.AUTHENTICATION_SECRET_NAME }} + {{ end }} volumeClaimTemplates: - metadata: name: var-lib-cassandra @@ -400,4 +413,4 @@ spec: {{ if $.Params.NODE_STORAGE_CLASS }} storageClassName: {{ $.Params.NODE_STORAGE_CLASS }} {{ end }} -{{ end }} \ No newline at end of file +{{ end }} diff --git a/templates/operator/params.yaml.template b/templates/operator/params.yaml.template index 861c1793..78e29dd2 100644 --- a/templates/operator/params.yaml.template +++ b/templates/operator/params.yaml.template @@ -277,6 +277,10 @@ parameters: description: "Authentication backend, implementing IAuthenticator; used to identify users." default: "AllowAllAuthenticator" + - name: AUTHENTICATION_SECRET_NAME + description: "Name of the secret containing the credentials used by the operator when running 'nodetool' for its functionality. Only relevant if AUTHENTICATOR is set to 'PasswordAuthenticator'. The secret needs to have a 'username' and a 'password' entry." + default: "" + - name: AUTHORIZER description: "Authorization backend, implementing IAuthorizer; used to limit access/provide permissions." default: "AllowAllAuthorizer" @@ -781,6 +785,10 @@ parameters: description: "Base64-encoded Cassandra properties appended to cassandra.yaml." default: "" + - name: KUBECTL_VERSION + description: "Version of 'bitnami/kubectl' image. This image is used for some functionality of the operator." + default: "1.18.2" + ################################################################################ ################################ JVM Options ################################### ################################################################################ diff --git a/templates/operator/templates/stateful-set.yaml.template b/templates/operator/templates/stateful-set.yaml.template index e9e40631..ddc7b5f7 100644 --- a/templates/operator/templates/stateful-set.yaml.template +++ b/templates/operator/templates/stateful-set.yaml.template @@ -121,8 +121,8 @@ spec: preStop: exec: command: - - nodetool - - drain + - /bin/bash + - /etc/cassandra/node-drain.sh readinessProbe: exec: command: @@ -225,6 +225,9 @@ spec: - name: jvm-options mountPath: /etc/cassandra/jvm.options subPath: jvm.options + - name: node-scripts + mountPath: /etc/cassandra/node-drain.sh + subPath: node-drain.sh - name: node-scripts mountPath: /etc/cassandra/node-readiness-probe.sh subPath: node-readiness-probe.sh @@ -246,6 +249,11 @@ spec: - name: nodetool-ssl-properties mountPath: /nodetool-ssl-properties {{ end }} + {{ if $.Params.AUTHENTICATION_SECRET_NAME }} + - name: authentication-secret + mountPath: /etc/cassandra/authentication + readOnly: true + {{ end }} {{ if eq $.Params.PROMETHEUS_EXPORTER_ENABLED "true" }} - name: prometheus-exporter image: {{ $.Params.PROMETHEUS_EXPORTER_DOCKER_IMAGE }} @@ -306,7 +314,7 @@ spec: {{ if $.Params.NODE_TOPOLOGY }} initContainers: - name: node-resolver - image: bitnami/kubectl:latest + image: bitnami/kubectl:{{ $.Params.KUBECTL_VERSION }} command: - "sh" - "-c" @@ -384,6 +392,11 @@ spec: configMap: name: {{ $.Name }}-nodetool-ssl-properties {{ end }} + {{ if $.Params.AUTHENTICATION_SECRET_NAME }} + - name: authentication-secret + secret: + secretName: {{ $.Params.AUTHENTICATION_SECRET_NAME }} + {{ end }} volumeClaimTemplates: - metadata: name: var-lib-cassandra @@ -400,4 +413,4 @@ spec: {{ if $.Params.NODE_STORAGE_CLASS }} storageClassName: {{ $.Params.NODE_STORAGE_CLASS }} {{ end }} -{{ end }} \ No newline at end of file +{{ end }} diff --git a/tests/go.sum b/tests/go.sum index ee84770c..7d177412 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -16,6 +16,7 @@ github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1Gn github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= @@ -39,6 +40,7 @@ github.com/alecthomas/gometalinter v3.0.0+incompatible/go.mod h1:qfIpQGGz3d+Nmgy github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 h1:H/GMMKYPkEIC3DF/JWQz8Pdd+Feifov2EIgGfNpeogI= github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -327,6 +329,7 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -549,6 +552,7 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20191106092431-e228e37189d3 h1:ho4SukHOmqjp7XHH7nPNx7GcgDK6ObVflhAQAT7MvpE= gopkg.in/yaml.v3 v3.0.0-20191106092431-e228e37189d3/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= @@ -595,6 +599,7 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= sigs.k8s.io/controller-runtime v0.5.1 h1:TNidCfVoU/cs2i+9xoTcL/l7yhl0bDhYXU0NCG6wmiE= sigs.k8s.io/controller-runtime v0.5.1/go.mod h1:Uojny7gvg55YLQnEGnPzRE3dC4ik2tRlZJgOUCWXAV4= sigs.k8s.io/controller-tools v0.2.6/go.mod h1:9VKHPszmf2DHz/QmHkcfZoewO6BL7pPs9uAiBVsaJSE= +sigs.k8s.io/kind v0.6.1 h1:13zGO85bX34eyJFQWilTWyuqncRAhCaHoffF9vPuYbg= sigs.k8s.io/kind v0.6.1/go.mod h1:dhW5h0O4PRVq8B6eExphIa9mBnrlBzxEz3R/P1YcYj0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= diff --git a/tests/suites/authentication/authentication_test.go b/tests/suites/authentication/authentication_test.go new file mode 100644 index 00000000..d1af6bd6 --- /dev/null +++ b/tests/suites/authentication/authentication_test.go @@ -0,0 +1,111 @@ +package authentication + +import ( + "fmt" + "os" + "testing" + "time" + + testclient "github.com/kudobuilder/test-tools/pkg/client" + "github.com/kudobuilder/test-tools/pkg/kubernetes" + "github.com/kudobuilder/test-tools/pkg/kudo" + . "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/reporters" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + + "github.com/mesosphere/kudo-cassandra-operator/tests/cassandra" +) + +var ( + kubeConfigPath = os.Getenv("KUBECONFIG") + operatorName = os.Getenv("OPERATOR_NAME") + operatorDirectory = os.Getenv("OPERATOR_DIRECTORY") + + instanceName = fmt.Sprintf("%s-instance", operatorName) + testNamespace = "authentication" +) + +var _ = Describe("Authentication tests", func() { + var ( + client testclient.Client + credentials kubernetes.Secret + operator kudo.Operator + ) + + AfterEach(func() { + err := operator.Uninstall() + Expect(err).NotTo(HaveOccurred()) + + err = credentials.Delete() + Expect(err).NotTo(HaveOccurred()) + + err = kubernetes.DeleteNamespace(client, testNamespace) + Expect(err).NotTo(HaveOccurred()) + }) + + Context("when using the 'PasswordAuthenticator'", func() { + It("should authenticate 'nodetool' calls", func() { + var err error + + client, err = testclient.NewForConfig(kubeConfigPath) + Expect(err).NotTo(HaveOccurred()) + + By("Setting up namespace") + err = kubernetes.CreateNamespace(client, testNamespace) + if !errors.IsAlreadyExists(err) { + Expect(err).NotTo(HaveOccurred()) + } + + By("Adding a secret containing the default user credentials") + const secretName = "authn-credentials" ////nolint:gosec + + credentials, err = kubernetes.CreateSecret(secretName). + WithNamespace(testNamespace). + WithStringData(map[string]string{ + "username": "cassandra", + "password": "cassandra", + }). + Do(client) + Expect(err).NotTo(HaveOccurred()) + + By("Installing the operator with 'PasswordAuthenticator'") + parameters := map[string]string{ + "AUTHENTICATOR": "PasswordAuthenticator", + "AUTHENTICATION_SECRET_NAME": secretName, + } + + operator, err = kudo.InstallOperator(operatorDirectory). + WithNamespace(testNamespace). + WithInstance(instanceName). + WithParameters(parameters). + Do(client) + Expect(err).NotTo(HaveOccurred()) + + err = operator.Instance.WaitForPlanComplete("deploy", kudo.WaitTimeout(time.Minute*10)) + Expect(err).NotTo(HaveOccurred()) + + By("Triggering a Cassandra node repair which uses 'nodetool'") + podName, err := cassandra.FirstPodName(operator.Instance) + Expect(err).To(BeNil()) + + err = operator.Instance.UpdateParameters(map[string]string{ + "REPAIR_POD": podName, + }) + Expect(err).To(BeNil()) + + err = operator.Instance.WaitForPlanComplete("repair-pod") + Expect(err).To(BeNil()) + + repair, err := cassandra.NodeWasRepaired(client, operator.Instance) + Expect(err).To(BeNil()) + Expect(repair).To(BeTrue()) + }) + }) +}) + +func TestService(t *testing.T) { + RegisterFailHandler(Fail) + junitReporter := reporters.NewJUnitReporter("authentication-test-junit.xml") + RunSpecsWithDefaultAndCustomReporters(t, "Authentication tests", []Reporter{junitReporter}) +}