diff --git a/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go b/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go index f76fe27c..00ba8b95 100644 --- a/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go +++ b/pkg/apis/jenkinsio/v1alpha1/jenkins_types.go @@ -19,9 +19,10 @@ type JenkinsSpec struct { // JenkinsMaster defines the Jenkins master pod attributes and plugins, // every single change requires Jenkins master pod restart type JenkinsMaster struct { - Image string `json:"image,omitempty"` - Annotations map[string]string `json:"masterAnnotations,omitempty"` - Resources corev1.ResourceRequirements `json:"resources,omitempty"` + Image string `json:"image,omitempty"` + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + Annotations map[string]string `json:"masterAnnotations,omitempty"` + Resources corev1.ResourceRequirements `json:"resources,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 8644e166..669e6d3d 100644 --- a/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/jenkinsio/v1alpha1/zz_generated.deepcopy.go @@ -113,6 +113,13 @@ 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 + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations *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 12ba7a2c..a5809eb7 100644 --- a/pkg/controller/jenkins/configuration/base/reconcile.go +++ b/pkg/controller/jenkins/configuration/base/reconcile.go @@ -338,40 +338,54 @@ func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.O return reconcile.Result{}, stackerr.WithStack(err) } - // Recreate pod + if currentJenkinsMasterPod != nil && isPodTerminating(*currentJenkinsMasterPod) { + return reconcile.Result{Requeue: true}, nil + } + if currentJenkinsMasterPod != nil && r.isRecreatePodNeeded(*currentJenkinsMasterPod) { + return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod(meta) + } + + return reconcile.Result{}, nil +} + +func isPodTerminating(pod corev1.Pod) bool { + return pod.ObjectMeta.DeletionTimestamp != nil +} + +func (r *ReconcileJenkinsBaseConfiguration) isRecreatePodNeeded(currentJenkinsMasterPod corev1.Pod) bool { recreatePod := false - if currentJenkinsMasterPod != nil && - (currentJenkinsMasterPod.Status.Phase == corev1.PodFailed || - currentJenkinsMasterPod.Status.Phase == corev1.PodSucceeded || - currentJenkinsMasterPod.Status.Phase == corev1.PodUnknown) { - r.logger.Info(fmt.Sprintf("Invalid Jenkins pod phase '%+v', recreating pod", currentJenkinsMasterPod.Status.Phase)) + + if currentJenkinsMasterPod.Status.Phase == corev1.PodFailed || + currentJenkinsMasterPod.Status.Phase == corev1.PodSucceeded || + currentJenkinsMasterPod.Status.Phase == corev1.PodUnknown { + r.logger.Info(fmt.Sprintf("Invalid Jenkins pod phase '%+v', recreating pod", currentJenkinsMasterPod.Status)) recreatePod = true } - if currentJenkinsMasterPod != nil && - r.jenkins.Spec.Master.Image != currentJenkinsMasterPod.Spec.Containers[0].Image { + if r.jenkins.Spec.Master.Image != currentJenkinsMasterPod.Spec.Containers[0].Image { r.logger.Info(fmt.Sprintf("Jenkins image has changed to '%+v', recreating pod", r.jenkins.Spec.Master.Image)) recreatePod = true } - if currentJenkinsMasterPod != nil && len(r.jenkins.Spec.Master.Annotations) > 0 && + if len(r.jenkins.Spec.Master.Annotations) > 0 && !reflect.DeepEqual(r.jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.ObjectMeta.Annotations) { r.logger.Info(fmt.Sprintf("Jenkins pod annotations have changed to '%+v', recreating pod", r.jenkins.Spec.Master.Annotations)) recreatePod = true } - if currentJenkinsMasterPod != nil && - !reflect.DeepEqual(r.jenkins.Spec.Master.Resources, currentJenkinsMasterPod.Spec.Containers[0].Resources) { + if !reflect.DeepEqual(r.jenkins.Spec.Master.Resources, currentJenkinsMasterPod.Spec.Containers[0].Resources) { r.logger.Info(fmt.Sprintf("Jenkins pod resources have changed, actual '%+v' required '%+v' - recreating pod", currentJenkinsMasterPod.Spec.Containers[0].Resources, r.jenkins.Spec.Master.Resources)) recreatePod = true } - if currentJenkinsMasterPod != nil && recreatePod && currentJenkinsMasterPod.ObjectMeta.DeletionTimestamp == nil { - return reconcile.Result{Requeue: true}, r.restartJenkinsMasterPod(meta) + 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", + r.jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector)) + recreatePod = true } - return reconcile.Result{}, nil + return recreatePod } func (r *ReconcileJenkinsBaseConfiguration) restartJenkinsMasterPod(meta metav1.ObjectMeta) error { diff --git a/pkg/controller/jenkins/configuration/base/resources/pod.go b/pkg/controller/jenkins/configuration/base/resources/pod.go index e1b68d3d..26bc9494 100644 --- a/pkg/controller/jenkins/configuration/base/resources/pod.go +++ b/pkg/controller/jenkins/configuration/base/resources/pod.go @@ -76,6 +76,7 @@ func NewJenkinsMasterPod(objectMeta metav1.ObjectMeta, jenkins *v1alpha1.Jenkins RunAsUser: &runAsUser, RunAsGroup: &runAsUser, }, + NodeSelector: jenkins.Spec.Master.NodeSelector, Containers: []corev1.Container{ { Name: "jenkins-master", diff --git a/test/e2e/configuration_test.go b/test/e2e/configuration_test.go index c3ad21c5..202e9eec 100644 --- a/test/e2e/configuration_test.go +++ b/test/e2e/configuration_test.go @@ -139,6 +139,10 @@ func verifyJenkinsMasterPodAttributes(t *testing.T, jenkins *v1alpha1.Jenkins) { t.Fatalf("Invalid jenkins pod continer resources expected '%+v', actual '%+v'", jenkins.Spec.Master.Resources, jenkinsPod.Spec.Containers[0].Resources) } + if !reflect.DeepEqual(jenkinsPod.Spec.NodeSelector, jenkins.Spec.Master.NodeSelector) { + t.Fatalf("Invalid jenkins pod node selector expected '%+v', actual '%+v'", jenkins.Spec.Master.NodeSelector, jenkinsPod.Spec.NodeSelector) + } + t.Log("Jenkins pod attributes are valid") } diff --git a/test/e2e/jenkins.go b/test/e2e/jenkins.go index d31b304e..05046045 100644 --- a/test/e2e/jenkins.go +++ b/test/e2e/jenkins.go @@ -72,6 +72,7 @@ func createJenkinsCR(t *testing.T, name, namespace string) *v1alpha1.Jenkins { "audit-trail:2.4": {}, "simple-theme-plugin:0.5.1": {}, }, + NodeSelector: map[string]string{"kubernetes.io/hostname": "minikube"}, }, //TODO(bantoniak) add seed job with private key SeedJobs: []v1alpha1.SeedJob{