223 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
| package base
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/backuprestore"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/notifications/event"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/notifications/reason"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/pkg/log"
 | |
| 	"github.com/jenkinsci/kubernetes-operator/version"
 | |
| 
 | |
| 	stackerr "github.com/pkg/errors"
 | |
| 	corev1 "k8s.io/api/core/v1"
 | |
| 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | |
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | |
| 	"sigs.k8s.io/controller-runtime/pkg/reconcile"
 | |
| )
 | |
| 
 | |
| func (r *ReconcileJenkinsBaseConfiguration) checkForPodRecreation(currentJenkinsMasterPod corev1.Pod, userAndPasswordHash string) reason.Reason {
 | |
| 	var messages []string
 | |
| 	var verbose []string
 | |
| 
 | |
| 	if currentJenkinsMasterPod.Status.Phase == corev1.PodFailed ||
 | |
| 		currentJenkinsMasterPod.Status.Phase == corev1.PodSucceeded ||
 | |
| 		currentJenkinsMasterPod.Status.Phase == corev1.PodUnknown {
 | |
| 		//TODO add Jenkins last 10 line logs
 | |
| 		messages = append(messages, fmt.Sprintf("Invalid Jenkins pod phase '%s'", currentJenkinsMasterPod.Status.Phase))
 | |
| 		verbose = append(verbose, fmt.Sprintf("Invalid Jenkins pod phase '%+v'", currentJenkinsMasterPod.Status))
 | |
| 		return reason.NewPodRestart(reason.KubernetesSource, messages, verbose...)
 | |
| 	}
 | |
| 
 | |
| 	userAndPasswordHashIsDifferent := userAndPasswordHash != r.Configuration.Jenkins.Status.UserAndPasswordHash
 | |
| 	userAndPasswordHashStatusNotEmpty := r.Configuration.Jenkins.Status.UserAndPasswordHash != ""
 | |
| 
 | |
| 	if userAndPasswordHashIsDifferent && userAndPasswordHashStatusNotEmpty {
 | |
| 		messages = append(messages, "User or password have changed")
 | |
| 		verbose = append(verbose, "User or password have changed, recreating pod")
 | |
| 	}
 | |
| 
 | |
| 	if r.Configuration.Jenkins.Spec.Restore.RecoveryOnce != 0 && r.Configuration.Jenkins.Status.RestoredBackup != 0 {
 | |
| 		messages = append(messages, "spec.restore.recoveryOnce is set")
 | |
| 		verbose = append(verbose, "spec.restore.recoveryOnce is set, recreating pod")
 | |
| 	}
 | |
| 
 | |
| 	if version.Version != r.Configuration.Jenkins.Status.OperatorVersion {
 | |
| 		messages = append(messages, "Jenkins Operator version has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins Operator version has changed, actual '%+v' new '%+v'",
 | |
| 			r.Configuration.Jenkins.Status.OperatorVersion, version.Version))
 | |
| 	}
 | |
| 
 | |
