From 2c9f63092682677d2ba2e46c67b5228f013d92dd Mon Sep 17 00:00:00 2001 From: Maciej Olesinski Date: Mon, 15 Apr 2019 13:34:36 +0200 Subject: [PATCH] Add Readiness and Liveness probes --- .../crds/jenkinsio_v1alpha1_jenkins_cr.yaml | 20 ++++++++++++ pkg/apis/jenkinsio/v1alpha1/jenkins_types.go | 2 ++ .../v1alpha1/zz_generated.deepcopy.go | 10 ++++++ .../jenkins/configuration/base/reconcile.go | 12 +++++++ .../configuration/base/resources/pod.go | 28 ++-------------- pkg/controller/jenkins/jenkins_controller.go | 32 ++++++++++++++++++- test/e2e/configuration_test.go | 10 ++++++ test/e2e/jenkins.go | 24 ++++++++++++++ 8 files changed, 111 insertions(+), 27 deletions(-) diff --git a/deploy/crds/jenkinsio_v1alpha1_jenkins_cr.yaml b/deploy/crds/jenkinsio_v1alpha1_jenkins_cr.yaml index bf25fa4c..d6c0d455 100644 --- a/deploy/crds/jenkinsio_v1alpha1_jenkins_cr.yaml +++ b/deploy/crds/jenkinsio_v1alpha1_jenkins_cr.yaml @@ -5,6 +5,26 @@ metadata: spec: master: image: jenkins/jenkins:lts + readinessProbe: + httpGet: + path: /login + port: 8080 + scheme: HTTP + failureThreshold: 12 + initialDelaySeconds: 20 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /login + port: 8080 + scheme: HTTP + initialDelaySeconds: 20 + failureThreshold: 12 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 seedJobs: - id: jenkins-operator targets: "cicd/jobs/*.jenkins" diff --git a/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go b/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go index 78b35633..d2e2faf7 100644 --- a/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go +++ b/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go @@ -27,6 +27,8 @@ type JenkinsMaster struct { Annotations map[string]string `json:"masterAnnotations,omitempty"` Resources corev1.ResourceRequirements `json:"resources,omitempty"` Env []corev1.EnvVar `json:"env,omitempty"` + LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"` + ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` // OperatorPlugins contains plugins required by operator OperatorPlugins map[string][]string `json:"basePlugins,omitempty"` // Plugins contains plugins required by user diff --git a/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go index c53b799c..f7ccff13 100644 --- a/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go @@ -135,6 +135,16 @@ func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } if in.OperatorPlugins != nil { in, out := &in.OperatorPlugins, &out.OperatorPlugins *out = make(map[string][]string, len(*in)) diff --git a/pkg/controller/jenkins/configuration/base/reconcile.go b/pkg/controller/jenkins/configuration/base/reconcile.go index a65f20e6..c7151c25 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -407,6 +407,18 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa return true } + if !reflect.DeepEqual(r.jenkins.Spec.Master.ReadinessProbe, currentJenkinsMasterPod.Spec.Containers[0].ReadinessProbe) { + r.logger.Info(fmt.Sprintf("Jenkins pod readinessProbe have changed, actual '%+v' required '%+v' - recreating pod", + currentJenkinsMasterPod.Spec.Containers[0].ReadinessProbe, r.jenkins.Spec.Master.ReadinessProbe)) + return true + } + + if !reflect.DeepEqual(r.jenkins.Spec.Master.LivenessProbe, currentJenkinsMasterPod.Spec.Containers[0].LivenessProbe) { + r.logger.Info(fmt.Sprintf("Jenkins pod livenessProbe have changed, actual '%+v' required '%+v' - recreating pod", + currentJenkinsMasterPod.Spec.Containers[0].LivenessProbe, r.jenkins.Spec.Master.LivenessProbe)) + return true + } + if !reflect.DeepEqual(r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) { r.logger.Info(fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v' - recreating pod", currentJenkinsMasterPod.Spec.NodeSelector, r.jenkins.Spec.Master.NodeSelector)) diff --git a/pkg/controller/jenkins/configuration/base/resources/pod.go b/pkg/controller/jenkins/configuration/base/resources/pod.go index e2963f0f..2319272c 100644 --- a/pkg/controller/jenkins/configuration/base/resources/pod.go +++ b/pkg/controller/jenkins/configuration/base/resources/pod.go @@ -8,7 +8,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" ) const ( @@ -75,9 +74,6 @@ func GetJenkinsMasterPodBaseEnvs() []corev1.EnvVar { // NewJenkinsMasterPod builds Jenkins Master Kubernetes Pod resource func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins) *corev1.Pod { - initialDelaySeconds := int32(30) - timeoutSeconds := int32(5) - failureThreshold := int32(12) runAsUser := jenkinsUserUID objectMeta.Annotations = jenkins.Spec.Master.Annotations @@ -104,28 +100,8 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins "bash", fmt.Sprintf("%s/%s", jenkinsScriptsVolumePath, initScriptName), }, - LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/login", - Port: intstr.FromString(httpPortName), - Scheme: corev1.URISchemeHTTP, - }, - }, - InitialDelaySeconds: initialDelaySeconds, - TimeoutSeconds: timeoutSeconds, - FailureThreshold: failureThreshold, - }, - ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ - HTTPGet: &corev1.HTTPGetAction{ - Path: "/login", - Port: intstr.FromString(httpPortName), - Scheme: corev1.URISchemeHTTP, - }, - }, - InitialDelaySeconds: initialDelaySeconds, - }, + LivenessProbe: jenkins.Spec.Master.LivenessProbe, + ReadinessProbe: jenkins.Spec.Master.ReadinessProbe, Ports: []corev1.ContainerPort{ { Name: httpPortName, diff --git a/pkg/controller/jenkins/jenkins_controller.go b/pkg/controller/jenkins/jenkins_controller.go index c387e622..0ec03f2f 100644 --- a/pkg/controller/jenkins/jenkins_controller.go +++ b/pkg/controller/jenkins/jenkins_controller.go @@ -3,6 +3,7 @@ package jenkins import ( "context" "fmt" + "k8s.io/apimachinery/pkg/util/intstr" "reflect" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" @@ -136,7 +137,6 @@ func (r *ReconcileJenkins) reconcile(request reconcile.Request, logger logr.Logg // Error reading the object - requeue the request. return reconcile.Result{}, errors.WithStack(err) } - err = r.setDefaults(jenkins, logger) if err != nil { return reconcile.Result{}, err @@ -227,6 +227,36 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha1.Jenkins, logger logr.Lo changed = true jenkins.Spec.Master.ImagePullPolicy = corev1.PullAlways } + if jenkins.Spec.Master.ReadinessProbe == nil { + logger.Info("Setting default Jenkins readinessProbe") + changed = true + jenkins.Spec.Master.ReadinessProbe = &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(30), + } + } + if jenkins.Spec.Master.LivenessProbe == nil { + logger.Info("Setting default Jenkins livenessProbe") + changed = true + jenkins.Spec.Master.LivenessProbe = &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(30), + TimeoutSeconds: int32(5), + FailureThreshold: int32(12), + } + } if len(jenkins.Spec.Master.OperatorPlugins) == 0 { logger.Info("Setting default operator plugins") changed = true diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index c32676e4..da7c4a11 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -25,6 +25,7 @@ func TestConfiguration(t *testing.T) { // Deletes test namespace defer ctx.Cleanup() + t.Logf("BASE") jenkinsCRName := "e2e" numberOfExecutors := 6 systemMessage := "Configuration as Code integration works!!!" @@ -42,6 +43,7 @@ func TestConfiguration(t *testing.T) { } // base + createUserConfigurationSecret(t, jenkinsCRName, namespace, systemMessageEnvName, systemMessage) createUserConfigurationConfigMap(t, jenkinsCRName, namespace, numberOfExecutors, fmt.Sprintf("${%s}", systemMessageEnvName)) jenkins := createJenkinsCR(t, jenkinsCRName, namespace, &[]v1alpha1.SeedJob{mySeedJob.SeedJob}) @@ -151,6 +153,14 @@ func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha1.Jenkins) { t.Fatalf("Invalid jenkins pod node selector expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) } + if !reflect.DeepEqual(jenkinsPod.Spec.Containers[0].ReadinessProbe, jenkins.Spec.Master.ReadinessProbe) { + t.Fatalf("Invalid jenkins pod readinessProbe. Expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) + } + + if !reflect.DeepEqual(jenkinsPod.Spec.Containers[0].LivenessProbe, jenkins.Spec.Master.LivenessProbe) { + t.Fatalf("Invalid jenkins pod livenessProbe. Expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) + } + requiredEnvs := resources.GetJenkinsMasterPodBaseEnvs() requiredEnvs = append(requiredEnvs, jenkins.Spec.Master.Env...) if !reflect.DeepEqual(jenkinsPod.Spec.Containers[0].Env, requiredEnvs) { diff --git a/test/e2e/jenkins.go b/test/e2e/jenkins.go index 66ae8574..c76a8795 100644 --- a/test/e2e/jenkins.go +++ b/test/e2e/jenkins.go @@ -2,6 +2,7 @@ package e2e import ( "context" + "k8s.io/apimachinery/pkg/util/intstr" "testing" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkinsio/v1alpha1" @@ -10,6 +11,7 @@ import ( framework "github.com/operator-framework/operator-sdk/pkg/test" "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" @@ -84,6 +86,28 @@ func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha1.S Value: "test_env_value", }, }, + ReadinessProbe: &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(50), + }, + LivenessProbe: &corev1.Probe{ + Handler: corev1.Handler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/login", + Port: intstr.FromString("http"), + Scheme: corev1.URISchemeHTTP, + }, + }, + InitialDelaySeconds: int32(40), + TimeoutSeconds: int32(8), + FailureThreshold: int32(15), + }, }, SeedJobs: seedJobs, },