Remove unused job package and fix error handling in seedjobs package
This commit is contained in:
		
							parent
							
								
									30b83638e2
								
							
						
					
					
						commit
						eb0f8a8331
					
				|  | @ -145,9 +145,8 @@ func isNotFoundError(err error) bool { | ||||||
| func (jenkins *jenkins) GetNodeSecret(name string) (string, error) { | func (jenkins *jenkins) GetNodeSecret(name string) (string, error) { | ||||||
| 	var content string | 	var content string | ||||||
| 	_, err := jenkins.Requester.GetXML(fmt.Sprintf("/computer/%s/slave-agent.jnlp", name), &content, nil) | 	_, err := jenkins.Requester.GetXML(fmt.Sprintf("/computer/%s/slave-agent.jnlp", name), &content, nil) | ||||||
| 
 |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", errors.WithStack(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	match := regex.FindStringSubmatch(content) | 	match := regex.FindStringSubmatch(content) | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" |  | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/internal/render" | 	"github.com/jenkinsci/kubernetes-operator/internal/render" | ||||||
|  | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | ||||||
| 
 | 
 | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
|  |  | ||||||
|  | @ -4,9 +4,9 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/jenkinsci/kubernetes-operator/internal/render" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/internal/render" |  | ||||||
| 
 | 
 | ||||||
| 	corev1 "k8s.io/api/core/v1" | 	corev1 "k8s.io/api/core/v1" | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ package user | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" | ||||||
| 	jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" | 	jenkinsclient "github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" | ||||||
|  | @ -11,10 +10,8 @@ import ( | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/casc" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/casc" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/seedjobs" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/user/seedjobs" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/groovy" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/groovy" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/go-logr/logr" | 	"github.com/go-logr/logr" | ||||||
| 	"github.com/pkg/errors" |  | ||||||
| 	"k8s.io/client-go/kubernetes" | 	"k8s.io/client-go/kubernetes" | ||||||
| 	"k8s.io/client-go/rest" | 	"k8s.io/client-go/rest" | ||||||
| 	k8s "sigs.k8s.io/controller-runtime/pkg/client" | 	k8s "sigs.k8s.io/controller-runtime/pkg/client" | ||||||
|  | @ -82,20 +79,10 @@ func (r *ReconcileUserConfiguration) ensureSeedJobs() (reconcile.Result, error) | ||||||
| 	seedJobs := seedjobs.New(r.jenkinsClient, r.k8sClient, r.logger) | 	seedJobs := seedjobs.New(r.jenkinsClient, r.k8sClient, r.logger) | ||||||
| 	done, err := seedJobs.EnsureSeedJobs(r.jenkins) | 	done, err := seedJobs.EnsureSeedJobs(r.jenkins) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		// build failed and can be recovered - retry build and requeue reconciliation loop with timeout
 | 		return reconcile.Result{}, err | ||||||
| 		if err == jobs.ErrorBuildFailed { |  | ||||||
| 			return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 10}, nil |  | ||||||
| 	} | 	} | ||||||
| 		// build failed and cannot be recovered
 |  | ||||||
| 		if err == jobs.ErrorUnrecoverableBuildFailed { |  | ||||||
| 			return reconcile.Result{}, nil |  | ||||||
| 		} |  | ||||||
| 		// unexpected error - requeue reconciliation loop
 |  | ||||||
| 		return reconcile.Result{}, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 	// build not finished yet - requeue reconciliation loop with timeout
 |  | ||||||
| 	if !done { | 	if !done { | ||||||
| 		return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil | 		return reconcile.Result{Requeue: true}, nil | ||||||
| 	} | 	} | ||||||
| 	return reconcile.Result{}, nil | 	return reconcile.Result{}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -166,7 +166,7 @@ func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err err | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		if err != nil && !apierrors.IsNotFound(err) { | 		if err != nil && !apierrors.IsNotFound(err) { | ||||||
| 			return false, err | 			return false, stackerr.WithStack(err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -193,7 +193,7 @@ func (s *SeedJobs) EnsureSeedJobs(jenkins *v1alpha2.Jenkins) (done bool, err err | ||||||
| 
 | 
 | ||||||
| // createJob is responsible for creating jenkins job which configures jenkins seed jobs and deploy keys
 | // createJob is responsible for creating jenkins job which configures jenkins seed jobs and deploy keys
 | ||||||
| func (s *SeedJobs) createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err error) { | func (s *SeedJobs) createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err error) { | ||||||
| 	groovyClient := groovy.New(s.jenkinsClient, s.k8sClient, s.logger, jenkins, "user-groovy", jenkins.Spec.GroovyScripts.Customization) | 	groovyClient := groovy.New(s.jenkinsClient, s.k8sClient, s.logger, jenkins, "seed-jobs", jenkins.Spec.GroovyScripts.Customization) | ||||||
| 	for _, seedJob := range jenkins.Spec.SeedJobs { | 	for _, seedJob := range jenkins.Spec.SeedJobs { | ||||||
| 		credentialValue, err := s.credentialValue(jenkins.Namespace, seedJob) | 		credentialValue, err := s.credentialValue(jenkins.Namespace, seedJob) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | @ -209,7 +209,6 @@ func (s *SeedJobs) createJobs(jenkins *v1alpha2.Jenkins) (requeue bool, err erro | ||||||
| 		hash.Write([]byte(groovyScript)) | 		hash.Write([]byte(groovyScript)) | ||||||
| 		hash.Write([]byte(credentialValue)) | 		hash.Write([]byte(credentialValue)) | ||||||
| 		requeue, err := groovyClient.EnsureSingle(seedJob.ID, fmt.Sprintf("%s.groovy", seedJob.ID), base64.URLEncoding.EncodeToString(hash.Sum(nil)), groovyScript) | 		requeue, err := groovyClient.EnsureSingle(seedJob.ID, fmt.Sprintf("%s.groovy", seedJob.ID), base64.URLEncoding.EncodeToString(hash.Sum(nil)), groovyScript) | ||||||
| 
 |  | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return true, err | 			return true, err | ||||||
| 		} | 		} | ||||||
|  | @ -240,9 +239,8 @@ func (s *SeedJobs) ensureLabelsForSecrets(jenkins v1alpha2.Jenkins) error { | ||||||
| 
 | 
 | ||||||
| 			if !resources.VerifyIfLabelsAreSet(secret, requiredLabels) { | 			if !resources.VerifyIfLabelsAreSet(secret, requiredLabels) { | ||||||
| 				secret.ObjectMeta.Labels = requiredLabels | 				secret.ObjectMeta.Labels = requiredLabels | ||||||
| 				err = stackerr.WithStack(s.k8sClient.Update(context.TODO(), secret)) | 				if err = s.k8sClient.Update(context.TODO(), secret); err != nil { | ||||||
| 				if err != nil { | 					return stackerr.WithStack(err) | ||||||
| 					return err |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -257,7 +255,7 @@ func (s *SeedJobs) credentialValue(namespace string, seedJob v1alpha2.SeedJob) ( | ||||||
| 		namespaceName := types.NamespacedName{Namespace: namespace, Name: seedJob.CredentialID} | 		namespaceName := types.NamespacedName{Namespace: namespace, Name: seedJob.CredentialID} | ||||||
| 		err := s.k8sClient.Get(context.TODO(), namespaceName, secret) | 		err := s.k8sClient.Get(context.TODO(), namespaceName, secret) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return "", err | 			return "", stackerr.WithStack(err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType { | 		if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType { | ||||||
|  | @ -321,10 +319,10 @@ func (s SeedJobs) createAgent(jenkinsClient jenkinsclient.Jenkins, k8sClient cli | ||||||
| 	if err != nil && err.Error() == "No node found" { | 	if err != nil && err.Error() == "No node found" { | ||||||
| 		_, err = jenkinsClient.CreateNode(agentName, 1, "The jenkins-operator generated agent", "/home/jenkins", agentName) | 		_, err = jenkinsClient.CreateNode(agentName, 1, "The jenkins-operator generated agent", "/home/jenkins", agentName) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return stackerr.WithStack(err) | ||||||
| 		} | 		} | ||||||
| 	} else if err != nil { | 	} else if err != nil { | ||||||
| 		return err | 		return stackerr.WithStack(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	secret, err := jenkinsClient.GetNodeSecret(agentName) | 	secret, err := jenkinsClient.GetNodeSecret(agentName) | ||||||
|  | @ -338,10 +336,10 @@ func (s SeedJobs) createAgent(jenkinsClient jenkinsclient.Jenkins, k8sClient cli | ||||||
| 	if apierrors.IsAlreadyExists(err) { | 	if apierrors.IsAlreadyExists(err) { | ||||||
| 		err := k8sClient.Update(context.TODO(), deployment) | 		err := k8sClient.Update(context.TODO(), deployment) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return stackerr.WithStack(err) | ||||||
| 		} | 		} | ||||||
| 	} else if err != nil { | 	} else if err != nil { | ||||||
| 		return err | 		return stackerr.WithStack(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
|  |  | ||||||
|  | @ -19,15 +19,15 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // ValidateSeedJobs verify seed jobs configuration
 | // ValidateSeedJobs verify seed jobs configuration
 | ||||||
| func (r *SeedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) (bool, error) { | func (s *SeedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) (bool, error) { | ||||||
| 	valid := true | 	valid := true | ||||||
| 
 | 
 | ||||||
| 	if !r.validateIfIDIsUnique(jenkins.Spec.SeedJobs) { | 	if !s.validateIfIDIsUnique(jenkins.Spec.SeedJobs) { | ||||||
| 		valid = false | 		valid = false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, seedJob := range jenkins.Spec.SeedJobs { | 	for _, seedJob := range jenkins.Spec.SeedJobs { | ||||||
| 		logger := r.logger.WithValues("seedJob", fmt.Sprintf("%+v", seedJob)).V(log.VWarn) | 		logger := s.logger.WithValues("seedJob", seedJob.ID).V(log.VWarn) | ||||||
| 
 | 
 | ||||||
| 		if len(seedJob.ID) == 0 { | 		if len(seedJob.ID) == 0 { | ||||||
| 			logger.Info("id can't be empty") | 			logger.Info("id can't be empty") | ||||||
|  | @ -69,7 +69,7 @@ func (r *SeedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) (bool, error) { | ||||||
| 		if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType { | 		if seedJob.JenkinsCredentialType == v1alpha2.BasicSSHCredentialType || seedJob.JenkinsCredentialType == v1alpha2.UsernamePasswordCredentialType { | ||||||
| 			secret := &v1.Secret{} | 			secret := &v1.Secret{} | ||||||
| 			namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID} | 			namespaceName := types.NamespacedName{Namespace: jenkins.Namespace, Name: seedJob.CredentialID} | ||||||
| 			err := r.k8sClient.Get(context.TODO(), namespaceName, secret) | 			err := s.k8sClient.Get(context.TODO(), namespaceName, secret) | ||||||
| 			if err != nil && apierrors.IsNotFound(err) { | 			if err != nil && apierrors.IsNotFound(err) { | ||||||
| 				logger.Info(fmt.Sprintf("required secret '%s' with Jenkins credential not found", seedJob.CredentialID)) | 				logger.Info(fmt.Sprintf("required secret '%s' with Jenkins credential not found", seedJob.CredentialID)) | ||||||
| 				return false, nil | 				return false, nil | ||||||
|  | @ -90,19 +90,19 @@ func (r *SeedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) (bool, error) { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if len(seedJob.BuildPeriodically) > 0 { | 		if len(seedJob.BuildPeriodically) > 0 { | ||||||
| 			if !r.validateSchedule(seedJob, seedJob.BuildPeriodically, "buildPeriodically") { | 			if !s.validateSchedule(seedJob, seedJob.BuildPeriodically, "buildPeriodically") { | ||||||
| 				valid = false | 				valid = false | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if len(seedJob.PollSCM) > 0 { | 		if len(seedJob.PollSCM) > 0 { | ||||||
| 			if !r.validateSchedule(seedJob, seedJob.PollSCM, "pollSCM") { | 			if !s.validateSchedule(seedJob, seedJob.PollSCM, "pollSCM") { | ||||||
| 				valid = false | 				valid = false | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if seedJob.GitHubPushTrigger { | 		if seedJob.GitHubPushTrigger { | ||||||
| 			if !r.validateGitHubPushTrigger(jenkins) { | 			if !s.validateGitHubPushTrigger(jenkins) { | ||||||
| 				valid = false | 				valid = false | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | @ -111,16 +111,16 @@ func (r *SeedJobs) ValidateSeedJobs(jenkins v1alpha2.Jenkins) (bool, error) { | ||||||
| 	return valid, nil | 	return valid, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *SeedJobs) validateSchedule(job v1alpha2.SeedJob, str string, key string) bool { | func (s *SeedJobs) validateSchedule(job v1alpha2.SeedJob, str string, key string) bool { | ||||||
| 	_, err := cron.Parse(str) | 	_, err := cron.Parse(str) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		r.logger.V(log.VWarn).Info(fmt.Sprintf("`%s` schedule '%s' is invalid cron spec in `%s`", key, str, job.ID)) | 		s.logger.V(log.VWarn).Info(fmt.Sprintf("`%s` schedule '%s' is invalid cron spec in `%s`", key, str, job.ID)) | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *SeedJobs) validateGitHubPushTrigger(jenkins v1alpha2.Jenkins) bool { | func (s *SeedJobs) validateGitHubPushTrigger(jenkins v1alpha2.Jenkins) bool { | ||||||
| 	exists := false | 	exists := false | ||||||
| 	for _, plugin := range jenkins.Spec.Master.BasePlugins { | 	for _, plugin := range jenkins.Spec.Master.BasePlugins { | ||||||
| 		if plugin.Name == "github" { | 		if plugin.Name == "github" { | ||||||
|  | @ -136,17 +136,17 @@ func (r *SeedJobs) validateGitHubPushTrigger(jenkins v1alpha2.Jenkins) bool { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !exists && !userExists { | 	if !exists && !userExists { | ||||||
| 		r.logger.V(log.VWarn).Info("githubPushTrigger is set. This function requires `github` plugin installed in .Spec.Master.Plugins because seed jobs Push Trigger function needs it") | 		s.logger.V(log.VWarn).Info("githubPushTrigger is set. This function requires `github` plugin installed in .Spec.Master.Plugins because seed jobs Push Trigger function needs it") | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *SeedJobs) validateIfIDIsUnique(seedJobs []v1alpha2.SeedJob) bool { | func (s *SeedJobs) validateIfIDIsUnique(seedJobs []v1alpha2.SeedJob) bool { | ||||||
| 	ids := map[string]bool{} | 	ids := map[string]bool{} | ||||||
| 	for _, seedJob := range seedJobs { | 	for _, seedJob := range seedJobs { | ||||||
| 		if _, found := ids[seedJob.ID]; found { | 		if _, found := ids[seedJob.ID]; found { | ||||||
| 			r.logger.V(log.VWarn).Info(fmt.Sprintf("'%s' seed job ID is not unique", seedJob.ID)) | 			s.logger.V(log.VWarn).Info(fmt.Sprintf("'%s' seed job ID is not unique", seedJob.ID)) | ||||||
| 			return false | 			return false | ||||||
| 		} | 		} | ||||||
| 		ids[seedJob.ID] = true | 		ids[seedJob.ID] = true | ||||||
|  |  | ||||||
|  | @ -11,7 +11,6 @@ import ( | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/configuration/base/resources" | 	"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/configuration/user" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/constants" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/jobs" |  | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins" | 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/plugins" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/event" | 	"github.com/jenkinsci/kubernetes-operator/pkg/event" | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | 	"github.com/jenkinsci/kubernetes-operator/pkg/log" | ||||||
|  | @ -169,9 +168,6 @@ func (r *ReconcileJenkins) Reconcile(request reconcile.Request) (reconcile.Resul | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if err == jobs.ErrorUnrecoverableBuildFailed { |  | ||||||
| 			return reconcile.Result{Requeue: false}, nil |  | ||||||
| 		} |  | ||||||
| 		if _, ok := err.(*jenkinsclient.GroovyScriptExecutionFailed); ok { | 		if _, ok := err.(*jenkinsclient.GroovyScriptExecutionFailed); ok { | ||||||
| 			return reconcile.Result{Requeue: false}, nil | 			return reconcile.Result{Requeue: false}, nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| // Package jobs implements common jenkins jobs operations
 |  | ||||||
| package jobs |  | ||||||
|  | @ -1,293 +0,0 @@ | ||||||
| package jobs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
| 
 |  | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" |  | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/controller/jenkins/client" |  | ||||||
| 	"github.com/jenkinsci/kubernetes-operator/pkg/log" |  | ||||||
| 
 |  | ||||||
| 	"github.com/go-logr/logr" |  | ||||||
| 	"github.com/pkg/errors" |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	k8s "sigs.k8s.io/controller-runtime/pkg/client" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var ( |  | ||||||
| 	// ErrorUnexpectedBuildStatus - this is custom error returned when jenkins build has unexpected status
 |  | ||||||
| 	ErrorUnexpectedBuildStatus = fmt.Errorf("unexpected build status") |  | ||||||
| 	// ErrorBuildFailed - this is custom error returned when jenkins build has failed
 |  | ||||||
| 	ErrorBuildFailed = fmt.Errorf("build failed") |  | ||||||
| 	// ErrorAbortBuildFailed - this is custom error returned when jenkins build couldn't be aborted
 |  | ||||||
| 	ErrorAbortBuildFailed = fmt.Errorf("build abort failed") |  | ||||||
| 	// ErrorUnrecoverableBuildFailed - this is custom error returned when jenkins build has failed and cannot be recovered
 |  | ||||||
| 	ErrorUnrecoverableBuildFailed = fmt.Errorf("build failed and cannot be recovered") |  | ||||||
| 	// ErrorNotFound - this is error returned when jenkins build couldn't be found
 |  | ||||||
| 	ErrorNotFound = fmt.Errorf("404") |  | ||||||
| 	// BuildRetires - determines max amount of retires for failed build
 |  | ||||||
| 	BuildRetires = 3 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // Jobs defines Jobs API tailored for operator sdk
 |  | ||||||
| type Jobs struct { |  | ||||||
| 	jenkinsClient client.Jenkins |  | ||||||
| 	logger        logr.Logger |  | ||||||
| 	k8sClient     k8s.Client |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // New creates jobs client
 |  | ||||||
| func New(jenkinsClient client.Jenkins, k8sClient k8s.Client, logger logr.Logger) *Jobs { |  | ||||||
| 	return &Jobs{ |  | ||||||
| 		jenkinsClient: jenkinsClient, |  | ||||||
| 		k8sClient:     k8sClient, |  | ||||||
| 		logger:        logger, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // EnsureBuildJob function takes care of jenkins build lifecycle according to the lifecycle of reconciliation loop
 |  | ||||||
| // implementation guarantees that jenkins build can be properly handled even after operator pod restart
 |  | ||||||
| // entire state is saved in Jenkins.Status.Builds section
 |  | ||||||
| // function return 'true' when build finished successfully or false when reconciliation loop should requeue this function
 |  | ||||||
| // preserveStatus determines that build won't be removed from Jenkins.Status.Builds section
 |  | ||||||
| func (jobs *Jobs) EnsureBuildJob(jobName, hash string, parameters map[string]string, jenkins *v1alpha2.Jenkins, preserveStatus bool) (done bool, err error) { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring build, name:'%s' hash:'%s'", jobName, hash)) |  | ||||||
| 
 |  | ||||||
| 	build := jobs.getBuildFromStatus(jobName, hash, jenkins) |  | ||||||
| 	if build != nil { |  | ||||||
| 		jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Build exists in status, %+v", build)) |  | ||||||
| 		switch build.Status { |  | ||||||
| 		case v1alpha2.BuildSuccessStatus: |  | ||||||
| 			return jobs.ensureSuccessBuild(*build, jenkins, preserveStatus) |  | ||||||
| 		case v1alpha2.BuildRunningStatus: |  | ||||||
| 			return jobs.ensureRunningBuild(*build, jenkins, preserveStatus) |  | ||||||
| 		case v1alpha2.BuildUnstableStatus, v1alpha2.BuildNotBuildStatus, v1alpha2.BuildFailureStatus, v1alpha2.BuildAbortedStatus: |  | ||||||
| 			return jobs.ensureFailedBuild(*build, jenkins, parameters, preserveStatus) |  | ||||||
| 		case v1alpha2.BuildExpiredStatus: |  | ||||||
| 			return jobs.ensureExpiredBuild(*build, jenkins, preserveStatus) |  | ||||||
| 		default: |  | ||||||
| 			jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Unexpected build status, %+v", build)) |  | ||||||
| 			return false, ErrorUnexpectedBuildStatus |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// build is run first time - build job and update status
 |  | ||||||
| 	created := metav1.Now() |  | ||||||
| 	newBuild := v1alpha2.Build{ |  | ||||||
| 		JobName:    jobName, |  | ||||||
| 		Hash:       hash, |  | ||||||
| 		CreateTime: &created, |  | ||||||
| 	} |  | ||||||
| 	return jobs.buildJob(newBuild, parameters, jenkins) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) getBuildFromStatus(jobName string, hash string, jenkins *v1alpha2.Jenkins) *v1alpha2.Build { |  | ||||||
| 	if jenkins != nil { |  | ||||||
| 		builds := jenkins.Status.Builds |  | ||||||
| 		for _, build := range builds { |  | ||||||
| 			if build.JobName == jobName && build.Hash == hash { |  | ||||||
| 				return &build |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) ensureSuccessBuild(build v1alpha2.Build, jenkins *v1alpha2.Jenkins, preserveStatus bool) (bool, error) { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring success build, %+v", build)) |  | ||||||
| 
 |  | ||||||
| 	if !preserveStatus { |  | ||||||
| 		err := jobs.removeBuildFromStatus(build, jenkins) |  | ||||||
| 		if err != nil { |  | ||||||
| 			jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) |  | ||||||
| 			return false, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return true, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) ensureRunningBuild(build v1alpha2.Build, jenkins *v1alpha2.Jenkins, preserveStatus bool) (bool, error) { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring running build, %+v", build)) |  | ||||||
| 	// FIXME (antoniaklja) implement build expiration
 |  | ||||||
| 
 |  | ||||||
| 	jenkinsBuild, err := jobs.jenkinsClient.GetBuild(build.JobName, build.Number) |  | ||||||
| 	if isNotFoundError(err) { |  | ||||||
| 		jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Build still running , %+v", build)) |  | ||||||
| 		return false, nil |  | ||||||
| 	} else if err != nil { |  | ||||||
| 		jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't get jenkins build, %+v", build)) |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if jenkinsBuild.GetResult() != "" { |  | ||||||
| 		build.Status = v1alpha2.BuildStatus(strings.ToLower(jenkinsBuild.GetResult())) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	err = jobs.updateBuildStatus(build, jenkins) |  | ||||||
| 	if err != nil { |  | ||||||
| 		jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Couldn't update build status, %+v", build)) |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if build.Status == v1alpha2.BuildSuccessStatus { |  | ||||||
| 		jobs.logger.Info(fmt.Sprintf("Build finished successfully, %+v", build)) |  | ||||||
| 		return true, nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if build.Status == v1alpha2.BuildFailureStatus || build.Status == v1alpha2.BuildUnstableStatus || |  | ||||||
| 		build.Status == v1alpha2.BuildNotBuildStatus || build.Status == v1alpha2.BuildAbortedStatus { |  | ||||||
| 		jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Build failed, %+v", build)) |  | ||||||
| 		return false, ErrorBuildFailed |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return false, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) ensureFailedBuild(build v1alpha2.Build, jenkins *v1alpha2.Jenkins, parameters map[string]string, preserveStatus bool) (bool, error) { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring failed build, %+v", build)) |  | ||||||
| 
 |  | ||||||
| 	if build.Retires < BuildRetires { |  | ||||||
| 		jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Retrying build, %+v", build)) |  | ||||||
| 		build.Retires = build.Retires + 1 |  | ||||||
| 		_, err := jobs.buildJob(build, parameters, jenkins) |  | ||||||
| 		if err != nil { |  | ||||||
| 			jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't retry build, %+v", build)) |  | ||||||
| 			return false, err |  | ||||||
| 		} |  | ||||||
| 		return false, nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	lastFailedBuild, err := jobs.jenkinsClient.GetBuild(build.JobName, build.Number) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
| 	jobs.logger.V(log.VWarn).Info(fmt.Sprintf("The retries limit was reached, build %+v, logs: %s", build, lastFailedBuild.GetConsoleOutput())) |  | ||||||
| 
 |  | ||||||
| 	if !preserveStatus { |  | ||||||
| 		err := jobs.removeBuildFromStatus(build, jenkins) |  | ||||||
| 		if err != nil { |  | ||||||
| 			jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) |  | ||||||
| 			return false, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false, ErrorUnrecoverableBuildFailed |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) ensureExpiredBuild(build v1alpha2.Build, jenkins *v1alpha2.Jenkins, preserveStatus bool) (bool, error) { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Ensuring expired build, %+v", build)) |  | ||||||
| 
 |  | ||||||
| 	jenkinsBuild, err := jobs.jenkinsClient.GetBuild(build.JobName, build.Number) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	_, err = jenkinsBuild.Stop() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	jenkinsBuild, err = jobs.jenkinsClient.GetBuild(build.JobName, build.Number) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if v1alpha2.BuildStatus(jenkinsBuild.GetResult()) != v1alpha2.BuildAbortedStatus { |  | ||||||
| 		return false, ErrorAbortBuildFailed |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	err = jobs.updateBuildStatus(build, jenkins) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// TODO(antoniaklja) clean up k8s resources
 |  | ||||||
| 
 |  | ||||||
| 	if !preserveStatus { |  | ||||||
| 		err = jobs.removeBuildFromStatus(build, jenkins) |  | ||||||
| 		if err != nil { |  | ||||||
| 			jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't remove build from status, %+v", build)) |  | ||||||
| 			return false, err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return true, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) removeBuildFromStatus(build v1alpha2.Build, jenkins *v1alpha2.Jenkins) error { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Removing build from status, %+v", build)) |  | ||||||
| 	builds := make([]v1alpha2.Build, len(jenkins.Status.Builds)) |  | ||||||
| 	for _, existingBuild := range jenkins.Status.Builds { |  | ||||||
| 		if existingBuild.JobName != build.JobName && existingBuild.Hash != build.Hash { |  | ||||||
| 			builds = append(builds, existingBuild) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	jenkins.Status.Builds = builds |  | ||||||
| 	err := jobs.k8sClient.Update(context.TODO(), jenkins) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err // don't wrap because apierrors.IsConflict(err) won't work in jenkins_controller
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) buildJob(build v1alpha2.Build, parameters map[string]string, jenkins *v1alpha2.Jenkins) (bool, error) { |  | ||||||
| 	jobs.logger.Info(fmt.Sprintf("Running job, %+v", build)) |  | ||||||
| 	job, err := jobs.jenkinsClient.GetJob(build.JobName) |  | ||||||
| 	if err != nil { |  | ||||||
| 		jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't find jenkins job, %+v", build)) |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 	nextBuildNumber := job.GetDetails().NextBuildNumber |  | ||||||
| 
 |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Running build, %+v", build)) |  | ||||||
| 	_, err = jobs.jenkinsClient.BuildJob(build.JobName, parameters) |  | ||||||
| 	if err != nil { |  | ||||||
| 		jobs.logger.V(log.VWarn).Info(fmt.Sprintf("Couldn't run build, %+v", build)) |  | ||||||
| 		return false, errors.WithStack(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	build.Status = v1alpha2.BuildRunningStatus |  | ||||||
| 	build.Number = nextBuildNumber |  | ||||||
| 
 |  | ||||||
| 	err = jobs.updateBuildStatus(build, jenkins) |  | ||||||
| 	if err != nil { |  | ||||||
| 		jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Couldn't update build status, %+v", build)) |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
| 	return false, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (jobs *Jobs) updateBuildStatus(build v1alpha2.Build, jenkins *v1alpha2.Jenkins) error { |  | ||||||
| 	jobs.logger.V(log.VDebug).Info(fmt.Sprintf("Updating build status, %+v", build)) |  | ||||||
| 	// get index of existing build from status if exists
 |  | ||||||
| 	buildIndex := -1 |  | ||||||
| 	for index, existingBuild := range jenkins.Status.Builds { |  | ||||||
| 		if build.JobName == existingBuild.JobName && build.Hash == existingBuild.Hash { |  | ||||||
| 			buildIndex = index |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// update build status
 |  | ||||||
| 	now := metav1.Now() |  | ||||||
| 	build.LastUpdateTime = &now |  | ||||||
| 	if buildIndex >= 0 { |  | ||||||
| 		jenkins.Status.Builds[buildIndex] = build |  | ||||||
| 	} else { |  | ||||||
| 		build.CreateTime = &now |  | ||||||
| 		jenkins.Status.Builds = append(jenkins.Status.Builds, build) |  | ||||||
| 	} |  | ||||||
| 	err := jobs.k8sClient.Update(context.TODO(), jenkins) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err // don't wrap because apierrors.IsConflict(err) won't work in jenkins_controller
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isNotFoundError(err error) bool { |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err.Error() == ErrorNotFound.Error() |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  | @ -1,434 +0,0 @@ | ||||||
| package jobs |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"crypto/sha256" |  | ||||||
| 	"encoding/base64" |  | ||||||
| 	"fmt" |  | ||||||
| 	"testing" |  | ||||||
| 
 |  | ||||||
| 	"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" |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| 	corev1 "k8s.io/api/core/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/api/resource" |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |  | ||||||
| 	"k8s.io/apimachinery/pkg/types" |  | ||||||
| 	"k8s.io/client-go/kubernetes/scheme" |  | ||||||
| 	"sigs.k8s.io/controller-runtime/pkg/client/fake" |  | ||||||
| 	logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func TestSuccessEnsureJob(t *testing.T) { |  | ||||||
| 	// given
 |  | ||||||
| 	ctx := context.TODO() |  | ||||||
| 	logger := logf.ZapLogger(false) |  | ||||||
| 	ctrl := gomock.NewController(t) |  | ||||||
| 	defer ctrl.Finish() |  | ||||||
| 
 |  | ||||||
| 	jobName := "Test Job" |  | ||||||
| 	hash := sha256.New() |  | ||||||
| 	hash.Write([]byte(jobName)) |  | ||||||
| 	encodedHash := base64.URLEncoding.EncodeToString(hash.Sum(nil)) |  | ||||||
| 
 |  | ||||||
| 	// when
 |  | ||||||
| 	jenkins := jenkinsCustomResource() |  | ||||||
| 	fakeClient := fake.NewFakeClient() |  | ||||||
| 	err := v1alpha2.SchemeBuilder.AddToScheme(scheme.Scheme) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	err = fakeClient.Create(ctx, jenkins) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 	for reconcileAttempt := 1; reconcileAttempt <= 2; reconcileAttempt++ { |  | ||||||
| 		logger.Info(fmt.Sprintf("Reconcile attempt #%d", reconcileAttempt)) |  | ||||||
| 		buildNumber := int64(1) |  | ||||||
| 		jenkinsClient := client.NewMockJenkins(ctrl) |  | ||||||
| 		jobs := New(jenkinsClient, fakeClient, logger) |  | ||||||
| 
 |  | ||||||
| 		jenkinsClient. |  | ||||||
| 			EXPECT(). |  | ||||||
| 			GetJob(jobName). |  | ||||||
| 			Return(&gojenkins.Job{ |  | ||||||
| 				Raw: &gojenkins.JobResponse{ |  | ||||||
| 					NextBuildNumber: buildNumber, |  | ||||||
| 				}, |  | ||||||
| 			}, nil).AnyTimes() |  | ||||||
| 
 |  | ||||||
| 		jenkinsClient. |  | ||||||
| 			EXPECT(). |  | ||||||
| 			BuildJob(jobName, gomock.Any()). |  | ||||||
| 			Return(int64(0), nil).AnyTimes() |  | ||||||
| 
 |  | ||||||
| 		jenkinsClient. |  | ||||||
| 			EXPECT(). |  | ||||||
| 			GetBuild(jobName, buildNumber). |  | ||||||
| 			Return(&gojenkins.Build{ |  | ||||||
| 				Raw: &gojenkins.BuildResponse{ |  | ||||||
| 					Result: string(v1alpha2.BuildSuccessStatus), |  | ||||||
| 				}, |  | ||||||
| 			}, nil).AnyTimes() |  | ||||||
| 
 |  | ||||||
| 		done, err := jobs.EnsureBuildJob(jobName, encodedHash, nil, jenkins, true) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		assert.NotEmpty(t, jenkins.Status.Builds) |  | ||||||
| 		assert.Equal(t, len(jenkins.Status.Builds), 1) |  | ||||||
| 
 |  | ||||||
| 		build := jenkins.Status.Builds[0] |  | ||||||
| 		assert.Equal(t, build.JobName, jobName) |  | ||||||
| 		assert.Equal(t, build.Hash, encodedHash) |  | ||||||
| 		assert.Equal(t, build.Number, buildNumber) |  | ||||||
| 		assert.Equal(t, build.Retires, 0) |  | ||||||
| 		assert.NotNil(t, build.CreateTime) |  | ||||||
| 		assert.NotNil(t, build.LastUpdateTime) |  | ||||||
| 
 |  | ||||||
| 		// first run - build should be scheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 1 { |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildRunningStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// second run -job should be success and status updated
 |  | ||||||
| 		if reconcileAttempt == 2 { |  | ||||||
| 			assert.True(t, done) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildSuccessStatus) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestEnsureJobWithFailedBuild(t *testing.T) { |  | ||||||
| 	// given
 |  | ||||||
| 	ctx := context.TODO() |  | ||||||
| 	logger := logf.ZapLogger(false) |  | ||||||
| 	ctrl := gomock.NewController(t) |  | ||||||
| 	defer ctrl.Finish() |  | ||||||
| 
 |  | ||||||
| 	jobName := "Test Job" |  | ||||||
| 	hash := sha256.New() |  | ||||||
| 	hash.Write([]byte(jobName)) |  | ||||||
| 	encodedHash := base64.URLEncoding.EncodeToString(hash.Sum(nil)) |  | ||||||
| 
 |  | ||||||
| 	// when
 |  | ||||||
| 	jenkins := jenkinsCustomResource() |  | ||||||
| 	fakeClient := fake.NewFakeClient() |  | ||||||
| 	err := fakeClient.Create(ctx, jenkins) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 	for reconcileAttempt := 1; reconcileAttempt <= 4; reconcileAttempt++ { |  | ||||||
| 		logger.Info(fmt.Sprintf("Reconcile attempt #%d", reconcileAttempt)) |  | ||||||
| 		jenkinsClient := client.NewMockJenkins(ctrl) |  | ||||||
| 		jobs := New(jenkinsClient, fakeClient, logger) |  | ||||||
| 
 |  | ||||||
| 		// first run - build should be scheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 1 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetJob(jobName). |  | ||||||
| 				Return(&gojenkins.Job{ |  | ||||||
| 					Raw: &gojenkins.JobResponse{ |  | ||||||
| 						NextBuildNumber: int64(1), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 
 |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				BuildJob(jobName, gomock.Any()). |  | ||||||
| 				Return(int64(0), nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// second run - build should be failure and status updated
 |  | ||||||
| 		if reconcileAttempt == 2 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetBuild(jobName, int64(1)). |  | ||||||
| 				Return(&gojenkins.Build{ |  | ||||||
| 					Raw: &gojenkins.BuildResponse{ |  | ||||||
| 						Result: string(v1alpha2.BuildFailureStatus), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// third run - build should be rescheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 3 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetJob(jobName). |  | ||||||
| 				Return(&gojenkins.Job{ |  | ||||||
| 					Raw: &gojenkins.JobResponse{ |  | ||||||
| 						NextBuildNumber: int64(2), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 
 |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				BuildJob(jobName, gomock.Any()). |  | ||||||
| 				Return(int64(0), nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fourth run - build should be success and status updated
 |  | ||||||
| 		if reconcileAttempt == 4 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetBuild(jobName, int64(2)). |  | ||||||
| 				Return(&gojenkins.Build{ |  | ||||||
| 					Raw: &gojenkins.BuildResponse{ |  | ||||||
| 						Result: string(v1alpha2.BuildSuccessStatus), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		done, errEnsureBuildJob := jobs.EnsureBuildJob(jobName, encodedHash, nil, jenkins, true) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		assert.NotEmpty(t, jenkins.Status.Builds) |  | ||||||
| 		assert.Equal(t, len(jenkins.Status.Builds), 1) |  | ||||||
| 
 |  | ||||||
| 		build := jenkins.Status.Builds[0] |  | ||||||
| 		assert.Equal(t, build.JobName, jobName) |  | ||||||
| 		assert.Equal(t, build.Hash, encodedHash) |  | ||||||
| 
 |  | ||||||
| 		assert.NotNil(t, build.CreateTime) |  | ||||||
| 		assert.NotNil(t, build.LastUpdateTime) |  | ||||||
| 
 |  | ||||||
| 		// first run - build should be scheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 1 { |  | ||||||
| 			assert.NoError(t, errEnsureBuildJob) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(1)) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildRunningStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// second run - build should be failure and status updated
 |  | ||||||
| 		if reconcileAttempt == 2 { |  | ||||||
| 			assert.Error(t, errEnsureBuildJob) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(1)) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildFailureStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// third run - build should be rescheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 3 { |  | ||||||
| 			assert.NoError(t, errEnsureBuildJob) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(2)) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildRunningStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fourth run - build should be success and status updated
 |  | ||||||
| 		if reconcileAttempt == 4 { |  | ||||||
| 			assert.NoError(t, errEnsureBuildJob) |  | ||||||
| 			assert.True(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(2)) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildSuccessStatus) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestEnsureJobFailedWithMaxRetries(t *testing.T) { |  | ||||||
| 	// given
 |  | ||||||
| 	ctx := context.TODO() |  | ||||||
| 	logger := logf.ZapLogger(false) |  | ||||||
| 	ctrl := gomock.NewController(t) |  | ||||||
| 	defer ctrl.Finish() |  | ||||||
| 
 |  | ||||||
| 	buildName := "Test Job" |  | ||||||
| 	hash := sha256.New() |  | ||||||
| 	hash.Write([]byte(buildName)) |  | ||||||
| 	encodedHash := base64.URLEncoding.EncodeToString(hash.Sum(nil)) |  | ||||||
| 
 |  | ||||||
| 	// when
 |  | ||||||
| 	jenkins := jenkinsCustomResource() |  | ||||||
| 	fakeClient := fake.NewFakeClient() |  | ||||||
| 	err := fakeClient.Create(ctx, jenkins) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 	BuildRetires = 1 // override max build retries
 |  | ||||||
| 	for reconcileAttempt := 1; reconcileAttempt <= 5; reconcileAttempt++ { |  | ||||||
| 		logger.Info(fmt.Sprintf("Reconcile attempt #%d", reconcileAttempt)) |  | ||||||
| 		jenkinsClient := client.NewMockJenkins(ctrl) |  | ||||||
| 		jobs := New(jenkinsClient, fakeClient, logger) |  | ||||||
| 
 |  | ||||||
| 		// first run - build should be scheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 1 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetJob(buildName). |  | ||||||
| 				Return(&gojenkins.Job{ |  | ||||||
| 					Raw: &gojenkins.JobResponse{ |  | ||||||
| 						NextBuildNumber: int64(1), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 
 |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				BuildJob(buildName, gomock.Any()). |  | ||||||
| 				Return(int64(0), nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// second run - build should be failure and status updated
 |  | ||||||
| 		if reconcileAttempt == 2 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetBuild(buildName, int64(1)). |  | ||||||
| 				Return(&gojenkins.Build{ |  | ||||||
| 					Raw: &gojenkins.BuildResponse{ |  | ||||||
| 						Result: string(v1alpha2.BuildFailureStatus), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// third run - build should be rescheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 3 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetJob(buildName). |  | ||||||
| 				Return(&gojenkins.Job{ |  | ||||||
| 					Raw: &gojenkins.JobResponse{ |  | ||||||
| 						NextBuildNumber: int64(2), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 
 |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				BuildJob(buildName, gomock.Any()). |  | ||||||
| 				Return(int64(0), nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fourth run - build should be success and status updated
 |  | ||||||
| 		if reconcileAttempt == 4 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetBuild(buildName, int64(2)). |  | ||||||
| 				Return(&gojenkins.Build{ |  | ||||||
| 					Raw: &gojenkins.BuildResponse{ |  | ||||||
| 						Result: string(v1alpha2.BuildFailureStatus), |  | ||||||
| 					}, |  | ||||||
| 				}, nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fifth run - build should be unrecoverable failed and status updated
 |  | ||||||
| 		if reconcileAttempt == 5 { |  | ||||||
| 			jenkinsClient. |  | ||||||
| 				EXPECT(). |  | ||||||
| 				GetBuild(buildName, int64(2)). |  | ||||||
| 				Return(&gojenkins.Build{ |  | ||||||
| 					Raw: &gojenkins.BuildResponse{ |  | ||||||
| 						Result: string(v1alpha2.BuildFailureStatus), |  | ||||||
| 					}, |  | ||||||
| 					Jenkins: gojenkins.CreateJenkins(nil, ""), |  | ||||||
| 				}, nil) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		done, errEnsureBuildJob := jobs.EnsureBuildJob(buildName, encodedHash, nil, jenkins, true) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		err = fakeClient.Get(ctx, types.NamespacedName{Name: jenkins.Name, Namespace: jenkins.Namespace}, jenkins) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 		assert.NotEmpty(t, jenkins.Status.Builds) |  | ||||||
| 		assert.Equal(t, len(jenkins.Status.Builds), 1) |  | ||||||
| 
 |  | ||||||
| 		build := jenkins.Status.Builds[0] |  | ||||||
| 		assert.Equal(t, build.JobName, buildName) |  | ||||||
| 		assert.Equal(t, build.Hash, encodedHash) |  | ||||||
| 
 |  | ||||||
| 		assert.NotNil(t, build.CreateTime) |  | ||||||
| 		assert.NotNil(t, build.LastUpdateTime) |  | ||||||
| 
 |  | ||||||
| 		// first run - build should be scheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 1 { |  | ||||||
| 			assert.NoError(t, errEnsureBuildJob) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(1)) |  | ||||||
| 			assert.Equal(t, build.Retires, 0) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildRunningStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// second run - build should be failure and status updated
 |  | ||||||
| 		if reconcileAttempt == 2 { |  | ||||||
| 			assert.EqualError(t, errEnsureBuildJob, ErrorBuildFailed.Error()) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(1)) |  | ||||||
| 			assert.Equal(t, build.Retires, 0) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildFailureStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// third run - build should be rescheduled and status updated
 |  | ||||||
| 		if reconcileAttempt == 3 { |  | ||||||
| 			assert.NoError(t, errEnsureBuildJob) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			//assert.Equal(t, build.Retires, 1)
 |  | ||||||
| 			assert.Equal(t, build.Number, int64(2)) |  | ||||||
| 			assert.Equal(t, build.Retires, 1) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildRunningStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fourth run - build should be failure and status updated
 |  | ||||||
| 		if reconcileAttempt == 4 { |  | ||||||
| 			assert.EqualError(t, errEnsureBuildJob, ErrorBuildFailed.Error()) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(2)) |  | ||||||
| 			assert.Equal(t, build.Retires, 1) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildFailureStatus) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// fifth run - build should be unrecoverable failed and status updated
 |  | ||||||
| 		if reconcileAttempt == 5 { |  | ||||||
| 			assert.EqualError(t, errEnsureBuildJob, ErrorUnrecoverableBuildFailed.Error()) |  | ||||||
| 			assert.False(t, done) |  | ||||||
| 			assert.Equal(t, build.Number, int64(2)) |  | ||||||
| 			assert.Equal(t, build.Retires, 1) |  | ||||||
| 			assert.Equal(t, build.Status, v1alpha2.BuildFailureStatus) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func jenkinsCustomResource() *v1alpha2.Jenkins { |  | ||||||
| 	return &v1alpha2.Jenkins{ |  | ||||||
| 		ObjectMeta: metav1.ObjectMeta{ |  | ||||||
| 			Name:      "jenkins", |  | ||||||
| 			Namespace: "default", |  | ||||||
| 		}, |  | ||||||
| 		Spec: v1alpha2.JenkinsSpec{ |  | ||||||
| 			Master: v1alpha2.JenkinsMaster{ |  | ||||||
| 				Annotations: map[string]string{"test": "label"}, |  | ||||||
| 				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"), |  | ||||||
| 							}, |  | ||||||
| 						}, |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			SeedJobs: []v1alpha2.SeedJob{ |  | ||||||
| 				{ |  | ||||||
| 					ID:                    "jenkins-operator-e2e", |  | ||||||
| 					JenkinsCredentialType: v1alpha2.NoJenkinsCredentialCredentialType, |  | ||||||
| 					Targets:               "cicd/jobs/*.jenkins", |  | ||||||
| 					Description:           "Jenkins Operator e2e tests repository", |  | ||||||
| 					RepositoryBranch:      "master", |  | ||||||
| 					RepositoryURL:         "https://github.com/jenkinsci/kubernetes-operator.git", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue