Allow customization of ZK Probes (#546)

diff --git a/api/v1beta1/solrcloud_types.go b/api/v1beta1/solrcloud_types.go
index e7671ef..ad5c66a 100644
--- a/api/v1beta1/solrcloud_types.go
+++ b/api/v1beta1/solrcloud_types.go
@@ -19,6 +19,7 @@
 
 import (
 	"fmt"
+	"github.com/apache/solr-operator/controllers/zk_api"
 	"github.com/go-logr/logr"
 	"strconv"
 	"strings"
@@ -847,6 +848,11 @@
 	// Additional Zookeeper Configuration settings
 	// +optional
 	Config ZookeeperConfig `json:"config,omitempty"`
+
+	// Probes specifies the timeout values for the Readiness and Liveness Probes
+	// for the zookeeper pods.
+	// +optional
+	Probes *zk_api.Probes `json:"probes,omitempty"`
 }
 
 type ZKPersistence struct {
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 513740c..0e42baf 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -23,6 +23,7 @@
 package v1beta1
 
 import (
+	"github.com/apache/solr-operator/controllers/zk_api"
 	"k8s.io/api/core/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/intstr"
@@ -1595,6 +1596,11 @@
 		**out = **in
 	}
 	in.Config.DeepCopyInto(&out.Config)
+	if in.Probes != nil {
+		in, out := &in.Probes, &out.Probes
+		*out = new(zk_api.Probes)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ZookeeperSpec.
diff --git a/config/crd/bases/solr.apache.org_solrclouds.yaml b/config/crd/bases/solr.apache.org_solrclouds.yaml
index dd860e4..c0cbb16 100644
--- a/config/crd/bases/solr.apache.org_solrclouds.yaml
+++ b/config/crd/bases/solr.apache.org_solrclouds.yaml
@@ -10240,6 +10240,57 @@
                                 type: string
                             type: object
                         type: object
+                      probes:
+                        description: Probes specifies the timeout values for the Readiness
+                          and Liveness Probes for the zookeeper pods.
+                        properties:
+                          livenessProbe:
+                            properties:
+                              failureThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              initialDelaySeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              periodSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              successThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              timeoutSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                            type: object
+                          readinessProbe:
+                            properties:
+                              failureThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              initialDelaySeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              periodSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              successThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              timeoutSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                            type: object
+                        type: object
                       readOnlyAcl:
                         description: ZooKeeper ACL to use when connecting with ZK
                           for reading operations. This ACL should have READ permission
diff --git a/controllers/controller_utils_test.go b/controllers/controller_utils_test.go
index 7aa663a..10fa31c 100644
--- a/controllers/controller_utils_test.go
+++ b/controllers/controller_utils_test.go
@@ -1005,4 +1005,18 @@
 	testIngressClass = "test-ingress-class"
 	testSolrZKOpts   = "-Dsolr.zk.opts=this"
 	testSolrOpts     = "-Dsolr.opts=this"
+	testZkProbes     = zk_api.Probes{
+		ReadinessProbe: &zk_api.Probe{
+			PeriodSeconds:    3,
+			SuccessThreshold: 5,
+			TimeoutSeconds:   10,
+		},
+		LivenessProbe: &zk_api.Probe{
+			InitialDelaySeconds: 6,
+			PeriodSeconds:       4,
+			FailureThreshold:    0,
+			SuccessThreshold:    3,
+			TimeoutSeconds:      0,
+		},
+	}
 )
diff --git a/controllers/solrcloud_controller_zk_test.go b/controllers/solrcloud_controller_zk_test.go
index 7461ab4..ad39a4c 100644
--- a/controllers/solrcloud_controller_zk_test.go
+++ b/controllers/solrcloud_controller_zk_test.go
@@ -220,6 +220,7 @@
 						},
 						Config: zkConf,
 						ChRoot: "a-ch/root",
+						Probes: &testZkProbes,
 					},
 				},
 				CustomSolrKubeOptions: solrv1beta1.CustomSolrKubeOptions{
@@ -275,6 +276,7 @@
 			Expect(zkCluster.Spec.Ephemeral).To(Not(BeNil()), "ZkCluster.spec.ephemeral should not be nil")
 			Expect(zkCluster.Spec.Ephemeral.EmptyDirVolumeSource.Medium).To(BeEquivalentTo("Memory"), "Incorrect EmptyDir medium for ZK Cluster ephemeral storage")
 			Expect(zkCluster.Spec.Persistence).To(BeNil(), "ZkCluster.spec.persistence should be nil when using ephermeral storage")
+			Expect(zkCluster.Spec.Probes).To(Equal(&testZkProbes), "Incorrect zkCluster probes")
 
 			// Check ZK Pod Options
 			Expect(zkCluster.Spec.Pod.Affinity).To(Equal(testAffinity), "Incorrect zkCluster affinity")
diff --git a/controllers/util/zk_util.go b/controllers/util/zk_util.go
index 0e1835f..1365683 100644
--- a/controllers/util/zk_util.go
+++ b/controllers/util/zk_util.go
@@ -164,6 +164,10 @@
 		}
 	}
 
+	if zkSpec.Probes != nil {
+		zkCluster.Spec.Probes = zkSpec.Probes
+	}
+
 	// Add defaults that the ZK Operator should set itself, otherwise we will have problems with reconcile loops.
 	// Also it will default the spec.Probes object which cannot be set to null.
 	// TODO: Might be able to remove when the following is resolved and the dependency is upgraded:
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 325c6f9..6607cee 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -139,6 +139,13 @@
           url: https://github.com/apache/solr-operator/issues/544
         - name: GitHub PR
           url: https://github.com/apache/solr-operator/pull/545
+    - kind: added
+      description: Support custom Zookeeper probes
+      links:
+        - name: GitHub Issue
+          url: https://github.com/apache/solr-operator/issues/477
+        - name: GitHub PR
+          url: https://github.com/apache/solr-operator/pull/546
   artifacthub.io/images: |
     - name: solr-operator
       image: apache/solr-operator:v0.7.0-prerelease
diff --git a/helm/solr-operator/crds/crds.yaml b/helm/solr-operator/crds/crds.yaml
index ac4b910..b0292e5 100644
--- a/helm/solr-operator/crds/crds.yaml
+++ b/helm/solr-operator/crds/crds.yaml
@@ -10489,6 +10489,57 @@
                                 type: string
                             type: object
                         type: object
+                      probes:
+                        description: Probes specifies the timeout values for the Readiness
+                          and Liveness Probes for the zookeeper pods.
+                        properties:
+                          livenessProbe:
+                            properties:
+                              failureThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              initialDelaySeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              periodSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              successThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              timeoutSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                            type: object
+                          readinessProbe:
+                            properties:
+                              failureThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              initialDelaySeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              periodSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              successThreshold:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                              timeoutSeconds:
+                                format: int32
+                                minimum: 0
+                                type: integer
+                            type: object
+                        type: object
                       readOnlyAcl:
                         description: ZooKeeper ACL to use when connecting with ZK
                           for reading operations. This ACL should have READ permission
diff --git a/helm/solr/README.md b/helm/solr/README.md
index 66f9533..7c74142 100644
--- a/helm/solr/README.md
+++ b/helm/solr/README.md
@@ -186,6 +186,8 @@
 | zk.provided.zookeeperPodPolicy.securityContext | object |  | Security context for the entire ZooKeeper pod. More information can be found in the [Kubernetes docs](More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context). |
 | zk.provided.zookeeperPodPolicy.terminationGracePeriodSeconds | int | `30` | The amount of time that Kubernetes will give for a zookeeper pod instance to shutdown normally. |
 | zk.provided.zookeeperPodPolicy.imagePullSecrets | []object |  | List of image pull secrets to inject into the Zookeeper pod. |
+| zk.provided.probes.readinessProbe | object |  | Override the default readinessProbe for Zookeeper Pods. |
+| zk.provided.probes.livenessProbe | object |  | Override the default livenessProbe for Zookeeper Pods. |
 | zk.acl.secret | string |  | Name of a secret in the same namespace as the Solr cloud that stores the ZK admin ACL information |
 | zk.acl.usernameKey | string |  | Key in the Admin ACL Secret that stores the ACL username |
 | zk.acl.passwordKey | string |  | Key in the Admin ACL Secret that stores the ACL password |
diff --git a/helm/solr/templates/solrcloud.yaml b/helm/solr/templates/solrcloud.yaml
index 50f6588..294c298 100644
--- a/helm/solr/templates/solrcloud.yaml
+++ b/helm/solr/templates/solrcloud.yaml
@@ -221,6 +221,10 @@
       zookeeperPodPolicy:
         {{- include "solr.zk.zookeeperPodPolicy" . | nindent 8 }}
       {{- end }}
+      {{- if .Values.zk.provided.probes }}
+      probes:
+        {{- toYaml .Values.zk.provided.probes | nindent 8 }}
+      {{- end }}
       {{- if .Values.zk.acl }}
       acl:
         {{- toYaml .Values.zk.acl | nindent 8 }}
diff --git a/helm/solr/values.yaml b/helm/solr/values.yaml
index d61dad2..3b02912 100644
--- a/helm/solr/values.yaml
+++ b/helm/solr/values.yaml
@@ -177,6 +177,8 @@
       # resources: {}
       # # Set ZK service account individually instead of the global "serviceAccount.name"
       # serviceAccountName: ""
+    # Override the default Zookeeper probes
+    probes: {}
 
     # Storage defaults to the type of storage you use for Solr, which is ephemeral by default.
     # Explicitly set the storage type, only necessary when wishing to use an empty persistence or ephemeral object.