diff --git a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_jenkins.go b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_jenkins.go index 3a5857fabe..7b150b8b1f 100644 --- a/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_jenkins.go +++ b/pkg/microservice/aslan/core/common/service/workflowcontroller/jobcontroller/job_jenkins.go @@ -27,6 +27,7 @@ import ( "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" + jenkinsclient "github.com/koderover/zadig/v2/pkg/tool/jenkins" "github.com/koderover/zadig/v2/pkg/tool/log" "go.uber.org/zap" ) @@ -76,11 +77,20 @@ func (c *JenkinsJobCtl) Run(ctx context.Context) { return } - job, err := jenkinsClient.GetJob(context.TODO(), c.jobTaskSpec.Job.JobName) + job := &jenkins.Job{ + Jenkins: jenkinsClient, + Raw: new(jenkins.JobResponse), + Base: jenkinsclient.JobPath(c.jobTaskSpec.Job.JobName), + } + status, err := job.Poll(context.TODO()) if err != nil { logError(c.job, fmt.Sprintf("failed to get jenkins job, error is: %s", err), c.logger) return } + if status != http.StatusOK { + logError(c.job, fmt.Sprintf("failed to get jenkins job, error is: %d", status), c.logger) + return + } params := make(map[string]string) for _, parameter := range c.jobTaskSpec.Job.Parameters { @@ -94,7 +104,7 @@ func (c *JenkinsJobCtl) Run(ctx context.Context) { return } - build, err := jenkinsClient.GetBuildFromQueueID(context.TODO(), queueid) + build, err := getJenkinsBuildFromQueueID(context.TODO(), jenkinsClient, job, queueid) if err != nil { logError(c.job, fmt.Sprintf("failed to get jenkins build, error is: %s", err), c.logger) return @@ -138,6 +148,21 @@ func (c *JenkinsJobCtl) Run(ctx context.Context) { return } +func getJenkinsBuildFromQueueID(ctx context.Context, jenkinsClient *jenkins.Jenkins, job *jenkins.Job, queueID int64) (*jenkins.Build, error) { + task, err := jenkinsClient.GetQueueItem(ctx, queueID) + if err != nil { + return nil, err + } + for task.Raw.Executable.Number == 0 { + time.Sleep(time.Second) + if _, err := task.Poll(ctx); err != nil { + return nil, err + } + } + + return job.GetBuild(ctx, task.Raw.Executable.Number) +} + func (c *JenkinsJobCtl) SaveInfo(ctx context.Context) error { return mongodb.NewJobInfoColl().Create(context.TODO(), &commonmodels.JobInfo{ Type: c.job.JobType, diff --git a/pkg/microservice/aslan/core/system/handler/jenkins.go b/pkg/microservice/aslan/core/system/handler/jenkins.go index 66b59d9c6c..10f31455a3 100644 --- a/pkg/microservice/aslan/core/system/handler/jenkins.go +++ b/pkg/microservice/aslan/core/system/handler/jenkins.go @@ -18,6 +18,7 @@ package handler import ( "fmt" + "strings" "github.com/gin-gonic/gin" "github.com/koderover/zadig/v2/pkg/setting" @@ -80,5 +81,5 @@ func ListJobBuildArgs(c *gin.Context) { ctx := internalhandler.NewContext(c) defer func() { internalhandler.JSONResponse(c, ctx) }() - ctx.Resp, ctx.RespErr = service.ListJobBuildArgs(c.Param("id"), c.Param("jobName"), ctx.Logger) + ctx.Resp, ctx.RespErr = service.ListJobBuildArgs(c.Param("id"), strings.TrimPrefix(c.Param("jobName"), "/"), ctx.Logger) } diff --git a/pkg/microservice/aslan/core/system/handler/router.go b/pkg/microservice/aslan/core/system/handler/router.go index 4183635355..113b4a299d 100644 --- a/pkg/microservice/aslan/core/system/handler/router.go +++ b/pkg/microservice/aslan/core/system/handler/router.go @@ -108,7 +108,7 @@ func (*Router) Inject(router *gin.RouterGroup) { jenkins.GET("/exist", CheckJenkinsIntegration) jenkins.POST("/user/connection", TestJenkinsConnection) jenkins.GET("/jobNames/:id", ListJobNames) - jenkins.GET("/buildArgs/:id/:jobName", ListJobBuildArgs) + jenkins.GET("/buildArgs/:id/*jobName", ListJobBuildArgs) } cicd := router.Group("cicdTools") diff --git a/pkg/microservice/aslan/core/system/service/jenkins.go b/pkg/microservice/aslan/core/system/service/jenkins.go index e0bdd6e53e..d312b1a412 100644 --- a/pkg/microservice/aslan/core/system/service/jenkins.go +++ b/pkg/microservice/aslan/core/system/service/jenkins.go @@ -25,6 +25,7 @@ import ( commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb" e "github.com/koderover/zadig/v2/pkg/tool/errors" + jenkinsclient "github.com/koderover/zadig/v2/pkg/tool/jenkins" "github.com/koderover/zadig/v2/pkg/types" ) @@ -65,42 +66,38 @@ func getJenkinsClient(id string, log *zap.SugaredLogger) (*gojenkins.Jenkins, co } func ListJobNames(id string, log *zap.SugaredLogger) ([]string, error) { - jenkinsClient, ctx, err := getJenkinsClient(id, log) + jenkinsIntegration, err := commonrepo.NewCICDToolColl().Get(id) if err != nil { return []string{}, e.ErrListJobNames.AddErr(err) } - innerJobs, err := jenkinsClient.GetAllJobNames(ctx) + + jenkinsClient := jenkinsclient.NewClient(jenkinsIntegration.URL, jenkinsIntegration.Username, jenkinsIntegration.Password) + jobNames, err := jenkinsClient.ListJobNames() if err != nil { return []string{}, e.ErrListJobNames.AddErr(err) } - jobNames := make([]string, 0) - for _, innerJob := range innerJobs { - jobNames = append(jobNames, innerJob.Name) - } return jobNames, nil } func ListJobBuildArgs(id, jobName string, log *zap.SugaredLogger) ([]*JenkinsBuildArgs, error) { - jenkinsClient, ctx, err := getJenkinsClient(id, log) - if err != nil { - return []*JenkinsBuildArgs{}, e.ErrListJobBuildArgs.AddErr(err) - } - jenkinsJob, err := jenkinsClient.GetJob(ctx, jobName) + jenkinsIntegration, err := commonrepo.NewCICDToolColl().Get(id) if err != nil { return []*JenkinsBuildArgs{}, e.ErrListJobBuildArgs.AddErr(err) } - paramDefinitions, err := jenkinsJob.GetParameters(ctx) + + jenkinsClient := jenkinsclient.NewClient(jenkinsIntegration.URL, jenkinsIntegration.Username, jenkinsIntegration.Password) + jenkinsJob, err := jenkinsClient.GetJob(jobName) if err != nil { return []*JenkinsBuildArgs{}, e.ErrListJobBuildArgs.AddErr(err) } jenkinsBuildArgsResp := make([]*JenkinsBuildArgs, 0) - for _, paramDefinition := range paramDefinitions { + for _, paramDefinition := range jenkinsJob.GetParameters() { arg := &JenkinsBuildArgs{ - Name: paramDefinition.DefaultParameterValue.Name, + Name: paramDefinition.Name, Value: paramDefinition.DefaultParameterValue.Value, - Type: paramDefinition.Type, + Type: string(paramDefinition.Type), } - if paramDefinition.Type == "ChoiceParameterDefinition" { + if paramDefinition.Type == jenkinsclient.Choice { arg.Type = string(types.Choice) } else { arg.Type = string(types.Str) diff --git a/pkg/microservice/aslan/core/workflow/handler/router.go b/pkg/microservice/aslan/core/workflow/handler/router.go index 91d0e0b10d..cb4934cd5d 100644 --- a/pkg/microservice/aslan/core/workflow/handler/router.go +++ b/pkg/microservice/aslan/core/workflow/handler/router.go @@ -114,7 +114,7 @@ func (*Router) Inject(router *gin.RouterGroup) { workflowV4.GET("/mse/offline", GetMseOfflineResources) workflowV4.GET("/mse/:envName/tag", GetMseTagsInEnv) workflowV4.GET("/bluegreen/:envName/:serviceName", GetBlueGreenServiceK8sServiceYaml) - workflowV4.GET("/jenkins/:id/:jobName", GetJenkinsJobParams) + workflowV4.GET("/jenkins/:id/*jobName", GetJenkinsJobParams) workflowV4.POST("/sql/validate", ValidateSQL) workflowV4.POST("/deploy/mergeImage", HelmDeployJobMergeImage) } diff --git a/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go b/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go index a6965ff528..df68a4e8e2 100644 --- a/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go +++ b/pkg/microservice/aslan/core/workflow/handler/workflow_v4.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "strings" "github.com/gin-gonic/gin" "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/util" @@ -1502,7 +1503,7 @@ func GetJenkinsJobParams(c *gin.Context) { ctx := internalhandler.NewContext(c) defer func() { internalhandler.JSONResponse(c, ctx) }() - jobParams, err := workflow.GetJenkinsJobParams(c.Param("id"), c.Param("jobName")) + jobParams, err := workflow.GetJenkinsJobParams(c.Param("id"), strings.TrimPrefix(c.Param("jobName"), "/")) if err != nil { ctx.RespErr = err return diff --git a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_jenkins.go b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_jenkins.go index 1fb2369989..db4c43862d 100644 --- a/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_jenkins.go +++ b/pkg/microservice/aslan/core/workflow/service/workflow/controller/job/job_jenkins.go @@ -84,11 +84,16 @@ func (j JenkinsJobController) Update(useUserInput bool, ticket *commonmodels.App j.errorPolicy = currJob.ErrorPolicy j.executePolicy = currJob.ExecutePolicy + if j.jobSpec.ID != "" && currJobSpec.ID != "" && j.jobSpec.ID != currJobSpec.ID { + j.jobSpec.Jobs = make([]*commonmodels.JenkinsJobInfo, 0) + } j.jobSpec.ID = currJobSpec.ID + j.jobSpec.JobOptions = getJenkinsJobOptions(currJobSpec) + if useUserInput { newJobs := make([]*commonmodels.JenkinsJobInfo, 0) configuredJobMap := make(map[string]*commonmodels.JenkinsJobInfo) - for _, option := range currJobSpec.JobOptions { + for _, option := range j.jobSpec.JobOptions { configuredJobMap[option.JobName] = option } @@ -110,11 +115,12 @@ func (j JenkinsJobController) Update(useUserInput bool, ticket *commonmodels.App } func (j JenkinsJobController) SetOptions(ticket *commonmodels.ApprovalTicket) error { + j.jobSpec.JobOptions = getJenkinsJobOptions(j.jobSpec) return nil } func (j JenkinsJobController) ClearOptions() { - return + j.jobSpec.JobOptions = make([]*commonmodels.JenkinsJobInfo, 0) } func (j JenkinsJobController) ClearSelection() { @@ -186,3 +192,11 @@ func (j JenkinsJobController) RenderDynamicVariableOptions(key string, option *R func (j JenkinsJobController) IsServiceTypeJob() bool { return false } + +func getJenkinsJobOptions(spec *commonmodels.JenkinsJobSpec) []*commonmodels.JenkinsJobInfo { + if len(spec.JobOptions) > 0 { + return spec.JobOptions + } + + return spec.Jobs +} diff --git a/pkg/tool/jenkins/job.go b/pkg/tool/jenkins/job.go index a656d4837e..933433c2d2 100644 --- a/pkg/tool/jenkins/job.go +++ b/pkg/tool/jenkins/job.go @@ -1,6 +1,15 @@ package jenkins -import "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" +import ( + "fmt" + "net/url" + "sort" + "strings" + + "github.com/koderover/zadig/v2/pkg/microservice/aslan/config" +) + +const listJobsTree = "jobs[name,fullName,_class]" type ParameterType string @@ -23,8 +32,9 @@ type ListJobsResp struct { Jobs []Jobs `json:"jobs"` } type Jobs struct { - Class string `json:"_class"` - Name string `json:"name"` + Class string `json:"_class"` + Name string `json:"name"` + FullName string `json:"fullName"` } func (c *Client) ListJob() (resp *ListJobsResp, err error) { @@ -32,6 +42,70 @@ func (c *Client) ListJob() (resp *ListJobsResp, err error) { return } +func (c *Client) ListJobNames() ([]string, error) { + jobNames, err := c.listJobNames("") + if err != nil { + return nil, err + } + sort.Strings(jobNames) + + return jobNames, nil +} + +func (c *Client) listJobNames(parent string) ([]string, error) { + resp := new(ListJobsResp) + jobPath := JobPath(parent) + if parent != "" && jobPath == "" { + return nil, fmt.Errorf("jenkins job name is empty") + } + + urlPath := jobPath + "/api/json?tree=" + listJobsTree + _, err := c.R().SetSuccessResult(resp).Get(urlPath) + if err != nil { + return nil, err + } + + jobNames := make([]string, 0) + for _, job := range resp.Jobs { + fullName := job.FullName + if fullName == "" { + if parent == "" { + fullName = job.Name + } else { + fullName = parent + "/" + job.Name + } + } + + if strings.Contains(job.Class, ".folder.") || strings.Contains(job.Class, "Folder") { + childJobNames, err := c.listJobNames(fullName) + if err != nil { + return nil, err + } + jobNames = append(jobNames, childJobNames...) + continue + } + + jobNames = append(jobNames, fullName) + } + return jobNames, nil +} + +func JobPath(name string) string { + parts := strings.Split(strings.Trim(name, "/"), "/") + escapedParts := make([]string, 0, len(parts)) + for _, part := range parts { + if part == "" { + continue + } + escapedParts = append(escapedParts, url.PathEscape(part)) + } + if len(escapedParts) == 0 { + return "" + } + + return "/job/" + strings.Join(escapedParts, "/job/") +} + type Job struct { Class string `json:"_class"` Actions []Actions `json:"actions"` @@ -101,7 +175,11 @@ type Scm struct { } func (c *Client) GetJob(name string) (resp *Job, err error) { - _, err = c.R().SetPathParam("job", name). - SetSuccessResult(&resp).Get("/job/{job}/api/json") + jobPath := JobPath(name) + if jobPath == "" { + return nil, fmt.Errorf("jenkins job name is empty") + } + + _, err = c.R().SetSuccessResult(&resp).Get(jobPath + "/api/json") return }