Ability to customize probes for the PrometheusExporter (#297)
diff --git a/controllers/controller_utils_test.go b/controllers/controller_utils_test.go
index ba48ec2..dace14f 100644
--- a/controllers/controller_utils_test.go
+++ b/controllers/controller_utils_test.go
@@ -23,7 +23,6 @@
"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"
- "reflect"
"strings"
"testing"
@@ -392,11 +391,11 @@
}
func testPodTolerations(t *testing.T, expectedTolerations []corev1.Toleration, foundTolerations []corev1.Toleration) {
- assert.True(t, reflect.DeepEqual(expectedTolerations, foundTolerations), "Expected tolerations and found tolerations don't match")
+ assert.EqualValues(t, expectedTolerations, foundTolerations, "Expected tolerations and found tolerations don't match")
}
-func testPodProbe(t *testing.T, expectedProbe *corev1.Probe, foundProbe *corev1.Probe) {
- assert.True(t, reflect.DeepEqual(expectedProbe, foundProbe), "Expected probe and found probe don't match")
+func testPodProbe(t *testing.T, expectedProbe *corev1.Probe, foundProbe *corev1.Probe, probeType string) {
+ assert.EqualValuesf(t, expectedProbe, foundProbe, "Incorrect default container %s probe", probeType)
}
func testMapsEqual(t *testing.T, mapName string, expected map[string]string, found map[string]string) {
diff --git a/controllers/solrcloud_controller_test.go b/controllers/solrcloud_controller_test.go
index 1e9a3e6..5a9ecdb 100644
--- a/controllers/solrcloud_controller_test.go
+++ b/controllers/solrcloud_controller_test.go
@@ -294,8 +294,9 @@
}
testMapsEqual(t, "pod annotations", util.MergeLabelsOrAnnotations(map[string]string{"solr.apache.org/solrXmlMd5": fmt.Sprintf("%x", md5.Sum([]byte(configMap.Data["solr.xml"])))}, testPodAnnotations), statefulSet.Spec.Template.Annotations)
testMapsEqual(t, "pod node selectors", testNodeSelectors, statefulSet.Spec.Template.Spec.NodeSelector)
- testPodProbe(t, testProbeLivenessNonDefaults, statefulSet.Spec.Template.Spec.Containers[0].LivenessProbe)
- testPodProbe(t, testProbeReadinessNonDefaults, statefulSet.Spec.Template.Spec.Containers[0].ReadinessProbe)
+ 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")
testPodTolerations(t, testTolerations, statefulSet.Spec.Template.Spec.Tolerations)
assert.EqualValues(t, testPriorityClass, statefulSet.Spec.Template.Spec.PriorityClassName, "Incorrect Priority class name for Pod Spec")
diff --git a/controllers/solrprometheusexporter_controller_test.go b/controllers/solrprometheusexporter_controller_test.go
index 271f3c2..be8f96e 100644
--- a/controllers/solrprometheusexporter_controller_test.go
+++ b/controllers/solrprometheusexporter_controller_test.go
@@ -29,6 +29,7 @@
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/intstr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/manager"
@@ -65,6 +66,8 @@
InitContainers: extraContainers1,
ImagePullSecrets: testAdditionalImagePullSecrets,
TerminationGracePeriodSeconds: &testTerminationGracePeriodSeconds,
+ LivenessProbe: testProbeLivenessNonDefaults,
+ ReadinessProbe: testProbeReadinessNonDefaults,
},
},
ExporterEntrypoint: "/test/entry-point",
@@ -132,6 +135,11 @@
assert.ElementsMatch(t, append(testAdditionalImagePullSecrets, corev1.LocalObjectReference{Name: testImagePullSecretName}), deployment.Spec.Template.Spec.ImagePullSecrets, "Incorrect imagePullSecrets")
assert.EqualValues(t, &testTerminationGracePeriodSeconds, deployment.Spec.Template.Spec.TerminationGracePeriodSeconds, "Incorrect terminationGracePeriodSeconds")
+ testPodProbe(t, testProbeLivenessNonDefaults, deployment.Spec.Template.Spec.Containers[0].LivenessProbe, "liveness")
+ testPodProbe(t, testProbeReadinessNonDefaults, deployment.Spec.Template.Spec.Containers[0].ReadinessProbe, "readiness")
+ assert.Nilf(t, deployment.Spec.Template.Spec.Containers[0].StartupProbe, "%s probe should be nil since it was not specified", "startup")
+
+ // Check the Service
service := expectService(t, g, requests, expectedMetricsRequest, metricsSKey, deployment.Spec.Template.Labels)
assert.Equal(t, "true", service.Annotations["prometheus.io/scrape"], "Metrics Service Prometheus scraping is not enabled.")
assert.EqualValues(t, "solr-metrics", service.Spec.Ports[0].Name, "Wrong port name on common Service")
@@ -151,6 +159,7 @@
Tolerations: testTolerationsPromExporter,
NodeSelector: testNodeSelectors,
PriorityClassName: testPriorityClass,
+ StartupProbe: testProbeStartup,
},
DeploymentOptions: &solr.DeploymentOptions{
Annotations: testDeploymentAnnotations,
@@ -228,6 +237,24 @@
assert.Equal(t, extraVolumes[0].Name, deployment.Spec.Template.Spec.Volumes[1].Name, "Additional Volume from podOptions not loaded into pod properly.")
assert.Equal(t, extraVolumes[0].Source, deployment.Spec.Template.Spec.Volumes[1].VolumeSource, "Additional Volume from podOptions not loaded into pod properly.")
+ testPodProbe(t, &corev1.Probe{
+ Handler: corev1.Handler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Scheme: corev1.URISchemeHTTP,
+ Path: "/metrics",
+ Port: intstr.FromInt(util.SolrMetricsPort),
+ },
+ },
+ InitialDelaySeconds: 20,
+ TimeoutSeconds: 1,
+ PeriodSeconds: 10,
+ SuccessThreshold: 1,
+ FailureThreshold: 3,
+ }, deployment.Spec.Template.Spec.Containers[0].LivenessProbe, "liveness")
+ testPodProbe(t, testProbeStartup, deployment.Spec.Template.Spec.Containers[0].StartupProbe, "startup")
+ assert.Nilf(t, deployment.Spec.Template.Spec.Containers[0].ReadinessProbe, "%s probe should be nil since it was not specified", "readiness")
+
+ // Check the Service
expectedServiceLabels := util.MergeLabelsOrAnnotations(instance.SharedLabelsWith(instance.Labels), map[string]string{"service-type": "metrics"})
expectedServiceAnnotations := map[string]string{"prometheus.io/path": "/metrics", "prometheus.io/port": "80", "prometheus.io/scheme": "http", "prometheus.io/scrape": "true"}
service := expectService(t, g, requests, expectedMetricsRequest, metricsSKey, expectedDeploymentLabels)
diff --git a/controllers/suite_test.go b/controllers/suite_test.go
index 4752253..77581c5 100644
--- a/controllers/suite_test.go
+++ b/controllers/suite_test.go
@@ -51,6 +51,14 @@
}
func TestMain(m *testing.M) {
+ // TODO: We can probably remove this once we upgrade our minimum supported version of Kubernetes
+ customApiServerFlags := []string{
+ "--feature-gates=StartupProbe=true",
+ }
+
+ apiServerFlags := append([]string(nil), envtest.DefaultKubeAPIServerFlags...)
+ apiServerFlags = append(apiServerFlags, customApiServerFlags...)
+
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
t := &envtest.Environment{
CRDDirectoryPaths: []string{
@@ -58,6 +66,7 @@
filepath.Join("..", "config", "dependencies"),
},
AttachControlPlaneOutput: false, // set to true to get more logging from the control plane
+ KubeAPIServerFlags: apiServerFlags,
}
solrv1beta1.AddToScheme(scheme.Scheme)
zkOp.AddToScheme(scheme.Scheme)
diff --git a/controllers/util/common.go b/controllers/util/common.go
index 7a3fd50..45074d4 100644
--- a/controllers/util/common.go
+++ b/controllers/util/common.go
@@ -132,6 +132,35 @@
return int32(ordinal) >= replicas
}
+// customizeProbe builds the probe logic used for pod liveness, readiness, startup checks
+func customizeProbe(initialProbe *corev1.Probe, customProbe corev1.Probe) *corev1.Probe {
+ if customProbe.InitialDelaySeconds != 0 {
+ initialProbe.InitialDelaySeconds = customProbe.InitialDelaySeconds
+ }
+
+ if customProbe.TimeoutSeconds != 0 {
+ initialProbe.TimeoutSeconds = customProbe.TimeoutSeconds
+ }
+
+ if customProbe.SuccessThreshold != 0 {
+ initialProbe.SuccessThreshold = customProbe.SuccessThreshold
+ }
+
+ if customProbe.FailureThreshold != 0 {
+ initialProbe.FailureThreshold = customProbe.FailureThreshold
+ }
+
+ if customProbe.PeriodSeconds != 0 {
+ initialProbe.PeriodSeconds = customProbe.PeriodSeconds
+ }
+
+ if customProbe.Handler.Exec != nil || customProbe.Handler.HTTPGet != nil || customProbe.Handler.TCPSocket != nil {
+ initialProbe.Handler = customProbe.Handler
+ }
+
+ return initialProbe
+}
+
// CopyConfigMapFields copies the owned fields from one ConfigMap to another
func CopyConfigMapFields(from, to *corev1.ConfigMap, logger logr.Logger) bool {
logger = logger.WithValues("kind", "configMap")
diff --git a/controllers/util/prometheus_exporter_util.go b/controllers/util/prometheus_exporter_util.go
index f7aef36..03636fb 100644
--- a/controllers/util/prometheus_exporter_util.go
+++ b/controllers/util/prometheus_exporter_util.go
@@ -303,6 +303,7 @@
deployment.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets
if nil != customPodOptions {
+ metricsContainer := &deployment.Spec.Template.Spec.Containers[0]
if customPodOptions.ServiceAccountName != "" {
deployment.Spec.Template.Spec.ServiceAccountName = customPodOptions.ServiceAccountName
}
@@ -311,12 +312,8 @@
deployment.Spec.Template.Spec.Affinity = customPodOptions.Affinity
}
- if customPodOptions.Resources.Requests != nil {
- deployment.Spec.Template.Spec.Containers[0].Resources.Requests = customPodOptions.Resources.Requests
- }
-
- if customPodOptions.Resources.Limits != nil {
- deployment.Spec.Template.Spec.Containers[0].Resources.Limits = customPodOptions.Resources.Limits
+ if customPodOptions.Resources.Limits != nil || customPodOptions.Resources.Requests != nil {
+ metricsContainer.Resources = customPodOptions.Resources
}
if customPodOptions.PodSecurityContext != nil {
@@ -338,6 +335,22 @@
if customPodOptions.TerminationGracePeriodSeconds != nil {
deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = customPodOptions.TerminationGracePeriodSeconds
}
+
+ if customPodOptions.ReadinessProbe != nil {
+ // Default Prometheus Exporter container does not contain a readinessProbe, so copy the livenessProbe
+ baseProbe := metricsContainer.LivenessProbe.DeepCopy()
+ metricsContainer.ReadinessProbe = customizeProbe(baseProbe, *customPodOptions.ReadinessProbe)
+ }
+
+ if customPodOptions.StartupProbe != nil {
+ // Default Prometheus Exporter container does not contain a startupProbe, so copy the livenessProbe
+ baseProbe := metricsContainer.LivenessProbe.DeepCopy()
+ metricsContainer.StartupProbe = customizeProbe(baseProbe, *customPodOptions.StartupProbe)
+ }
+
+ if customPodOptions.LivenessProbe != nil {
+ metricsContainer.LivenessProbe = customizeProbe(metricsContainer.LivenessProbe, *customPodOptions.LivenessProbe)
+ }
}
return deployment
diff --git a/controllers/util/solr_util.go b/controllers/util/solr_util.go
index 5c44f79..14276cb 100644
--- a/controllers/util/solr_util.go
+++ b/controllers/util/solr_util.go
@@ -63,24 +63,6 @@
DefaultStatefulSetPodManagementPolicy = appsv1.ParallelPodManagement
- DefaultLivenessProbeInitialDelaySeconds = 20
- DefaultLivenessProbeTimeoutSeconds = 1
- DefaultLivenessProbeSuccessThreshold = 1
- DefaultLivenessProbeFailureThreshold = 3
- DefaultLivenessProbePeriodSeconds = 10
-
- DefaultReadinessProbeInitialDelaySeconds = 15
- DefaultReadinessProbeTimeoutSeconds = 1
- DefaultReadinessProbeSuccessThreshold = 1
- DefaultReadinessProbeFailureThreshold = 3
- DefaultReadinessProbePeriodSeconds = 5
-
- DefaultStartupProbeInitialDelaySeconds = 20
- DefaultStartupProbeTimeoutSeconds = 30
- DefaultStartupProbeSuccessThreshold = 1
- DefaultStartupProbeFailureThreshold = 15
- DefaultStartupProbePeriodSeconds = 10
-
DefaultKeyStorePath = "/var/solr/tls"
Pkcs12KeystoreFile = "keystore.p12"
DefaultWritableKeyStorePath = "/var/solr/tls/pkcs12"
@@ -445,19 +427,19 @@
},
},
LivenessProbe: &corev1.Probe{
- InitialDelaySeconds: DefaultLivenessProbeInitialDelaySeconds,
- TimeoutSeconds: DefaultLivenessProbeTimeoutSeconds,
- SuccessThreshold: DefaultLivenessProbeSuccessThreshold,
- FailureThreshold: DefaultLivenessProbeFailureThreshold,
- PeriodSeconds: DefaultLivenessProbePeriodSeconds,
+ InitialDelaySeconds: 20,
+ TimeoutSeconds: 1,
+ SuccessThreshold: 1,
+ FailureThreshold: 3,
+ PeriodSeconds: 10,
Handler: defaultHandler,
},
ReadinessProbe: &corev1.Probe{
- InitialDelaySeconds: DefaultReadinessProbeInitialDelaySeconds,
- TimeoutSeconds: DefaultReadinessProbeTimeoutSeconds,
- SuccessThreshold: DefaultReadinessProbeSuccessThreshold,
- FailureThreshold: DefaultReadinessProbeFailureThreshold,
- PeriodSeconds: DefaultReadinessProbePeriodSeconds,
+ InitialDelaySeconds: 15,
+ TimeoutSeconds: 1,
+ SuccessThreshold: 1,
+ FailureThreshold: 3,
+ PeriodSeconds: 5,
Handler: defaultHandler,
},
VolumeMounts: volumeMounts,
@@ -552,6 +534,8 @@
stateful.Spec.Template.Spec.ImagePullSecrets = imagePullSecrets
if nil != customPodOptions {
+ solrContainer := &stateful.Spec.Template.Spec.Containers[0]
+
if customPodOptions.ServiceAccountName != "" {
stateful.Spec.Template.Spec.ServiceAccountName = customPodOptions.ServiceAccountName
}
@@ -561,7 +545,7 @@
}
if customPodOptions.Resources.Limits != nil || customPodOptions.Resources.Requests != nil {
- stateful.Spec.Template.Spec.Containers[0].Resources = customPodOptions.Resources
+ solrContainer.Resources = customPodOptions.Resources
}
if customPodOptions.PodSecurityContext != nil {
@@ -576,16 +560,21 @@
stateful.Spec.Template.Spec.NodeSelector = customPodOptions.NodeSelector
}
+ if customPodOptions.StartupProbe != nil {
+ // Default Solr container does not contain a startupProbe, so copy the livenessProbe
+ baseProbe := solrContainer.LivenessProbe.DeepCopy()
+ // Two options are different by default from the livenessProbe
+ baseProbe.TimeoutSeconds = 30
+ baseProbe.FailureThreshold = 15
+ solrContainer.StartupProbe = customizeProbe(baseProbe, *customPodOptions.StartupProbe)
+ }
+
if customPodOptions.LivenessProbe != nil {
- stateful.Spec.Template.Spec.Containers[0].LivenessProbe = fillProbe(*customPodOptions.LivenessProbe, DefaultLivenessProbeInitialDelaySeconds, DefaultLivenessProbeTimeoutSeconds, DefaultLivenessProbeSuccessThreshold, DefaultLivenessProbeFailureThreshold, DefaultLivenessProbePeriodSeconds, &defaultHandler)
+ solrContainer.LivenessProbe = customizeProbe(solrContainer.LivenessProbe, *customPodOptions.LivenessProbe)
}
if customPodOptions.ReadinessProbe != nil {
- stateful.Spec.Template.Spec.Containers[0].ReadinessProbe = fillProbe(*customPodOptions.ReadinessProbe, DefaultReadinessProbeInitialDelaySeconds, DefaultReadinessProbeTimeoutSeconds, DefaultReadinessProbeSuccessThreshold, DefaultReadinessProbeFailureThreshold, DefaultReadinessProbePeriodSeconds, &defaultHandler)
- }
-
- if customPodOptions.StartupProbe != nil {
- stateful.Spec.Template.Spec.Containers[0].StartupProbe = fillProbe(*customPodOptions.StartupProbe, DefaultStartupProbeInitialDelaySeconds, DefaultStartupProbeTimeoutSeconds, DefaultStartupProbeSuccessThreshold, DefaultStartupProbeFailureThreshold, DefaultStartupProbePeriodSeconds, &defaultHandler)
+ solrContainer.ReadinessProbe = customizeProbe(solrContainer.ReadinessProbe, *customPodOptions.ReadinessProbe)
}
if customPodOptions.PriorityClassName != "" {
@@ -686,44 +675,6 @@
return configMap
}
-// fillProbe builds the probe logic used for pod liveness, readiness, startup checks
-func fillProbe(customProbe corev1.Probe, defaultInitialDelaySeconds int32, defaultTimeoutSeconds int32, defaultSuccessThreshold int32, defaultFailureThreshold int32, defaultPeriodSeconds int32, defaultHandler *corev1.Handler) *corev1.Probe {
- probe := &corev1.Probe{
- InitialDelaySeconds: defaultInitialDelaySeconds,
- TimeoutSeconds: defaultTimeoutSeconds,
- SuccessThreshold: defaultSuccessThreshold,
- FailureThreshold: defaultFailureThreshold,
- PeriodSeconds: defaultPeriodSeconds,
- Handler: *defaultHandler,
- }
-
- if customProbe.InitialDelaySeconds != 0 {
- probe.InitialDelaySeconds = customProbe.InitialDelaySeconds
- }
-
- if customProbe.TimeoutSeconds != 0 {
- probe.TimeoutSeconds = customProbe.TimeoutSeconds
- }
-
- if customProbe.SuccessThreshold != 0 {
- probe.SuccessThreshold = customProbe.SuccessThreshold
- }
-
- if customProbe.FailureThreshold != 0 {
- probe.FailureThreshold = customProbe.FailureThreshold
- }
-
- if customProbe.PeriodSeconds != 0 {
- probe.PeriodSeconds = customProbe.PeriodSeconds
- }
-
- if customProbe.Handler.Exec != nil || customProbe.Handler.HTTPGet != nil || customProbe.Handler.TCPSocket != nil {
- probe.Handler = customProbe.Handler
- }
-
- return probe
-}
-
// GenerateCommonService returns a new corev1.Service pointer generated for the entire SolrCloud instance
// solrCloud: SolrCloud instance
func GenerateCommonService(solrCloud *solr.SolrCloud) *corev1.Service {
diff --git a/helm/solr-operator/Chart.yaml b/helm/solr-operator/Chart.yaml
index 9d61acd..6d791f6 100644
--- a/helm/solr-operator/Chart.yaml
+++ b/helm/solr-operator/Chart.yaml
@@ -98,6 +98,13 @@
url: https://github.com/apache/solr-operator/issues/294
- name: Github PR
url: https://github.com/apache/solr-operator/pull/295
+ - kind: added
+ description: Ability to customize probes for PrometheusExporter
+ links:
+ - name: Github Issue
+ url: https://github.com/apache/solr-operator/issues/282
+ - name: Github PR
+ url: https://github.com/apache/solr-operator/pull/297
artifacthub.io/images: |
- name: solr-operator
image: apache/solr-operator:v0.4.0-prerelease