| @@ -11,20 +11,20 @@ import ( | |||
| "xorm.io/builder" | |||
| ) | |||
| type CloudbrainStatus int8 | |||
| type CloudbrainStatus string | |||
| const ( | |||
| JobWaiting CloudbrainStatus = iota | |||
| JobStopped | |||
| JobSucceeded | |||
| JobFailed | |||
| JobWaiting CloudbrainStatus = "WAITING" | |||
| JobStopped CloudbrainStatus = "STOPPED" | |||
| JobSucceeded CloudbrainStatus = "SUCCEEDED" | |||
| JobFailed CloudbrainStatus = "FAILED" | |||
| ) | |||
| type Cloudbrain struct { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| JobID string `xorm:"INDEX NOT NULL"` | |||
| JobName string | |||
| Status int32 `xorm:"INDEX"` | |||
| Status string `xorm:"INDEX"` | |||
| UserID int64 `xorm:"INDEX"` | |||
| RepoID int64 `xorm:"INDEX"` | |||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
| @@ -79,9 +79,9 @@ type CloudbrainsOptions struct { | |||
| RepoID int64 // include all repos if empty | |||
| UserID int64 | |||
| JobID int64 | |||
| JobStatus CloudbrainStatus | |||
| SortType string | |||
| CloudbrainIDs []int64 | |||
| // JobStatus CloudbrainStatus | |||
| } | |||
| type TaskPod struct { | |||
| TaskRoleStatus struct { | |||
| @@ -127,7 +127,7 @@ type JobResultPayload struct { | |||
| AppProgress string `json:"appProgress"` | |||
| AppTrackingURL string `json:"appTrackingUrl"` | |||
| AppLaunchedTime int64 `json:"appLaunchedTime"` | |||
| AppCompletedTime int64 `json:"appCompletedTime"` | |||
| AppCompletedTime interface{} `json:"appCompletedTime"` | |||
| AppExitCode int `json:"appExitCode"` | |||
| AppExitDiagnostics string `json:"appExitDiagnostics"` | |||
| 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 { | |||
| cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | |||
| @@ -244,6 +244,17 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { | |||
| 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) { | |||
| cb := &Cloudbrain{JobID: jobID} | |||
| has, err := x.Get(cb) | |||
| @@ -12,7 +12,7 @@ func addCloudBrainTable(x *xorm.Engine) error { | |||
| ID int64 `xorm:"pk autoincr"` | |||
| JobID string `xorm:"INDEX NOT NULL"` | |||
| JobName string | |||
| Status int32 `xorm:"INDEX"` | |||
| Status string `xorm:"INDEX"` | |||
| UserID int64 `xorm:"INDEX"` | |||
| RepoID int64 `xorm:"INDEX"` | |||
| 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) | |||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
| Status: int32(models.JobWaiting), | |||
| Status: string(models.JobWaiting), | |||
| UserID: ctx.User.ID, | |||
| RepoID: ctx.Repo.Repository.ID, | |||
| JobID: jobID, | |||
| @@ -849,6 +849,9 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| Delete(reqToken(), repo.DeleteTopic) | |||
| }, reqAdmin()) | |||
| }, reqAnyRepoReader()) | |||
| m.Group("/cloudbrain", func() { | |||
| m.Get("/:jobid", repo.GetCloudbrainTask) | |||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | |||
| }, 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}} | |||
| <div class="ui grid item"> | |||
| <div class="row"> | |||
| <div class="five wide column"> | |||
| <div class="seven wide column"> | |||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | |||
| <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | |||
| <span class="fitted">{{.JobName}}</span> | |||
| </a> | |||
| </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 class="three wide column"> | |||
| <span class="ui text center">{{svg "octicon-flame" 16}} {{TimeSinceUnix .CreatedUnix $.Lang}}</span> | |||
| </div> | |||
| <div class="two wide column"> | |||
| <span class="ui text center clipboard">18h 0m 8s</span> | |||
| </div> | |||
| <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> | |||
| @@ -66,3 +63,23 @@ | |||
| </div> | |||
| </div> | |||
| {{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": { | |||
| "get": { | |||
| "produces": [ | |||