| 	if !reflect.DeepEqual(r.Configuration.Jenkins.Spec.Master.SecurityContext, currentJenkinsMasterPod.Spec.SecurityContext) {
 | |
| 		messages = append(messages, "Jenkins pod security context has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins pod security context has changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.Spec.SecurityContext, r.Configuration.Jenkins.Spec.Master.SecurityContext))
 | |
| 	}
 | |
| 
 | |
| 	if !compareImagePullSecrets(r.Configuration.Jenkins.Spec.Master.ImagePullSecrets, currentJenkinsMasterPod.Spec.ImagePullSecrets) {
 | |
| 		messages = append(messages, "Jenkins Pod ImagePullSecrets has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins Pod ImagePullSecrets has changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.Spec.ImagePullSecrets, r.Configuration.Jenkins.Spec.Master.ImagePullSecrets))
 | |
| 	}
 | |
| 
 | |
| 	if !compareMap(r.Configuration.Jenkins.Spec.Master.NodeSelector, currentJenkinsMasterPod.Spec.NodeSelector) {
 | |
| 		messages = append(messages, "Jenkins pod node selector has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins pod node selector has changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.Spec.NodeSelector, r.Configuration.Jenkins.Spec.Master.NodeSelector))
 | |
| 	}
 | |
| 
 | |
| 	if !compareMap(r.Configuration.Jenkins.Spec.Master.Labels, currentJenkinsMasterPod.Labels) {
 | |
| 		messages = append(messages, "Jenkins pod labels have changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins pod labels have changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.Labels, r.Configuration.Jenkins.Spec.Master.Labels))
 | |
| 	}
 | |
| 
 | |
| 	if !compareMap(r.Configuration.Jenkins.Spec.Master.Annotations, currentJenkinsMasterPod.ObjectMeta.Annotations) {
 | |
| 		messages = append(messages, "Jenkins pod annotations have changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins pod annotations have changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.ObjectMeta.Annotations, r.Configuration.Jenkins.Spec.Master.Annotations))
 | |
| 	}
 | |
| 
 | |
| 	if !r.compareVolumes(currentJenkinsMasterPod) {
 | |
| 		messages = append(messages, "Jenkins pod volumes have changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins pod volumes have changed, actual '%v' required '%v'",
 | |
| 			currentJenkinsMasterPod.Spec.Volumes, r.Configuration.Jenkins.Spec.Master.Volumes))
 | |
| 	}
 | |
| 
 | |
| 	if len(r.Configuration.Jenkins.Spec.Master.Containers) != len(currentJenkinsMasterPod.Spec.Containers) {
 | |
| 		messages = append(messages, "Jenkins amount of containers has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins amount of containers has changed, actual '%+v' required '%+v'",
 | |
| 			len(currentJenkinsMasterPod.Spec.Containers), len(r.Configuration.Jenkins.Spec.Master.Containers)))
 | |
| 	}
 | |
| 
 | |
| 	if r.Configuration.Jenkins.Spec.Master.PriorityClassName != currentJenkinsMasterPod.Spec.PriorityClassName {
 | |
| 		messages = append(messages, "Jenkins priorityClassName has changed")
 | |
| 		verbose = append(verbose, fmt.Sprintf("Jenkins priorityClassName has changed, actual '%+v' required '%+v'",
 | |
| 			currentJenkinsMasterPod.Spec.PriorityClassName, r.Configuration.Jenkins.Spec.Master.PriorityClassName))
 | |
| 	}
 | |
| 
 | |
| 	customResourceReplaced := (r.Configuration.Jenkins.Status.BaseConfigurationCompletedTime == nil ||
 | |
| 		r.Configuration.Jenkins.Status.UserConfigurationCompletedTime == nil) &&
 | |
| 		r.Configuration.Jenkins.Status.UserAndPasswordHash == ""
 | |
| 
 | |
| 	if customResourceReplaced {
 | |
| 		messages = append(messages, "Jenkins CR has been replaced")
 | |
| 		verbose = append(verbose, "Jenkins CR has been replaced")
 | |
| 	}
 | |
| 
 | |
| 	for _, actualContainer := range currentJenkinsMasterPod.Spec.Containers {
 | |
| 		if actualContainer.Name == resources.JenkinsMasterContainerName {
 | |
| 			containerMessages, verboseMessages := r.compareContainers(resources.NewJenkinsMasterContainer(r.Configuration.Jenkins), actualContainer)
 | |
| 			messages = append(messages, containerMessages...)
 | |
| 			verbose = append(verbose, verboseMessages...)
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var expectedContainer *corev1.Container
 | |
| 		for _, jenkinsContainer := range r.Configuration.Jenkins.Spec.Master.Containers {
 | |
| 			if jenkinsContainer.Name == actualContainer.Name {
 | |
| 				tmp := resources.ConvertJenkinsContainerToKubernetesContainer(jenkinsContainer)
 | |
| 				expectedContainer = &tmp
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if expectedContainer == nil {
 | |
| 			messages = append(messages, fmt.Sprintf("Container '%s' not found in pod", actualContainer.Name))
 | |
| 			verbose = append(verbose, fmt.Sprintf("Container '%+v' not found in pod", actualContainer))
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		containerMessages, verboseMessages := r.compareContainers(*expectedContainer, actualContainer)
 | |
| 
 | |
| 		messages = append(messages, containerMessages...)
 | |
| 		verbose = append(verbose, verboseMessages...)
 | |
| 	}
 | |
| 
 | |
| 	return reason.NewPodRestart(reason.OperatorSource, messages, verbose...)
 | |
| }
 | |
| 
 | |
| func (r *ReconcileJenkinsBaseConfiguration) ensureJenkinsMasterPod(meta metav1.ObjectMeta) (reconcile.Result, error) {
 | |
| 	userAndPasswordHash, err := r.calculateUserAndPasswordHash()
 | |
| 	if err != nil {
 | |
| 		return reconcile.Result{}, err
 | |
| 	}
 | |
| 
 | |
| 	// Check if this Pod already exists
 | |
| 	currentJenkinsMasterPod, err := r.getJenkinsMasterPod()
 | |
| 	if err != nil && apierrors.IsNotFound(err) {
 | |
| 		jenkinsMasterPod := resources.NewJenkinsMasterPod(meta, r.Configuration.Jenkins)
 | |
| 		*r.Notifications <- event.Event{
 | |
| 			Jenkins: *r.Configuration.Jenkins,
 | |
| 			Phase:   event.PhaseBase,
 | |
| 			Level:   v1alpha2.NotificationLevelInfo,
 | |
| 			Reason:  reason.NewPodCreation(reason.OperatorSource, []string{"Creating a new Jenkins Master Pod"}),
 | |
| 		}
 | |
| 		r.logger.Info(fmt.Sprintf("Creating a new Jenkins Master Pod %s/%s", jenkinsMasterPod.Namespace, jenkinsMasterPod.Name))
 | |
| 		err = r.CreateResource(jenkinsMasterPod)
 | |
| 		if err != nil {
 | |
| 			return reconcile.Result{}, stackerr.WithStack(err)
 | |
| 		}
 | |
| 
 | |
| 		currentJenkinsMasterPod, err := r.waitUntilCreateJenkinsMasterPod()
 | |
| 		if err == nil {
 | |
| 			r.handleAdmissionControllerChanges(currentJenkinsMasterPod)
 | |
| 		} else {
 | |
| 			r.logger.V(log.VWarn).Info(fmt.Sprintf("waitUntilCreateJenkinsMasterPod has failed: %s", err))
 | |
| 		}
 | |
| 
 | |
| 		now := metav1.Now()
 | |
| 		r.Configuration.Jenkins.Status = v1alpha2.JenkinsStatus{
 | |
| 			OperatorVersion:     version.Version,
 | |
| 			ProvisionStartTime:  &now,
 | |
| 			LastBackup:          r.Configuration.Jenkins.Status.LastBackup,
 | |
| 			PendingBackup:       r.Configuration.Jenkins.Status.LastBackup,
 | |
| 			UserAndPasswordHash: userAndPasswordHash,
 | |
| 		}
 | |
| 		return reconcile.Result{Requeue: true}, r.Client.Update(context.TODO(), r.Configuration.Jenkins)
 | |
| 	} else if err != nil && !apierrors.IsNotFound(err) {
 | |
| 		return reconcile.Result{}, stackerr.WithStack(err)
 | |
| 	}
 | |
| 
 | |
| 	if currentJenkinsMasterPod == nil {
 | |
| 		return reconcile.Result{Requeue: true}, nil
 | |
| 	}
 | |
| 
 | |
| 	if r.IsJenkinsTerminating(*currentJenkinsMasterPod) && r.Configuration.Jenkins.Status.UserConfigurationCompletedTime != nil {
 | |
| 		backupAndRestore := backuprestore.New(r.Configuration, r.logger)
 | |
| 		if backupAndRestore.IsBackupTriggerEnabled() {
 | |
| 			backupAndRestore.StopBackupTrigger()
 | |
| 		}
 | |
| 		if r.Configuration.Jenkins.Spec.Backup.MakeBackupBeforePodDeletion {
 | |
| 			if r.Configuration.Jenkins.Status.LastBackup == r.Configuration.Jenkins.Status.PendingBackup && !r.Configuration.Jenkins.Status.BackupDoneBeforePodDeletion {
 | |
| 				r.Configuration.Jenkins.Status.PendingBackup = r.Configuration.Jenkins.Status.PendingBackup + 1
 | |
| 				r.Configuration.Jenkins.Status.BackupDoneBeforePodDeletion = true
 | |
| 				err = r.Client.Update(context.TODO(), r.Configuration.Jenkins)
 | |
| 				if err != nil {
 | |
| 					return reconcile.Result{}, err
 | |
| 				}
 | |
| 			}
 | |
| 			if err = backupAndRestore.Backup(); err != nil {
 | |
| 				return reconcile.Result{}, err
 | |
| 			}
 | |
| 		}
 | |
| 		return reconcile.Result{Requeue: true}, nil
 | |
| 	}
 | |
| 
 | |
| 	if !r.IsJenkinsTerminating(*currentJenkinsMasterPod) {
 | |
| 		restartReason := r.checkForPodRecreation(*currentJenkinsMasterPod, userAndPasswordHash)
 | |
| 		if restartReason.HasMessages() {
 | |
| 			for _, msg := range restartReason.Verbose() {
 | |
| 				r.logger.Info(msg)
 | |
| 			}
 | |
| 
 | |
| 			return reconcile.Result{Requeue: true}, r.Configuration.RestartJenkinsMasterPod(restartReason)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return reconcile.Result{}, nil
 | |
| }
 |