diff --git a/api/v1beta1/common_types.go b/api/v1beta1/common_types.go index 838b9f5e..13984f4d 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 6560597e..9fb0fd96 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 8632db70..bdd7eab8 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 eventually terminate within the Pod''s termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. 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 + 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 e8d9f9bf..eb1a9e4d 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 eventually terminate within the Pod''s termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. 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 + type: object livenessProbe: description: Liveness probe parameters properties: diff --git a/controllers/controller_utils_test.go b/controllers/controller_utils_test.go index a7279d6b..db69faea 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 3a68cd44..696c769a 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 a86a5c1c..fc6e6eb2 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 5a9ecdbf..353876dc 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 03aab7e1..ff75e245 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 d4b3a8d2..5432133b 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 45074d44..1ef1ea67 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 99ee1d0f..f5a122a7 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 d686e0b8..53621232 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 6962c6a7..2d15575b 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 0d53866b..508bd40b 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 eventually terminate within the Pod''s termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. 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 + 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 eventually terminate within the Pod''s termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. 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 + type: object livenessProbe: description: Liveness probe parameters properties: diff --git a/helm/solr/README.md b/helm/solr/README.md index 4ca49ea0..10b15cca 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 b4d21ceb..0e02556a 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 0473421a..0ef711b0 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: []