pkg/controller/jenkins_job/chain/put_jenkins_pipeline.go (213 lines of code) (raw):

package chain import ( "bytes" "context" "fmt" "text/template" "time" "github.com/go-logr/logr" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" cdPipeApi "github.com/epam/edp-cd-pipeline-operator/v2/pkg/apis/edp/v1" codebaseApi "github.com/epam/edp-codebase-operator/v2/pkg/apis/edp/v1" jenkinsApi "github.com/epam/edp-jenkins-operator/v2/pkg/apis/v2/v1" jenkinsClient "github.com/epam/edp-jenkins-operator/v2/pkg/client/jenkins" jobhandler "github.com/epam/edp-jenkins-operator/v2/pkg/controller/jenkins_job/chain/handler" "github.com/epam/edp-jenkins-operator/v2/pkg/service/platform" "github.com/epam/edp-jenkins-operator/v2/pkg/util/consts" plutil "github.com/epam/edp-jenkins-operator/v2/pkg/util/platform" ) const ( logNameKey = "name" ) type PutJenkinsPipeline struct { next jobhandler.JenkinsJobHandler client client.Client ps platform.PlatformService log logr.Logger } func (h PutJenkinsPipeline) ServeRequest(jj *jenkinsApi.JenkinsJob) error { h.log.Info("start creating Jenkins CD Pipeline") if err := h.setStatus(jj, consts.StatusInProgress, jenkinsApi.CreateJenkinsPipeline, nil); err != nil { return fmt.Errorf("failed to set status: %w", err) } if err := h.tryToCreateJob(jj); err != nil { if setStatusErr := h.setStatus(jj, consts.StatusFailed, jenkinsApi.CreateJenkinsPipeline, err); setStatusErr != nil { return setStatusErr } return err } if err := h.setStatus(jj, consts.StatusFinished, jenkinsApi.CreateJenkinsPipeline, nil); err != nil { return err } h.log.Info("end creating Jenkins CD Pipeline") return nextServeOrNil(h.next, jj) } func (h PutJenkinsPipeline) tryToCreateJob(jj *jenkinsApi.JenkinsJob) error { jc, err := h.initGoJenkinsClient(jj) if err != nil { return err } s, err := plutil.GetStageInstanceOwner(h.client, jj) if err != nil { return fmt.Errorf("failed to get StageInstanceOwner: %w", err) } json, err := h.ps.CreateStageJSON(s) if err != nil { return fmt.Errorf("failed to create StageJSON: %w", err) } conf, err := h.createStageConfig(s, json, jj.Spec.Job.Config) if err != nil { return err } if err := h.createJob(jc, conf, jj); err != nil { return fmt.Errorf("failed to create jenkins job: %w", err) } h.log.Info("job has been created", logNameKey, jj.Spec.Job.Name) return nil } func (h PutJenkinsPipeline) createJob(jc *jenkinsClient.JenkinsClient, conf *string, jj *jenkinsApi.JenkinsJob) error { if jj.Spec.JenkinsFolder != nil && *jj.Spec.JenkinsFolder != "" { pfn := fmt.Sprintf("%v-%v", *jj.Spec.JenkinsFolder, "cd-pipeline") _, err := jc.GoJenkins.CreateJobInFolder(*conf, jj.Spec.Job.Name, pfn) if err != nil { return fmt.Errorf("failed to create job in folder: %w", err) } h.log.Info("job has been created", logNameKey, fmt.Sprintf("%v/%v", pfn, jj.Spec.Job.Name)) return nil } if _, err := jc.GoJenkins.CreateJob(*conf, jj.Spec.Job.Name); err != nil { return fmt.Errorf("failed to create job: %w", err) } h.log.Info("job has been created", logNameKey, jj.Spec.Job.Name) return nil } func (h PutJenkinsPipeline) initGoJenkinsClient(jj *jenkinsApi.JenkinsJob) (*jenkinsClient.JenkinsClient, error) { j, err := plutil.GetJenkinsInstanceOwner(h.client, jj.Name, jj.Namespace, jj.Spec.OwnerName, jj.GetOwnerReferences()) if err != nil { return nil, fmt.Errorf("failed to get owner jenkins for jenkins job %v: %w", jj.Name, err) } h.log.Info("Jenkins instance has been created", logNameKey, j.Name) jClient, err := jenkinsClient.InitGoJenkinsClient(j, h.ps) if err != nil { return nil, fmt.Errorf("failed to init GoJenkinsClient: %w", err) } return jClient, nil } func (h PutJenkinsPipeline) createStageConfig(s *cdPipeApi.Stage, ps, conf string) (*string, error) { pipeSrc := map[string]interface{}{ "type": "default", "library": map[string]string{}, } if s.Spec.Source.Type == consts.LibraryCodebase { h.setPipeSrcParams(s, pipeSrc) } tmpl, err := template.New("cd-pipeline.tmpl").Parse(conf) if err != nil { return nil, fmt.Errorf("failed to create new template: %w", err) } params := map[string]interface{}{ "name": s.Spec.Name, "gitServerCrVersion": "v2", "pipelineStages": ps, "source": pipeSrc, } var cdPipelineBuffer bytes.Buffer if err := tmpl.Execute(&cdPipelineBuffer, params); err != nil { return nil, fmt.Errorf("failed to execute: %w", err) } pipeConf := cdPipelineBuffer.String() return &pipeConf, nil } func (h PutJenkinsPipeline) setPipeSrcParams(stage *cdPipeApi.Stage, pipeSrc map[string]interface{}) { cb, err := h.getLibraryParams(stage.Spec.Source.Library.Name, stage.Namespace) if err != nil { h.log.Error(err, "couldn't retrieve parameters for pipeline's library, default source type will be used", "Library name", stage.Spec.Source.Library.Name) return } gs, err := h.getGitServerParams(cb.Spec.GitServer, stage.Namespace) if err != nil { h.log.Error(err, "couldn't retrieve parameters for git server, default source type will be used", "Git server", cb.Spec.GitServer) return } pipeSrc["type"] = "library" pipeSrc["library"] = map[string]string{ "url": fmt.Sprintf("ssh://%v@%v:%v%v", gs.Spec.GitUser, gs.Spec.GitHost, gs.Spec.SshPort, getPathToRepository(string(cb.Spec.Strategy), stage.Spec.Source.Library.Name, cb.Spec.GitUrlPath)), "credentials": gs.Spec.NameSshKeySecret, "branch": stage.Spec.Source.Library.Branch, } } func (h PutJenkinsPipeline) getLibraryParams(name, namespace string) (*codebaseApi.Codebase, error) { namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } codebase := &codebaseApi.Codebase{} if err := h.client.Get(context.TODO(), namespacedName, codebase); err != nil { return nil, fmt.Errorf("failed to get Codebase: %w", err) } return codebase, nil } func (h PutJenkinsPipeline) getGitServerParams(name, namespace string) (*codebaseApi.GitServer, error) { namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } gitServer := &codebaseApi.GitServer{} if err := h.client.Get(context.TODO(), namespacedName, gitServer); err != nil { return nil, fmt.Errorf("failed to get GitServer: %w", err) } return gitServer, nil } func getPathToRepository(strategy, name string, url *string) string { if strategy == consts.ImportStrategy { return *url } return "/" + name } func (h PutJenkinsPipeline) setStatus(jj *jenkinsApi.JenkinsJob, status string, action jenkinsApi.ActionType, err error) error { jj.Status = jenkinsApi.JenkinsJobStatus{ Status: status, Available: status == consts.StatusFinished, LastTimeUpdated: metav1.NewTime(time.Now()), Action: action, Result: getResult(status), Username: "system", Value: getValue(status), } if err != nil { errV := err jj.Status.DetailedMessage = errV.Error() } return updateStatus(h.client, jj) } func getResult(status string) jenkinsApi.Result { if status == consts.StatusFailed { return jenkinsApi.Error } return jenkinsApi.Success } func getValue(status string) string { if status == consts.StatusFinished { return "active" } if status == consts.StatusFailed { return "failed" } return "inactive" } func updateStatus(c client.Client, jj *jenkinsApi.JenkinsJob) error { if err := c.Status().Update(context.TODO(), jj); err != nil { if err := c.Update(context.TODO(), jj); err != nil { return fmt.Errorf("failed to update jenkins job status: %w", err) } } log.Info("JenkinsJob status has been updated", logNameKey, jj.Name) return nil }