You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ho...@apache.org on 2021/09/23 20:09:57 UTC

[solr-operator] branch main updated: Support custom Lifecycle for Solr & PrometheusExporter containers (#324)

This is an automated email from the ASF dual-hosted git repository.

houston 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 38d5ff3  Support custom Lifecycle for Solr & PrometheusExporter containers (#324)
38d5ff3 is described below

commit 38d5ff3ad85255fe4859cad9cf10179f9ca5129c
Author: Nick Vladiceanu <vl...@gmail.com>
AuthorDate: Thu Sep 23 22:09:51 2021 +0200

    Support custom Lifecycle for Solr & PrometheusExporter containers (#324)
    
    Co-authored-by: Nick Vladiceanu <ni...@olx.com>
    Co-authored-by: Houston Putman <ho...@apache.org>
---
 api/v1beta1/common_types.go                        |   4 +
 api/v1beta1/zz_generated.deepcopy.go               |   5 +
 config/crd/bases/solr.apache.org_solrclouds.yaml   | 134 +++++++++++
 .../solr.apache.org_solrprometheusexporters.yaml   | 134 +++++++++++
 controllers/controller_utils_test.go               |  19 +-
 .../solrcloud_controller_externaldns_test.go       |   3 +-
 controllers/solrcloud_controller_ingress_test.go   |   5 +-
 controllers/solrcloud_controller_test.go           |   8 +-
 controllers/solrcloud_controller_zk_test.go        |   3 +-
 .../solrprometheusexporter_controller_test.go      |  11 +-
 controllers/util/common.go                         |  13 +-
 controllers/util/prometheus_exporter_util.go       |   9 +-
 controllers/util/solr_util.go                      |  30 ++-
 helm/solr-operator/Chart.yaml                      |   9 +-
 helm/solr-operator/crds/crds.yaml                  | 268 +++++++++++++++++++++
 helm/solr/README.md                                |   1 +
 helm/solr/templates/_custom_option_helpers.tpl     |   4 +
 helm/solr/values.yaml                              |   3 +
 18 files changed, 630 insertions(+), 33 deletions(-)

diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go
index 838b9f5..13984f4 100644
--- a/api/v1beta1/common_types.go
+++ b/api/v1beta1/common_types.go
@@ -110,6 +110,10 @@ type PodOptions struct {
 	// +optional
 	PriorityClassName string `json:"priorityClassName,omitempty"`
 
+	// Lifecycle for the main container
+	// +optional
+	Lifecycle *corev1.Lifecycle `json:"lifecycle,omitempty"`
+
 	// Sidecar containers to run in the pod. These are in addition to the Solr Container
 	// +optional
 	SidecarContainers []corev1.Container `json:"sidecarContainers,omitempty"`
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 6560597..9fb0fd9 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -465,6 +465,11 @@ func (in *PodOptions) DeepCopyInto(out *PodOptions) {
 		*out = new(v1.Probe)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Lifecycle != nil {
+		in, out := &in.Lifecycle, &out.Lifecycle
+		*out = new(v1.Lifecycle)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.SidecarContainers != nil {
 		in, out := &in.SidecarContainers, &out.SidecarContainers
 		*out = make([]v1.Container, len(*in))
diff --git a/config/crd/bases/solr.apache.org_solrclouds.yaml b/config/crd/bases/solr.apache.org_solrclouds.yaml
index 8632db7..bdd7eab 100644
--- a/config/crd/bases/solr.apache.org_solrclouds.yaml
+++ b/config/crd/bases/solr.apache.org_solrclouds.yaml
@@ -1335,6 +1335,140 @@ spec:
                           type: string
                         description: Labels to be added for pods.
                         type: object
+                      lifecycle:
+                        description: Lifecycle for the main container
+                        properties:
+                          postStart:
+                            description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                          preStop:
+                            description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod''s termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will  [...]
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                        type: object
                       livenessProbe:
                         description: Liveness probe parameters
                         properties:
diff --git a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
index e8d9f9b..eb1a9e4 100644
--- a/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
+++ b/config/crd/bases/solr.apache.org_solrprometheusexporters.yaml
@@ -1277,6 +1277,140 @@ spec:
                           type: string
                         description: Labels to be added for pods.
                         type: object
+                      lifecycle:
+                        description: Lifecycle for the main container
+                        properties:
+                          postStart:
+                            description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                          preStop:
+                            description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod''s termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will  [...]
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                        type: object
                       livenessProbe:
                         description: Liveness probe parameters
                         properties:
diff --git a/controllers/controller_utils_test.go b/controllers/controller_utils_test.go
index a7279d6..db69fae 100644
--- a/controllers/controller_utils_test.go
+++ b/controllers/controller_utils_test.go
@@ -19,10 +19,11 @@ package controllers
 
 import (
 	"fmt"
+	"testing"
+
 	"github.com/apache/solr-operator/controllers/util"
 	zkv1beta1 "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"testing"
 
 	solr "github.com/apache/solr-operator/api/v1beta1"
 	"github.com/onsi/gomega"
@@ -276,6 +277,10 @@ func testPodProbe(t *testing.T, expectedProbe *corev1.Probe, foundProbe *corev1.
 	assert.EqualValuesf(t, expectedProbe, foundProbe, "Incorrect default container %s probe", probeType)
 }
 
+func testPodLifecycle(t *testing.T, expectedLifecycle *corev1.Lifecycle, foundLifecycle *corev1.Lifecycle) {
+	assert.EqualValuesf(t, expectedLifecycle, foundLifecycle, "Expected Lifecycle and found Lifecyle don't match")
+}
+
 func testMapsEqual(t *testing.T, mapName string, expected map[string]string, found map[string]string) {
 	assert.Equal(t, expected, found, "Expected and found %s are not the same", mapName)
 }
@@ -520,6 +525,18 @@ var (
 			},
 		},
 	}
+	testLifecycle = &corev1.Lifecycle{
+		PostStart: &corev1.Handler{
+			Exec: &corev1.ExecAction{
+				Command: []string{"/bin/sh", "-c", "echo Hello from the postStart handler"},
+			},
+		},
+		PreStop: &corev1.Handler{
+			Exec: &corev1.ExecAction{
+				Command: []string{"/bin/sh", "-c", "echo Hello from the preStop handler"},
+			},
+		},
+	}
 	testTolerations = []corev1.Toleration{
 		{
 			Effect:   "NoSchedule",
diff --git a/controllers/solrcloud_controller_externaldns_test.go b/controllers/solrcloud_controller_externaldns_test.go
index 3a68cd4..696c769 100644
--- a/controllers/solrcloud_controller_externaldns_test.go
+++ b/controllers/solrcloud_controller_externaldns_test.go
@@ -18,9 +18,10 @@
 package controllers
 
 import (
-	"k8s.io/apimachinery/pkg/types"
 	"testing"
 
+	"k8s.io/apimachinery/pkg/types"
+
 	"github.com/apache/solr-operator/controllers/util"
 	"github.com/stretchr/testify/assert"
 
diff --git a/controllers/solrcloud_controller_ingress_test.go b/controllers/solrcloud_controller_ingress_test.go
index a86a5c1..fc6e6eb 100644
--- a/controllers/solrcloud_controller_ingress_test.go
+++ b/controllers/solrcloud_controller_ingress_test.go
@@ -18,11 +18,12 @@
 package controllers
 
 import (
-	extv1 "k8s.io/api/extensions/v1beta1"
-	"k8s.io/apimachinery/pkg/types"
 	"strconv"
 	"testing"
 
+	extv1 "k8s.io/api/extensions/v1beta1"
+	"k8s.io/apimachinery/pkg/types"
+
 	"github.com/apache/solr-operator/controllers/util"
 	"github.com/stretchr/testify/assert"
 
diff --git a/controllers/solrcloud_controller_test.go b/controllers/solrcloud_controller_test.go
index 5a9ecdb..353876d 100644
--- a/controllers/solrcloud_controller_test.go
+++ b/controllers/solrcloud_controller_test.go
@@ -23,6 +23,9 @@ import (
 	"strconv"
 	"time"
 
+	"strings"
+	"testing"
+
 	solr "github.com/apache/solr-operator/api/v1beta1"
 	"github.com/apache/solr-operator/controllers/util"
 	"github.com/onsi/gomega"
@@ -36,8 +39,6 @@ import (
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"strings"
-	"testing"
 )
 
 var _ reconcile.Reconciler = &SolrCloudReconciler{}
@@ -206,6 +207,7 @@ func TestCustomKubeOptionsCloudReconcile(t *testing.T) {
 					LivenessProbe:                 testProbeLivenessNonDefaults,
 					ReadinessProbe:                testProbeReadinessNonDefaults,
 					StartupProbe:                  testProbeStartup,
+					Lifecycle:                     testLifecycle,
 					PriorityClassName:             testPriorityClass,
 					ImagePullSecrets:              testAdditionalImagePullSecrets,
 					TerminationGracePeriodSeconds: &testTerminationGracePeriodSeconds,
@@ -297,7 +299,7 @@ func TestCustomKubeOptionsCloudReconcile(t *testing.T) {
 	testPodProbe(t, testProbeLivenessNonDefaults, statefulSet.Spec.Template.Spec.Containers[0].LivenessProbe, "liveness")
 	testPodProbe(t, testProbeReadinessNonDefaults, statefulSet.Spec.Template.Spec.Containers[0].ReadinessProbe, "readiness")
 	testPodProbe(t, testProbeStartup, statefulSet.Spec.Template.Spec.Containers[0].StartupProbe, "startup")
-	assert.Equal(t, []string{"solr", "stop", "-p", "8983"}, statefulSet.Spec.Template.Spec.Containers[0].Lifecycle.PreStop.Exec.Command, "Incorrect pre-stop command")
+	testPodLifecycle(t, testLifecycle, statefulSet.Spec.Template.Spec.Containers[0].Lifecycle)
 	testPodTolerations(t, testTolerations, statefulSet.Spec.Template.Spec.Tolerations)
 	assert.EqualValues(t, testPriorityClass, statefulSet.Spec.Template.Spec.PriorityClassName, "Incorrect Priority class name for Pod Spec")
 	assert.ElementsMatch(t, append(testAdditionalImagePullSecrets, corev1.LocalObjectReference{Name: testImagePullSecretName}), statefulSet.Spec.Template.Spec.ImagePullSecrets, "Incorrect imagePullSecrets")
diff --git a/controllers/solrcloud_controller_zk_test.go b/controllers/solrcloud_controller_zk_test.go
index 03aab7e..ff75e24 100644
--- a/controllers/solrcloud_controller_zk_test.go
+++ b/controllers/solrcloud_controller_zk_test.go
@@ -19,13 +19,14 @@ package controllers
 
 import (
 	"fmt"
+	"testing"
+
 	"github.com/apache/solr-operator/controllers/util"
 	zkv1beta1 "github.com/pravega/zookeeper-operator/pkg/apis/zookeeper/v1beta1"
 	"github.com/pravega/zookeeper-operator/pkg/controller/zookeepercluster"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
-	"testing"
 
 	"github.com/stretchr/testify/assert"
 
diff --git a/controllers/solrprometheusexporter_controller_test.go b/controllers/solrprometheusexporter_controller_test.go
index d4b3a8d..5432133 100644
--- a/controllers/solrprometheusexporter_controller_test.go
+++ b/controllers/solrprometheusexporter_controller_test.go
@@ -20,6 +20,10 @@ package controllers
 import (
 	"crypto/md5"
 	"fmt"
+	"strings"
+	"testing"
+	"time"
+
 	solr "github.com/apache/solr-operator/api/v1beta1"
 	"github.com/apache/solr-operator/controllers/util"
 	"github.com/onsi/gomega"
@@ -34,9 +38,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
-	"strings"
-	"testing"
-	"time"
 )
 
 var _ reconcile.Reconciler = &SolrPrometheusExporterReconciler{}
@@ -68,6 +69,7 @@ func TestMetricsReconcileWithoutExporterConfig(t *testing.T) {
 					TerminationGracePeriodSeconds: &testTerminationGracePeriodSeconds,
 					LivenessProbe:                 testProbeLivenessNonDefaults,
 					ReadinessProbe:                testProbeReadinessNonDefaults,
+					Lifecycle:                     testLifecycle,
 				},
 			},
 			ExporterEntrypoint: "/test/entry-point",
@@ -137,6 +139,7 @@ func TestMetricsReconcileWithoutExporterConfig(t *testing.T) {
 
 	testPodProbe(t, testProbeLivenessNonDefaults, deployment.Spec.Template.Spec.Containers[0].LivenessProbe, "liveness")
 	testPodProbe(t, testProbeReadinessNonDefaults, deployment.Spec.Template.Spec.Containers[0].ReadinessProbe, "readiness")
+	testPodLifecycle(t, testLifecycle, deployment.Spec.Template.Spec.Containers[0].Lifecycle)
 	assert.Nilf(t, deployment.Spec.Template.Spec.Containers[0].StartupProbe, "%s probe should be nil since it was not specified", "startup")
 
 	// Check the Service
@@ -160,6 +163,7 @@ func TestMetricsReconcileWithExporterConfig(t *testing.T) {
 					NodeSelector:      testNodeSelectors,
 					PriorityClassName: testPriorityClass,
 					StartupProbe:      testProbeStartup,
+					Lifecycle:         testLifecycle,
 				},
 				DeploymentOptions: &solr.DeploymentOptions{
 					Annotations: testDeploymentAnnotations,
@@ -252,6 +256,7 @@ func TestMetricsReconcileWithExporterConfig(t *testing.T) {
 		FailureThreshold:    3,
 	}, deployment.Spec.Template.Spec.Containers[0].LivenessProbe, "liveness")
 	testPodProbe(t, testProbeStartup, deployment.Spec.Template.Spec.Containers[0].StartupProbe, "startup")
+	testPodLifecycle(t, testLifecycle, deployment.Spec.Template.Spec.Containers[0].Lifecycle)
 	assert.Nilf(t, deployment.Spec.Template.Spec.Containers[0].ReadinessProbe, "%s probe should be nil since it was not specified", "readiness")
 
 	// Check the Service
diff --git a/controllers/util/common.go b/controllers/util/common.go
index 45074d4..1ef1ea6 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -18,6 +18,10 @@
 package util
 
 import (
+	"reflect"
+	"strconv"
+	"strings"
+
 	"github.com/go-logr/logr"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -25,10 +29,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/utils/pointer"
-	"reflect"
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
-	"strconv"
-	"strings"
 )
 
 // CopyLabelsAndAnnotations copies the labels and annotations from one object to another.
@@ -559,6 +560,12 @@ func CopyPodContainers(fromPtr, toPtr *[]corev1.Container, basePath string, logg
 				to[i].StartupProbe = from[i].StartupProbe
 			}
 
+			if !DeepEqualWithNils(to[i].Lifecycle, from[i].Lifecycle) {
+				requireUpdate = true
+				logger.Info("Update required because field changed", "field", containerBasePath+"Lifecycle", "from", to[i].Lifecycle, "to", from[i].Lifecycle)
+				to[i].Lifecycle = from[i].Lifecycle
+			}
+
 			if from[i].TerminationMessagePath != "" && !DeepEqualWithNils(to[i].TerminationMessagePath, from[i].TerminationMessagePath) {
 				requireUpdate = true
 				logger.Info("Update required because field changed", "field", containerBasePath+"TerminationMessagePath", "from", to[i].TerminationMessagePath, "to", from[i].TerminationMessagePath)
diff --git a/controllers/util/prometheus_exporter_util.go b/controllers/util/prometheus_exporter_util.go
index 99ee1d0..f5a122a 100644
--- a/controllers/util/prometheus_exporter_util.go
+++ b/controllers/util/prometheus_exporter_util.go
@@ -18,14 +18,15 @@
 package util
 
 import (
+	"strconv"
+	"strings"
+
 	solr "github.com/apache/solr-operator/api/v1beta1"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	netv1 "k8s.io/api/networking/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
-	"strconv"
-	"strings"
 )
 
 const (
@@ -322,6 +323,10 @@ func GenerateSolrPrometheusExporterDeployment(solrPrometheusExporter *solr.SolrP
 		if customPodOptions.LivenessProbe != nil {
 			metricsContainer.LivenessProbe = customizeProbe(metricsContainer.LivenessProbe, *customPodOptions.LivenessProbe)
 		}
+
+		if customPodOptions.Lifecycle != nil {
+			metricsContainer.Lifecycle = customPodOptions.Lifecycle
+		}
 	}
 
 	// Enrich the deployment definition to allow the exporter to make requests to TLS enabled Solr pods
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index d686e0b..5362123 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -22,18 +22,19 @@ import (
 	b64 "encoding/base64"
 	"encoding/json"
 	"fmt"
-	solr "github.com/apache/solr-operator/api/v1beta1"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	netv1 "k8s.io/api/networking/v1beta1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/intstr"
 	"math/rand"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	solr "github.com/apache/solr-operator/api/v1beta1"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	netv1 "k8s.io/api/networking/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
 )
 
 const (
@@ -327,6 +328,13 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl
 		}
 	}
 
+	// Default preStop hook
+	preStop := &corev1.Handler{
+		Exec: &corev1.ExecAction{
+			Command: []string{"solr", "stop", "-p", strconv.Itoa(solrPodPort)},
+		},
+	}
+
 	// Add Custom EnvironmentVariables to the solr container
 	if nil != customPodOptions {
 		envVars = append(envVars, customPodOptions.EnvVariables...)
@@ -422,11 +430,7 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl
 			Env:          envVars,
 			Lifecycle: &corev1.Lifecycle{
 				PostStart: postStart,
-				PreStop: &corev1.Handler{
-					Exec: &corev1.ExecAction{
-						Command: []string{"solr", "stop", "-p", strconv.Itoa(solrPodPort)},
-					},
-				},
+				PreStop:   preStop,
 			},
 		},
 	}
@@ -522,6 +526,10 @@ func GenerateStatefulSet(solrCloud *solr.SolrCloud, solrCloudStatus *solr.SolrCl
 			stateful.Spec.Template.Spec.SecurityContext = customPodOptions.PodSecurityContext
 		}
 
+		if customPodOptions.Lifecycle != nil {
+			solrContainer.Lifecycle = customPodOptions.Lifecycle
+		}
+
 		if customPodOptions.Tolerations != nil {
 			stateful.Spec.Template.Spec.Tolerations = customPodOptions.Tolerations
 		}
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 6962c6a..2d15575 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -55,15 +55,12 @@ annotations:
   # Allowed syntax is described at: https://artifacthub.io/docs/topics/annotations/helm/#example
   artifacthub.io/changes: |
     - kind: added
-      description: Addition 1
+      description: Customize the Lifecycle for Solr and PrometheusExporter containers
       links:
         - name: Github Issue
-          url: https://github.com/issue-url
-    - kind: changed
-      description: Change 2
-      links:
+          url: https://github.com/apache/solr-operator/issues/322
         - name: Github PR
-          url: https://github.com/pr-url
+          url: https://github.com/apache/solr-operator/pull/324
   artifacthub.io/images: |
     - name: solr-operator
       image: apache/solr-operator:v0.5.0-prerelease
diff --git a/helm/solr-operator/crds/crds.yaml b/helm/solr-operator/crds/crds.yaml
index 0d53866..508bd40 100644
--- a/helm/solr-operator/crds/crds.yaml
+++ b/helm/solr-operator/crds/crds.yaml
@@ -2462,6 +2462,140 @@ spec:
                           type: string
                         description: Labels to be added for pods.
                         type: object
+                      lifecycle:
+                        description: Lifecycle for the main container
+                        properties:
+                          postStart:
+                            description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                          preStop:
+                            description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod''s termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will  [...]
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                        type: object
                       livenessProbe:
                         description: Liveness probe parameters
                         properties:
@@ -8093,6 +8227,140 @@ spec:
                           type: string
                         description: Labels to be added for pods.
                         type: object
+                      lifecycle:
+                        description: Lifecycle for the main container
+                        properties:
+                          postStart:
+                            description: 'PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks'
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                          preStop:
+                            description: 'PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod''s termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will  [...]
+                            properties:
+                              exec:
+                                description: One and only one of the following should be specified. Exec specifies the action to take.
+                                properties:
+                                  command:
+                                    description: Command is the command line to execute inside the container, the working directory for the command  is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.
+                                    items:
+                                      type: string
+                                    type: array
+                                type: object
+                              httpGet:
+                                description: HTTPGet specifies the http request to perform.
+                                properties:
+                                  host:
+                                    description: Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+                                    type: string
+                                  httpHeaders:
+                                    description: Custom headers to set in the request. HTTP allows repeated headers.
+                                    items:
+                                      description: HTTPHeader describes a custom header to be used in HTTP probes
+                                      properties:
+                                        name:
+                                          description: The header field name
+                                          type: string
+                                        value:
+                                          description: The header field value
+                                          type: string
+                                      required:
+                                      - name
+                                      - value
+                                      type: object
+                                    type: array
+                                  path:
+                                    description: Path to access on the HTTP server.
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                  scheme:
+                                    description: Scheme to use for connecting to the host. Defaults to HTTP.
+                                    type: string
+                                required:
+                                - port
+                                type: object
+                              tcpSocket:
+                                description: 'TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported TODO: implement a realistic TCP lifecycle hook'
+                                properties:
+                                  host:
+                                    description: 'Optional: Host name to connect to, defaults to the pod IP.'
+                                    type: string
+                                  port:
+                                    anyOf:
+                                    - type: integer
+                                    - type: string
+                                    description: Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+                                    x-kubernetes-int-or-string: true
+                                required:
+                                - port
+                                type: object
+                            type: object
+                        type: object
                       livenessProbe:
                         description: Liveness probe parameters
                         properties:
diff --git a/helm/solr/README.md b/helm/solr/README.md
index 4ca49ea..10b15cc 100644
--- a/helm/solr/README.md
+++ b/helm/solr/README.md
@@ -255,6 +255,7 @@ Configure Solr to use a separate TLS certificate for client auth.
 | podOptions.livenessProbe | object |  | Custom liveness probe for the Solr container |
 | podOptions.readinessProbe | object |  | Custom readiness probe for the Solr container |
 | podOptions.startupProbe | object |  | Custom startup probe for the Solr container |
+| podOptions.lifecycle | object |  | Custom lifecycle for the Solr container |
 | podOptions.imagePullSecrets | []object |  | List of image pull secrets to inject into the Solr pod, in addition to `global.imagePullSecrets` |
 | podOptions.volumes | []object |  | List of additional volumes to attach to the Solr pod, and optionally how to mount them to the Solr container |
 | statefulSetOptions.annotations | map[string]string |  | Custom annotations to add to the Solr statefulSet |
diff --git a/helm/solr/templates/_custom_option_helpers.tpl b/helm/solr/templates/_custom_option_helpers.tpl
index b4d21ce..0e02556 100644
--- a/helm/solr/templates/_custom_option_helpers.tpl
+++ b/helm/solr/templates/_custom_option_helpers.tpl
@@ -82,6 +82,10 @@ readinessProbe:
 startupProbe:
   {{- toYaml .Values.podOptions.startupProbe | nindent 2 }}
 {{ end }}
+{{- if .Values.podOptions.lifecycle -}}
+lifecycle:
+  {{- toYaml .Values.podOptions.lifecycle | nindent 2 }}
+{{ end }}
 {{- if .Values.podOptions.sidecarContainers -}}
 sidecarContainers:
   {{- toYaml .Values.podOptions.sidecarContainers | nindent 2 }}
diff --git a/helm/solr/values.yaml b/helm/solr/values.yaml
index 0473421..0ef711b 100644
--- a/helm/solr/values.yaml
+++ b/helm/solr/values.yaml
@@ -255,6 +255,9 @@ podOptions:
   livenessProbe: {}
   readinessProbe: {}
   startupProbe: {}
+  
+  # Lifecycle for the Solr container
+  lifecycle: {}
 
   imagePullSecrets: []