Move Jenkins container properties to spec.master.containers list

This commit is contained in:
Tomasz Sęk 2019-06-11 19:53:02 +02:00
parent e1aba3ed9f
commit 25469b1174
No known key found for this signature in database
GPG Key ID: DC356D23F6A644D0
11 changed files with 177 additions and 135 deletions

View File

@ -4,26 +4,37 @@ metadata:
name: example
spec:
master:
readinessProbe:
httpGet:
path: /login
port: 8080
scheme: HTTP
failureThreshold: 10
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
livenessProbe:
httpGet:
path: /login
port: 8080
scheme: HTTP
initialDelaySeconds: 35
failureThreshold: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
containers:
- name: jenkins-master
image: jenkins/jenkins:lts
imagePullPolicy: Always
livenessProbe:
failureThreshold: 12
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 80
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 3
httpGet:
path: /login
port: http
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 1500m
memory: 3Gi
requests:
cpu: "1"
memory: 600Mi
seedJobs:
- id: jenkins-operator
targets: "cicd/jobs/*.jenkins"

View File

@ -47,8 +47,6 @@ type Plugin struct {
// JenkinsMaster defines the Jenkins master pod attributes and plugins,
// every single change requires Jenkins master pod restart
type JenkinsMaster struct {
Container //TODO move to containers
// pod properties
Annotations map[string]string `json:"masterAnnotations,omitempty"`
NodeSelector map[string]string `json:"nodeSelector,omitempty"`

View File

@ -170,8 +170,8 @@ func (in *JenkinsList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *JenkinsMaster) DeepCopyInto(out *JenkinsMaster) {
*out = *in
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val

View File

@ -438,8 +438,8 @@ func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMa
return true
}
if (len(r.jenkins.Spec.Master.Containers) + 1) != len(currentJenkinsMasterPod.Spec.Containers) {
r.logger.Info(fmt.Sprintf("Jenkins amount of containers has changed to '%+v', recreating pod", len(r.jenkins.Spec.Master.Containers)+1))
if len(r.jenkins.Spec.Master.Containers) != len(currentJenkinsMasterPod.Spec.Containers) {
r.logger.Info(fmt.Sprintf("Jenkins amount of containers has changed to '%+v', recreating pod", len(r.jenkins.Spec.Master.Containers)))
return true
}

View File

@ -194,19 +194,20 @@ func GetJenkinsMasterContainerBaseVolumeMounts() []corev1.VolumeMount {
// NewJenkinsMasterContainer returns Jenkins master Kubernetes container
func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
jenkinsContainer := jenkins.Spec.Master.Containers[0]
envs := GetJenkinsMasterPodBaseEnvs()
envs = append(envs, jenkins.Spec.Master.Env...)
envs = append(envs, jenkinsContainer.Env...)
return corev1.Container{
Name: JenkinsMasterContainerName,
Image: jenkins.Spec.Master.Image,
ImagePullPolicy: jenkins.Spec.Master.ImagePullPolicy,
Image: jenkinsContainer.Image,
ImagePullPolicy: jenkinsContainer.ImagePullPolicy,
Command: []string{
"bash",
fmt.Sprintf("%s/%s", jenkinsScriptsVolumePath, initScriptName),
},
LivenessProbe: jenkins.Spec.Master.LivenessProbe,
ReadinessProbe: jenkins.Spec.Master.ReadinessProbe,
LivenessProbe: jenkinsContainer.LivenessProbe,
ReadinessProbe: jenkinsContainer.ReadinessProbe,
Ports: []corev1.ContainerPort{
{
Name: httpPortName,
@ -220,8 +221,8 @@ func NewJenkinsMasterContainer(jenkins *v1alpha2.Jenkins) corev1.Container {
},
},
Env: envs,
Resources: jenkins.Spec.Master.Resources,
VolumeMounts: append(GetJenkinsMasterContainerBaseVolumeMounts(), jenkins.Spec.Master.VolumeMounts...),
Resources: jenkinsContainer.Resources,
VolumeMounts: append(GetJenkinsMasterContainerBaseVolumeMounts(), jenkinsContainer.VolumeMounts...),
}
}
@ -249,7 +250,7 @@ func ConvertJenkinsContainerToKubernetesContainer(container v1alpha2.Container)
func newContainers(jenkins *v1alpha2.Jenkins) (containers []corev1.Container) {
containers = append(containers, NewJenkinsMasterContainer(jenkins))
for _, container := range jenkins.Spec.Master.Containers {
for _, container := range jenkins.Spec.Master.Containers[1:] {
containers = append(containers, ConvertJenkinsContainerToKubernetesContainer(container))
}

View File

@ -32,10 +32,6 @@ func (r *ReconcileJenkinsBaseConfiguration) Validate(jenkins *v1alpha2.Jenkins)
return false, nil
}
if !r.validateContainer(jenkins.Spec.Master.Container) {
return false, nil
}
for _, container := range jenkins.Spec.Master.Containers {
if !r.validateContainer(container) {
return false, nil
@ -205,7 +201,7 @@ func (r *ReconcileJenkinsBaseConfiguration) validateJenkinsMasterPodEnvs() bool
}
valid := true
for _, userEnv := range r.jenkins.Spec.Master.Env {
for _, userEnv := range r.jenkins.Spec.Master.Containers[0].Env {
if _, overriding := baseEnvNames[userEnv.Name]; overriding {
r.logger.V(log.VWarn).Info(fmt.Sprintf("Jenkins Master pod env '%s' cannot be overridden", userEnv.Name))
valid = false

View File

@ -136,11 +136,13 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
jenkins := v1alpha2.Jenkins{
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Container: v1alpha2.Container{
Env: []v1.EnvVar{
{
Name: "SOME_VALUE",
Value: "",
Containers: []v1alpha2.Container{
{
Env: []v1.EnvVar{
{
Name: "SOME_VALUE",
Value: "",
},
},
},
},
@ -156,11 +158,13 @@ func TestValidateJenkinsMasterPodEnvs(t *testing.T) {
jenkins := v1alpha2.Jenkins{
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Container: v1alpha2.Container{
Env: []v1.EnvVar{
{
Name: "JENKINS_HOME",
Value: "",
Containers: []v1alpha2.Container{
{
Env: []v1.EnvVar{
{
Name: "JENKINS_HOME",
Value: "",
},
},
},
},
@ -220,7 +224,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
}
baseReconcileLoop := New(nil, nil, logf.ZapLogger(false),
&jenkins, false, false)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Container)
got := baseReconcileLoop.validateContainerVolumeMounts(v1alpha2.Container{})
assert.Equal(t, true, got)
})
t.Run("one extra volume", func(t *testing.T) {
@ -232,11 +236,13 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
Name: "example",
},
},
Container: v1alpha2.Container{
VolumeMounts: []v1.VolumeMount{
{
Name: "example",
MountPath: "/test",
Containers: []v1alpha2.Container{
{
VolumeMounts: []v1.VolumeMount{
{
Name: "example",
MountPath: "/test",
},
},
},
},
@ -245,7 +251,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
}
baseReconcileLoop := New(nil, nil, logf.ZapLogger(false),
&jenkins, false, false)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Container)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Equal(t, true, got)
})
t.Run("empty mountPath", func(t *testing.T) {
@ -257,11 +263,13 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
Name: "example",
},
},
Container: v1alpha2.Container{
VolumeMounts: []v1.VolumeMount{
{
Name: "example",
MountPath: "", // empty
Containers: []v1alpha2.Container{
{
VolumeMounts: []v1.VolumeMount{
{
Name: "example",
MountPath: "", // empty
},
},
},
},
@ -270,18 +278,20 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
}
baseReconcileLoop := New(nil, nil, logf.ZapLogger(false),
&jenkins, false, false)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Container)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Equal(t, false, got)
})
t.Run("missing volume", func(t *testing.T) {
jenkins := v1alpha2.Jenkins{
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Container: v1alpha2.Container{
VolumeMounts: []v1.VolumeMount{
{
Name: "missing-volume",
MountPath: "/test",
Containers: []v1alpha2.Container{
{
VolumeMounts: []v1.VolumeMount{
{
Name: "missing-volume",
MountPath: "/test",
},
},
},
},
@ -290,7 +300,7 @@ func TestValidateContainerVolumeMounts(t *testing.T) {
}
baseReconcileLoop := New(nil, nil, logf.ZapLogger(false),
&jenkins, false, false)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Container)
got := baseReconcileLoop.validateContainerVolumeMounts(jenkins.Spec.Master.Containers[0])
assert.Equal(t, false, got)
})
}

View File

@ -7,6 +7,7 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
"github.com/bndr/gojenkins"
"github.com/golang/mock/gomock"
@ -120,16 +121,19 @@ func jenkinsCustomResource() *v1alpha2.Jenkins {
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Annotations: map[string]string{"test": "label"},
Container: v1alpha2.Container{
Image: "jenkins/jenkins",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("300m"),
corev1.ResourceMemory: resource.MustParse("500Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
corev1.ResourceMemory: resource.MustParse("2Gi"),
Containers: []v1alpha2.Container{
{
Name: resources.JenkinsMasterContainerName,
Image: "jenkins/jenkins",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("300m"),
corev1.ResourceMemory: resource.MustParse("500Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
corev1.ResourceMemory: resource.MustParse("2Gi"),
},
},
},
},

View File

@ -7,6 +7,7 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins"
@ -221,21 +222,33 @@ func (r *ReconcileJenkins) buildLogger(jenkinsName string) logr.Logger {
func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Logger) error {
changed := false
if len(jenkins.Spec.Master.Image) == 0 {
var jenkinsContainer v1alpha2.Container
if len(jenkins.Spec.Master.Containers) == 0 {
changed = true
jenkinsContainer = v1alpha2.Container{Name: resources.JenkinsMasterContainerName}
} else {
if jenkins.Spec.Master.Containers[0].Name != resources.JenkinsMasterContainerName {
return errors.Errorf("first container in spec.master.containers must be Jenkins container with name '%s', please correct CR", resources.JenkinsMasterContainerName)
}
jenkinsContainer = jenkins.Spec.Master.Containers[0]
}
if len(jenkinsContainer.Image) == 0 {
logger.Info("Setting default Jenkins master image: " + constants.DefaultJenkinsMasterImage)
changed = true
jenkins.Spec.Master.Image = constants.DefaultJenkinsMasterImage
jenkins.Spec.Master.ImagePullPolicy = corev1.PullAlways
jenkinsContainer.Image = constants.DefaultJenkinsMasterImage
jenkinsContainer.ImagePullPolicy = corev1.PullAlways
}
if len(jenkins.Spec.Master.ImagePullPolicy) == 0 {
if len(jenkinsContainer.ImagePullPolicy) == 0 {
logger.Info(fmt.Sprintf("Setting default Jenkins master image pull policy: %s", corev1.PullAlways))
changed = true
jenkins.Spec.Master.ImagePullPolicy = corev1.PullAlways
jenkinsContainer.ImagePullPolicy = corev1.PullAlways
}
if jenkins.Spec.Master.ReadinessProbe == nil {
if jenkinsContainer.ReadinessProbe == nil {
logger.Info("Setting default Jenkins readinessProbe")
changed = true
jenkins.Spec.Master.ReadinessProbe = &corev1.Probe{
jenkinsContainer.ReadinessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
@ -246,10 +259,10 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
InitialDelaySeconds: int32(30),
}
}
if jenkins.Spec.Master.LivenessProbe == nil {
if jenkinsContainer.LivenessProbe == nil {
logger.Info("Setting default Jenkins livenessProbe")
changed = true
jenkins.Spec.Master.LivenessProbe = &corev1.Probe{
jenkinsContainer.LivenessProbe = &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
@ -281,10 +294,10 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
changed = true
jenkins.Spec.Master.Plugins = []v1alpha2.Plugin{{Name: "simple-theme-plugin", Version: "0.5.1"}}
}
if isResourceRequirementsNotSet(jenkins.Spec.Master.Resources) {
if isResourceRequirementsNotSet(jenkinsContainer.Resources) {
logger.Info("Setting default Jenkins master container resource requirements")
changed = true
jenkins.Spec.Master.Resources = corev1.ResourceRequirements{
jenkinsContainer.Resources = corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1"),
corev1.ResourceMemory: resource.MustParse("500Mi"),
@ -319,12 +332,16 @@ func (r *ReconcileJenkins) setDefaults(jenkins *v1alpha2.Jenkins, logger logr.Lo
Port: constants.DefaultSlavePortInt32,
}
}
for i, container := range jenkins.Spec.Master.Containers {
if setDefaultsForContainer(jenkins, i, logger.WithValues("container", container.Name)) {
changed = true
if len(jenkins.Spec.Master.Containers) > 1 {
for i, container := range jenkins.Spec.Master.Containers[1:] {
if setDefaultsForContainer(jenkins, i, logger.WithValues("container", container.Name)) {
changed = true
}
}
}
jenkins.Spec.Master.Containers = []v1alpha2.Container{jenkinsContainer}
if changed {
return errors.WithStack(r.client.Update(context.TODO(), jenkins))
}

View File

@ -9,6 +9,7 @@ import (
"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client"
"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
"github.com/bndr/gojenkins"
"github.com/golang/mock/gomock"
@ -388,16 +389,19 @@ func jenkinsCustomResource() *v1alpha2.Jenkins {
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Annotations: map[string]string{"test": "label"},
Container: v1alpha2.Container{
Image: "jenkins/jenkins",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("300m"),
corev1.ResourceMemory: resource.MustParse("500Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
corev1.ResourceMemory: resource.MustParse("2Gi"),
Containers: []v1alpha2.Container{
{
Name: resources.JenkinsMasterContainerName,
Image: "jenkins/jenkins",
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("300m"),
corev1.ResourceMemory: resource.MustParse("500Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("2"),
corev1.ResourceMemory: resource.MustParse("2Gi"),
},
},
},
},

View File

@ -74,40 +74,41 @@ func createJenkinsCR(t *testing.T, name, namespace string, seedJob *[]v1alpha2.S
Spec: v1alpha2.JenkinsSpec{
Master: v1alpha2.JenkinsMaster{
Annotations: map[string]string{"test": "label"},
Container: v1alpha2.Container{
Image: "jenkins/jenkins",
Env: []v1.EnvVar{
{
Name: "TEST_ENV",
Value: "test_env_value",
},
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
Port: intstr.FromString("http"),
Scheme: corev1.URISchemeHTTP,
},
},
InitialDelaySeconds: int32(80),
TimeoutSeconds: int32(4),
FailureThreshold: int32(10),
},
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
Port: intstr.FromString("http"),
Scheme: corev1.URISchemeHTTP,
},
},
InitialDelaySeconds: int32(80),
TimeoutSeconds: int32(4),
FailureThreshold: int32(10),
},
},
Containers: []v1alpha2.Container{
{
Name: resources.JenkinsMasterContainerName,
Image: "jenkins/jenkins",
Env: []v1.EnvVar{
{
Name: "TEST_ENV",
Value: "test_env_value",
},
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
Port: intstr.FromString("http"),
Scheme: corev1.URISchemeHTTP,
},
},
InitialDelaySeconds: int32(80),
TimeoutSeconds: int32(4),
FailureThreshold: int32(10),
},
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/login",
Port: intstr.FromString("http"),
Scheme: corev1.URISchemeHTTP,
},
},
InitialDelaySeconds: int32(80),
TimeoutSeconds: int32(4),
FailureThreshold: int32(10),
},
},
{
Name: "envoyproxy",
Image: "envoyproxy/envoy-alpine",