Add Readiness and Liveness probes

This commit is contained in:
Maciej Olesinski 2019-04-15 13:34:36 +02:00
parent 7f106df949
commit 2c9f630926
8 changed files with 111 additions and 27 deletions

View File

@ -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"

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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,

View File

@ -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

View File

@ -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) {

View File

@ -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,
},