mirror of https://github.com/goharbor/harbor.git
569 lines
18 KiB
Go
569 lines
18 KiB
Go
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/go-openapi/runtime/middleware"
|
|
"github.com/go-openapi/strfmt"
|
|
|
|
"github.com/goharbor/harbor/src/common/rbac"
|
|
"github.com/goharbor/harbor/src/controller/replication"
|
|
repctlmodel "github.com/goharbor/harbor/src/controller/replication/model"
|
|
"github.com/goharbor/harbor/src/jobservice/job"
|
|
"github.com/goharbor/harbor/src/lib/errors"
|
|
"github.com/goharbor/harbor/src/lib/q"
|
|
"github.com/goharbor/harbor/src/pkg/reg/model"
|
|
"github.com/goharbor/harbor/src/pkg/task"
|
|
"github.com/goharbor/harbor/src/server/v2.0/models"
|
|
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/replication"
|
|
)
|
|
|
|
func newReplicationAPI() *replicationAPI {
|
|
return &replicationAPI{
|
|
ctl: replication.Ctl,
|
|
}
|
|
}
|
|
|
|
type replicationAPI struct {
|
|
BaseAPI
|
|
ctl replication.Controller
|
|
}
|
|
|
|
func (r *replicationAPI) Prepare(ctx context.Context, operation string, params interface{}) middleware.Responder {
|
|
return nil
|
|
}
|
|
|
|
func (r *replicationAPI) CreateReplicationPolicy(ctx context.Context, params operation.CreateReplicationPolicyParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceReplicationPolicy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
sc, err := r.GetSecurityContext(ctx)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
policy := &repctlmodel.Policy{
|
|
Name: params.Policy.Name,
|
|
Description: params.Policy.Description,
|
|
Creator: sc.GetUsername(),
|
|
DestNamespace: params.Policy.DestNamespace,
|
|
ReplicateDeletion: params.Policy.Deletion,
|
|
Override: params.Policy.Override,
|
|
Enabled: params.Policy.Enabled,
|
|
}
|
|
// Make this field be optional to keep backward compatibility
|
|
if params.Policy.DestNamespaceReplaceCount != nil {
|
|
policy.DestNamespaceReplaceCount = *params.Policy.DestNamespaceReplaceCount
|
|
} else {
|
|
policy.DestNamespaceReplaceCount = -1 // -1 mean the legacy mode
|
|
}
|
|
if params.Policy.SrcRegistry != nil {
|
|
policy.SrcRegistry = &model.Registry{
|
|
ID: params.Policy.SrcRegistry.ID,
|
|
}
|
|
}
|
|
if params.Policy.DestRegistry != nil {
|
|
policy.DestRegistry = &model.Registry{
|
|
ID: params.Policy.DestRegistry.ID,
|
|
}
|
|
}
|
|
if len(params.Policy.Filters) > 0 {
|
|
for _, filter := range params.Policy.Filters {
|
|
policy.Filters = append(policy.Filters, &model.Filter{
|
|
Type: filter.Type,
|
|
Value: filter.Value,
|
|
Decoration: filter.Decoration,
|
|
})
|
|
}
|
|
}
|
|
if params.Policy.Trigger != nil {
|
|
policy.Trigger = &model.Trigger{
|
|
Type: params.Policy.Trigger.Type,
|
|
}
|
|
if params.Policy.Trigger.TriggerSettings != nil {
|
|
policy.Trigger.Settings = &model.TriggerSettings{
|
|
Cron: params.Policy.Trigger.TriggerSettings.Cron,
|
|
}
|
|
}
|
|
}
|
|
if params.Policy.Speed != nil {
|
|
if *params.Policy.Speed < 0 {
|
|
*params.Policy.Speed = 0
|
|
}
|
|
policy.Speed = *params.Policy.Speed
|
|
}
|
|
|
|
if params.Policy.CopyByChunk != nil {
|
|
policy.CopyByChunk = *params.Policy.CopyByChunk
|
|
}
|
|
|
|
id, err := r.ctl.CreatePolicy(ctx, policy)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
|
|
return operation.NewCreateReplicationPolicyCreated().WithLocation(location)
|
|
}
|
|
|
|
func (r *replicationAPI) UpdateReplicationPolicy(ctx context.Context, params operation.UpdateReplicationPolicyParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceReplicationPolicy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
policy := &repctlmodel.Policy{
|
|
ID: params.ID,
|
|
Name: params.Policy.Name,
|
|
Description: params.Policy.Description,
|
|
DestNamespace: params.Policy.DestNamespace,
|
|
ReplicateDeletion: params.Policy.Deletion,
|
|
Override: params.Policy.Override,
|
|
Enabled: params.Policy.Enabled,
|
|
}
|
|
// Make this field be optional to keep backward compatibility
|
|
if params.Policy.DestNamespaceReplaceCount != nil {
|
|
policy.DestNamespaceReplaceCount = *params.Policy.DestNamespaceReplaceCount
|
|
} else {
|
|
policy.DestNamespaceReplaceCount = -1 // -1 mean the legacy mode
|
|
}
|
|
|
|
if params.Policy.SrcRegistry != nil {
|
|
policy.SrcRegistry = &model.Registry{
|
|
ID: params.Policy.SrcRegistry.ID,
|
|
}
|
|
}
|
|
if params.Policy.DestRegistry != nil {
|
|
policy.DestRegistry = &model.Registry{
|
|
ID: params.Policy.DestRegistry.ID,
|
|
}
|
|
}
|
|
if len(params.Policy.Filters) > 0 {
|
|
for _, filter := range params.Policy.Filters {
|
|
policy.Filters = append(policy.Filters, &model.Filter{
|
|
Type: filter.Type,
|
|
Value: filter.Value,
|
|
Decoration: filter.Decoration,
|
|
})
|
|
}
|
|
}
|
|
if params.Policy.Trigger != nil {
|
|
policy.Trigger = &model.Trigger{
|
|
Type: params.Policy.Trigger.Type,
|
|
}
|
|
if params.Policy.Trigger.TriggerSettings != nil {
|
|
policy.Trigger.Settings = &model.TriggerSettings{
|
|
Cron: params.Policy.Trigger.TriggerSettings.Cron,
|
|
}
|
|
}
|
|
}
|
|
if params.Policy.Speed != nil {
|
|
if *params.Policy.Speed < 0 {
|
|
*params.Policy.Speed = 0
|
|
}
|
|
policy.Speed = *params.Policy.Speed
|
|
}
|
|
|
|
if params.Policy.CopyByChunk != nil {
|
|
policy.CopyByChunk = *params.Policy.CopyByChunk
|
|
}
|
|
|
|
if err := r.ctl.UpdatePolicy(ctx, policy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return operation.NewUpdateReplicationPolicyOK()
|
|
}
|
|
|
|
func (r *replicationAPI) ListReplicationPolicies(ctx context.Context, params operation.ListReplicationPoliciesParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationPolicy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
query, err := r.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
if params.Name != nil {
|
|
query.Keywords["Name"] = &q.FuzzyMatchValue{
|
|
Value: *params.Name,
|
|
}
|
|
}
|
|
total, err := r.ctl.PolicyCount(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
policies, err := r.ctl.ListPolicies(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
var result []*models.ReplicationPolicy
|
|
for _, policy := range policies {
|
|
result = append(result, convertReplicationPolicy(policy))
|
|
}
|
|
return operation.NewListReplicationPoliciesOK().
|
|
WithXTotalCount(total).
|
|
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
|
WithPayload(result)
|
|
}
|
|
|
|
func (r *replicationAPI) GetReplicationPolicy(ctx context.Context, params operation.GetReplicationPolicyParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceReplicationPolicy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
policy, err := r.ctl.GetPolicy(ctx, params.ID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return operation.NewGetReplicationPolicyOK().WithPayload(convertReplicationPolicy(policy))
|
|
}
|
|
|
|
func (r *replicationAPI) DeleteReplicationPolicy(ctx context.Context, params operation.DeleteReplicationPolicyParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceReplicationPolicy); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
if err := r.ctl.DeletePolicy(ctx, params.ID); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return operation.NewDeleteReplicationPolicyOK()
|
|
}
|
|
|
|
func (r *replicationAPI) StartReplication(ctx context.Context, params operation.StartReplicationParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
policy, err := r.ctl.GetPolicy(ctx, params.Execution.PolicyID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
// the legacy replication scheduler job("src/jobservice/job/impl/replication/scheduler.go") calls the start replication API
|
|
// to trigger the scheduled replication, a query string "trigger" is added when sending the request
|
|
// here is the logic to cover this part
|
|
trigger := task.ExecutionTriggerManual
|
|
if params.HTTPRequest.URL.Query().Get("trigger") == "scheduled" {
|
|
trigger = task.ExecutionTriggerSchedule
|
|
}
|
|
|
|
executionID, err := r.ctl.Start(ctx, policy, nil, trigger)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
location := strings.TrimSuffix(params.HTTPRequest.URL.Path, "/") + "/" + strconv.FormatInt(executionID, 10)
|
|
return operation.NewStartReplicationCreated().WithLocation(location)
|
|
}
|
|
|
|
func (r *replicationAPI) StopReplication(ctx context.Context, params operation.StopReplicationParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
if err := r.ctl.Stop(ctx, params.ID); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *replicationAPI) ListReplicationExecutions(ctx context.Context, params operation.ListReplicationExecutionsParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
query, err := r.BuildQuery(ctx, nil, params.Sort, params.Page, params.PageSize)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
if params.PolicyID != nil {
|
|
query.Keywords["PolicyID"] = *params.PolicyID
|
|
}
|
|
if params.Status != nil {
|
|
status := *params.Status
|
|
// as we convert the status when responding requests to keep the backward compatibility,
|
|
// here we need to reverse-convert the status
|
|
switch status {
|
|
case "InProgress":
|
|
status = job.RunningStatus.String()
|
|
case "Succeed":
|
|
status = job.SuccessStatus.String()
|
|
case "Stopped":
|
|
status = job.StoppedStatus.String()
|
|
case "Failed":
|
|
status = job.ErrorStatus.String()
|
|
}
|
|
query.Keywords["Status"] = status
|
|
}
|
|
if params.Trigger != nil {
|
|
trigger := *params.Trigger
|
|
// as we convert the trigger when responding requests to keep the backward compatibility,
|
|
// here we need to reverse-convert the trigger
|
|
switch trigger {
|
|
case "manual":
|
|
trigger = task.ExecutionTriggerManual
|
|
case "scheduled":
|
|
trigger = task.ExecutionTriggerSchedule
|
|
case "event_based":
|
|
trigger = task.ExecutionTriggerEvent
|
|
}
|
|
query.Keywords["Trigger"] = trigger
|
|
}
|
|
|
|
total, err := r.ctl.ExecutionCount(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
executions, err := r.ctl.ListExecutions(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
|
|
var execs []*models.ReplicationExecution
|
|
for _, execution := range executions {
|
|
execs = append(execs, convertExecution(execution))
|
|
}
|
|
|
|
return operation.NewListReplicationExecutionsOK().
|
|
WithXTotalCount(total).
|
|
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
|
WithPayload(execs)
|
|
}
|
|
|
|
func (r *replicationAPI) GetReplicationExecution(ctx context.Context, params operation.GetReplicationExecutionParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
execution, err := r.ctl.GetExecution(ctx, params.ID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return operation.NewGetReplicationExecutionOK().WithPayload(convertExecution(execution))
|
|
}
|
|
|
|
func (r *replicationAPI) ListReplicationTasks(ctx context.Context, params operation.ListReplicationTasksParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
// check the existence of the replication execution
|
|
_, err := r.ctl.GetExecution(ctx, params.ID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
|
|
query, err := r.BuildQuery(ctx, nil, params.Sort, params.Page, params.PageSize)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
query.Keywords["ExecutionID"] = params.ID
|
|
if params.Status != nil {
|
|
var status interface{} = *params.Status
|
|
// as we convert the status when responding requests to keep the backward compatibility,
|
|
// here we need to reverse-convert the status
|
|
// the status "pending" and "stopped" is same with jobservice, no need to convert
|
|
switch status {
|
|
case "InProgress":
|
|
status = &q.OrList{
|
|
Values: []interface{}{
|
|
job.ScheduledStatus.String(),
|
|
job.RunningStatus.String(),
|
|
},
|
|
}
|
|
case "Succeed":
|
|
status = job.SuccessStatus.String()
|
|
case "Failed":
|
|
status = job.ErrorStatus.String()
|
|
}
|
|
query.Keywords["Status"] = status
|
|
}
|
|
if params.ResourceType != nil {
|
|
query.Keywords["ExtraAttrs.resource_type"] = *params.ResourceType
|
|
}
|
|
|
|
total, err := r.ctl.TaskCount(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
|
|
tasks, err := r.ctl.ListTasks(ctx, query)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
|
|
var tks []*models.ReplicationTask
|
|
for _, task := range tasks {
|
|
tks = append(tks, convertTask(task))
|
|
}
|
|
|
|
return operation.NewListReplicationTasksOK().
|
|
WithXTotalCount(total).
|
|
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
|
WithPayload(tks)
|
|
}
|
|
|
|
func (r *replicationAPI) GetReplicationLog(ctx context.Context, params operation.GetReplicationLogParams) middleware.Responder {
|
|
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceReplication); err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
execution, err := r.ctl.GetExecution(ctx, params.ID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
task, err := r.ctl.GetTask(ctx, params.TaskID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
if execution.ID != task.ExecutionID {
|
|
return r.SendError(ctx, errors.New(nil).
|
|
WithCode(errors.NotFoundCode).
|
|
WithMessage("execution %d contains no task with ID %d", params.ID, params.TaskID))
|
|
}
|
|
log, err := r.ctl.GetTaskLog(ctx, params.TaskID)
|
|
if err != nil {
|
|
return r.SendError(ctx, err)
|
|
}
|
|
return operation.NewGetReplicationLogOK().WithContentType("text/plain").WithPayload(string(log))
|
|
}
|
|
|
|
func convertReplicationPolicy(policy *repctlmodel.Policy) *models.ReplicationPolicy {
|
|
replaceCount := policy.DestNamespaceReplaceCount
|
|
p := &models.ReplicationPolicy{
|
|
CreationTime: strfmt.DateTime(policy.CreationTime),
|
|
Deletion: policy.ReplicateDeletion,
|
|
Description: policy.Description,
|
|
DestNamespace: policy.DestNamespace,
|
|
DestNamespaceReplaceCount: &replaceCount,
|
|
Enabled: policy.Enabled,
|
|
ID: policy.ID,
|
|
Name: policy.Name,
|
|
Override: policy.Override,
|
|
ReplicateDeletion: policy.ReplicateDeletion,
|
|
Speed: &policy.Speed,
|
|
UpdateTime: strfmt.DateTime(policy.UpdateTime),
|
|
CopyByChunk: &policy.CopyByChunk,
|
|
}
|
|
if policy.SrcRegistry != nil {
|
|
p.SrcRegistry = convertRegistry(policy.SrcRegistry)
|
|
}
|
|
if policy.DestRegistry != nil {
|
|
p.DestRegistry = convertRegistry(policy.DestRegistry)
|
|
}
|
|
if len(policy.Filters) > 0 {
|
|
for _, filter := range policy.Filters {
|
|
p.Filters = append(p.Filters, &models.ReplicationFilter{
|
|
Type: string(filter.Type),
|
|
Value: filter.Value,
|
|
Decoration: filter.Decoration,
|
|
})
|
|
}
|
|
}
|
|
if policy.Trigger != nil {
|
|
trigger := &models.ReplicationTrigger{
|
|
Type: string(policy.Trigger.Type),
|
|
}
|
|
if policy.Trigger.Settings != nil {
|
|
trigger.TriggerSettings = &models.ReplicationTriggerSettings{
|
|
Cron: policy.Trigger.Settings.Cron,
|
|
}
|
|
}
|
|
p.Trigger = trigger
|
|
}
|
|
return p
|
|
}
|
|
|
|
func convertRegistry(registry *model.Registry) *models.Registry {
|
|
r := &models.Registry{
|
|
CreationTime: strfmt.DateTime(registry.CreationTime),
|
|
Description: registry.Description,
|
|
ID: registry.ID,
|
|
Insecure: registry.Insecure,
|
|
Name: registry.Name,
|
|
Status: registry.Status,
|
|
Type: string(registry.Type),
|
|
UpdateTime: strfmt.DateTime(registry.UpdateTime),
|
|
URL: registry.URL,
|
|
}
|
|
if registry.Credential != nil {
|
|
credential := &models.RegistryCredential{
|
|
AccessKey: registry.Credential.AccessKey,
|
|
Type: string(registry.Credential.Type),
|
|
}
|
|
if len(registry.Credential.AccessSecret) > 0 {
|
|
credential.AccessSecret = "*****"
|
|
}
|
|
r.Credential = credential
|
|
}
|
|
return r
|
|
}
|
|
|
|
func convertExecution(execution *replication.Execution) *models.ReplicationExecution {
|
|
exec := &models.ReplicationExecution{
|
|
ID: execution.ID,
|
|
PolicyID: execution.PolicyID,
|
|
StatusText: execution.StatusMessage,
|
|
StartTime: strfmt.DateTime(execution.StartTime),
|
|
EndTime: strfmt.DateTime(execution.EndTime),
|
|
}
|
|
// keep backward compatibility
|
|
if execution.Metrics != nil {
|
|
exec.Total = execution.Metrics.TaskCount
|
|
exec.Succeed = execution.Metrics.SuccessTaskCount
|
|
exec.Failed = execution.Metrics.ErrorTaskCount
|
|
exec.InProgress = execution.Metrics.PendingTaskCount +
|
|
execution.Metrics.ScheduledTaskCount + execution.Metrics.RunningTaskCount
|
|
exec.Stopped = execution.Metrics.StoppedTaskCount
|
|
}
|
|
switch execution.Trigger {
|
|
case task.ExecutionTriggerManual:
|
|
exec.Trigger = "manual"
|
|
case task.ExecutionTriggerSchedule:
|
|
exec.Trigger = "scheduled"
|
|
case task.ExecutionTriggerEvent:
|
|
exec.Trigger = "event_based"
|
|
}
|
|
switch execution.Status {
|
|
case job.RunningStatus.String():
|
|
exec.Status = "InProgress"
|
|
case job.SuccessStatus.String():
|
|
exec.Status = "Succeed"
|
|
case job.StoppedStatus.String():
|
|
exec.Status = "Stopped"
|
|
case job.ErrorStatus.String():
|
|
exec.Status = "Failed"
|
|
}
|
|
|
|
return exec
|
|
}
|
|
|
|
func convertTask(task *replication.Task) *models.ReplicationTask {
|
|
tk := &models.ReplicationTask{
|
|
ID: task.ID,
|
|
ExecutionID: task.ExecutionID,
|
|
JobID: task.JobID,
|
|
Operation: task.Operation,
|
|
ResourceType: task.ResourceType,
|
|
SrcResource: task.SourceResource,
|
|
DstResource: task.DestinationResource,
|
|
StartTime: strfmt.DateTime(task.StartTime),
|
|
EndTime: strfmt.DateTime(task.EndTime),
|
|
}
|
|
// keep backward compatibility
|
|
switch task.Status {
|
|
case job.ScheduledStatus.String(), job.RunningStatus.String():
|
|
tk.Status = "InProgress"
|
|
case job.SuccessStatus.String():
|
|
tk.Status = "Succeed"
|
|
case job.ErrorStatus.String():
|
|
tk.Status = "Failed"
|
|
// the status "pending" and "stopped" is same with jobservice, no need to convert
|
|
default:
|
|
tk.Status = task.Status
|
|
}
|
|
return tk
|
|
}
|