| @@ -11,20 +11,20 @@ import ( | |||||
| "xorm.io/builder" | "xorm.io/builder" | ||||
| ) | ) | ||||
| type CloudbrainStatus int8 | |||||
| type CloudbrainStatus string | |||||
| const ( | const ( | ||||
| JobWaiting CloudbrainStatus = iota | |||||
| JobStopped | |||||
| JobSucceeded | |||||
| JobFailed | |||||
| JobWaiting CloudbrainStatus = "WAITING" | |||||
| JobStopped CloudbrainStatus = "STOPPED" | |||||
| JobSucceeded CloudbrainStatus = "SUCCEEDED" | |||||
| JobFailed CloudbrainStatus = "FAILED" | |||||
| ) | ) | ||||
| type Cloudbrain struct { | type Cloudbrain struct { | ||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| JobID string `xorm:"INDEX NOT NULL"` | JobID string `xorm:"INDEX NOT NULL"` | ||||
| JobName string | JobName string | ||||
| Status int32 `xorm:"INDEX"` | |||||
| Status string `xorm:"INDEX"` | |||||
| UserID int64 `xorm:"INDEX"` | UserID int64 `xorm:"INDEX"` | ||||
| RepoID int64 `xorm:"INDEX"` | RepoID int64 `xorm:"INDEX"` | ||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| @@ -79,9 +79,9 @@ type CloudbrainsOptions struct { | |||||
| RepoID int64 // include all repos if empty | RepoID int64 // include all repos if empty | ||||
| UserID int64 | UserID int64 | ||||
| JobID int64 | JobID int64 | ||||
| JobStatus CloudbrainStatus | |||||
| SortType string | SortType string | ||||
| CloudbrainIDs []int64 | CloudbrainIDs []int64 | ||||
| // JobStatus CloudbrainStatus | |||||
| } | } | ||||
| type TaskPod struct { | type TaskPod struct { | ||||
| TaskRoleStatus struct { | TaskRoleStatus struct { | ||||
| @@ -127,7 +127,7 @@ type JobResultPayload struct { | |||||
| AppProgress string `json:"appProgress"` | AppProgress string `json:"appProgress"` | ||||
| AppTrackingURL string `json:"appTrackingUrl"` | AppTrackingURL string `json:"appTrackingUrl"` | ||||
| AppLaunchedTime int64 `json:"appLaunchedTime"` | AppLaunchedTime int64 `json:"appLaunchedTime"` | ||||
| AppCompletedTime int64 `json:"appCompletedTime"` | |||||
| AppCompletedTime interface{} `json:"appCompletedTime"` | |||||
| AppExitCode int `json:"appExitCode"` | AppExitCode int `json:"appExitCode"` | ||||
| AppExitDiagnostics string `json:"appExitDiagnostics"` | AppExitDiagnostics string `json:"appExitDiagnostics"` | ||||
| AppExitType interface{} `json:"appExitType"` | AppExitType interface{} `json:"appExitType"` | ||||
| @@ -196,16 +196,16 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { | |||||
| ) | ) | ||||
| } | } | ||||
| switch opts.JobStatus { | |||||
| case JobWaiting: | |||||
| cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | |||||
| case JobFailed: | |||||
| cond.And(builder.Eq{"cloudbrain.status": int(JobFailed)}) | |||||
| case JobStopped: | |||||
| cond.And(builder.Eq{"cloudbrain.status": int(JobStopped)}) | |||||
| case JobSucceeded: | |||||
| cond.And(builder.Eq{"cloudbrain.status": int(JobSucceeded)}) | |||||
| } | |||||
| // switch opts.JobStatus { | |||||
| // case JobWaiting: | |||||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | |||||
| // case JobFailed: | |||||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobFailed)}) | |||||
| // case JobStopped: | |||||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobStopped)}) | |||||
| // case JobSucceeded: | |||||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobSucceeded)}) | |||||
| // } | |||||
| if len(opts.CloudbrainIDs) > 0 { | if len(opts.CloudbrainIDs) > 0 { | ||||
| cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | ||||
| @@ -244,6 +244,17 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func GetRepoCloudBrainByJobID(repoID int64, jobID string) (*Cloudbrain, error) { | |||||
| cb := &Cloudbrain{JobID: jobID, RepoID: repoID} | |||||
| has, err := x.Get(cb) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, errors.New("cloudbrain task is not found") | |||||
| } | |||||
| return cb, nil | |||||
| } | |||||
| func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | ||||
| cb := &Cloudbrain{JobID: jobID} | cb := &Cloudbrain{JobID: jobID} | ||||
| has, err := x.Get(cb) | has, err := x.Get(cb) | ||||
| @@ -12,7 +12,7 @@ func addCloudBrainTable(x *xorm.Engine) error { | |||||
| ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
| JobID string `xorm:"INDEX NOT NULL"` | JobID string `xorm:"INDEX NOT NULL"` | ||||
| JobName string | JobName string | ||||
| Status int32 `xorm:"INDEX"` | |||||
| Status string `xorm:"INDEX"` | |||||
| UserID int64 `xorm:"INDEX"` | UserID int64 `xorm:"INDEX"` | ||||
| RepoID int64 `xorm:"INDEX"` | RepoID int64 `xorm:"INDEX"` | ||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| @@ -39,7 +39,7 @@ func GenerateTask(ctx *context.Context, jobName, image, command string) error { | |||||
| var jobID = jobResult.Payload["jobId"].(string) | var jobID = jobResult.Payload["jobId"].(string) | ||||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | err = models.CreateCloudbrain(&models.Cloudbrain{ | ||||
| Status: int32(models.JobWaiting), | |||||
| Status: string(models.JobWaiting), | |||||
| UserID: ctx.User.ID, | UserID: ctx.User.ID, | ||||
| RepoID: ctx.Repo.Repository.ID, | RepoID: ctx.Repo.Repository.ID, | ||||
| JobID: jobID, | JobID: jobID, | ||||
| @@ -849,6 +849,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| Delete(reqToken(), repo.DeleteTopic) | Delete(reqToken(), repo.DeleteTopic) | ||||
| }, reqAdmin()) | }, reqAdmin()) | ||||
| }, reqAnyRepoReader()) | }, reqAnyRepoReader()) | ||||
| m.Group("/cloudbrain", func() { | |||||
| m.Get("/:jobid", repo.GetCloudbrainTask) | |||||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | |||||
| }, repoAssignment()) | }, repoAssignment()) | ||||
| }) | }) | ||||
| @@ -0,0 +1,75 @@ | |||||
| // Copyright 2016 The Gogs Authors. All rights reserved. | |||||
| // Copyright 2018 The Gitea Authors. All rights reserved. | |||||
| // Use of this source code is governed by a MIT-style | |||||
| // license that can be found in the LICENSE file. | |||||
| package repo | |||||
| import ( | |||||
| "net/http" | |||||
| "time" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| ) | |||||
| // cloudbrain get job task by jobid | |||||
| func GetCloudbrainTask(ctx *context.APIContext) { | |||||
| // swagger:operation GET /repos/{owner}/{repo}/cloudbrain/{jobid} cloudbrain jobTask | |||||
| // --- | |||||
| // summary: Get a single task | |||||
| // produces: | |||||
| // - application/json | |||||
| // parameters: | |||||
| // - name: owner | |||||
| // in: path | |||||
| // description: owner of the repo | |||||
| // type: string | |||||
| // required: true | |||||
| // - name: repo | |||||
| // in: path | |||||
| // description: name of the repo | |||||
| // type: string | |||||
| // required: true | |||||
| // - name: jobid | |||||
| // in: path | |||||
| // description: id of cloudbrain jobid | |||||
| // type: string | |||||
| // required: true | |||||
| // responses: | |||||
| // "200": | |||||
| // "$ref": "#/responses/Label" | |||||
| var ( | |||||
| err error | |||||
| ) | |||||
| jobID := ctx.Params(":jobid") | |||||
| repoID := ctx.Repo.Repository.ID | |||||
| _, err = models.GetRepoCloudBrainByJobID(repoID, jobID) | |||||
| if err != nil { | |||||
| ctx.NotFound(err) | |||||
| return | |||||
| } | |||||
| jobResult, err := cloudbrain.GetJob(jobID) | |||||
| if err != nil { | |||||
| ctx.NotFound(err) | |||||
| return | |||||
| } | |||||
| result, err := models.ConvertToJobResultPayload(jobResult.Payload) | |||||
| if err != nil { | |||||
| ctx.NotFound(err) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||||
| "JobID": result.Config.JobID, | |||||
| "JobStatus": result.JobStatus.State, | |||||
| "SubState": result.JobStatus.SubState, | |||||
| "CreatedTime": time.Unix(result.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05"), | |||||
| "CompletedTime": time.Unix(result.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05"), | |||||
| }) | |||||
| } | |||||
| @@ -33,27 +33,24 @@ | |||||
| {{range .Tasks}} | {{range .Tasks}} | ||||
| <div class="ui grid item"> | <div class="ui grid item"> | ||||
| <div class="row"> | <div class="row"> | ||||
| <div class="five wide column"> | |||||
| <div class="seven wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | <a class="title" href="{{$.Link}}/{{.JobID}}"> | ||||
| <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | ||||
| <span class="fitted">{{.JobName}}</span> | <span class="fitted">{{.JobName}}</span> | ||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| <div class="two wide column"> | |||||
| waiting | |||||
| <div class="four wide column job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </div> | </div> | ||||
| <div class="three wide column"> | <div class="three wide column"> | ||||
| <span class="ui text center">{{svg "octicon-flame" 16}} {{TimeSinceUnix .CreatedUnix $.Lang}}</span> | <span class="ui text center">{{svg "octicon-flame" 16}} {{TimeSinceUnix .CreatedUnix $.Lang}}</span> | ||||
| </div> | </div> | ||||
| <div class="two wide column"> | |||||
| <span class="ui text center clipboard">18h 0m 8s</span> | |||||
| </div> | |||||
| <div class="two wide column"> | <div class="two wide column"> | ||||
| <span class="ui text center clipboard">{{.Status}}</span> | |||||
| </div> | |||||
| <div class="two wide column"> | |||||
| <span class="ui text center clipboard">再次提交</span> | |||||
| <span class="ui text center clipboard"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | |||||
| <span class="fitted">查看</span> | |||||
| </a> | |||||
| </span> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -66,3 +63,23 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| <script> | |||||
| $( document ).ready(function() { | |||||
| $( ".job-status" ).each(( index, job ) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| if (job.textContent.trim() != 'WAITING') { | |||||
| return | |||||
| } | |||||
| $.get( `/api/v1/repos/${repoPath}/cloudbrain/${jobID}`, ( data ) => { | |||||
| const jobID = data.JobID | |||||
| const status = data.JobStatus | |||||
| $('#'+ jobID).text(status) | |||||
| // console.log(data) | |||||
| }).fail(function(err) { | |||||
| console.log( err ); | |||||
| }); | |||||
| }); | |||||
| }); | |||||
| </script> | |||||
| @@ -2324,6 +2324,44 @@ | |||||
| } | } | ||||
| } | } | ||||
| }, | }, | ||||
| "/repos/{owner}/{repo}/cloudbrain/{jobid}": { | |||||
| "get": { | |||||
| "produces": [ | |||||
| "application/json" | |||||
| ], | |||||
| "tags": [ | |||||
| "cloudbrain" | |||||
| ], | |||||
| "summary": "Get a single task", | |||||
| "operationId": "jobTask", | |||||
| "parameters": [ | |||||
| { | |||||
| "type": "string", | |||||
| "description": "owner of the repo", | |||||
| "name": "owner", | |||||
| "in": "path", | |||||
| "required": true | |||||
| }, | |||||
| { | |||||
| "type": "string", | |||||
| "description": "name of the repo", | |||||
| "name": "repo", | |||||
| "in": "path", | |||||
| "required": true | |||||
| }, | |||||
| { | |||||
| "type": "string", | |||||
| "description": "id of cloudbrain jobid", | |||||
| "name": "jobid", | |||||
| "in": "path", | |||||
| "required": true | |||||
| } | |||||
| ], | |||||
| "responses": { | |||||
| "200": {} | |||||
| } | |||||
| } | |||||
| }, | |||||
| "/repos/{owner}/{repo}/collaborators": { | "/repos/{owner}/{repo}/collaborators": { | ||||
| "get": { | "get": { | ||||
| "produces": [ | "produces": [ | ||||