add handler
parent
960176c863
commit
c0a27b8d1f
|
|
@ -0,0 +1,96 @@
|
|||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
# tests: false # 过滤_test.go文件
|
||||
build-tags:
|
||||
- goexperiment.jsonv2
|
||||
|
||||
issues:
|
||||
# 显示所有 issue
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
formatters:
|
||||
# 必须在enable打开才能用
|
||||
enable:
|
||||
- gofmt
|
||||
- gci
|
||||
|
||||
settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(git.ifooth.com/common/pkg/task)
|
||||
custom-order: true
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# enable by default
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
|
||||
# custom
|
||||
- goconst
|
||||
- goheader
|
||||
- gosec
|
||||
- misspell
|
||||
- nakedret
|
||||
- revive
|
||||
- unconvert
|
||||
- unparam
|
||||
- modernize
|
||||
|
||||
# 忽略特定错误
|
||||
# exclusions:
|
||||
# rules:
|
||||
# - linters:
|
||||
# - govet
|
||||
# text: 'shadow: declaration of "err" shadows declaration'
|
||||
|
||||
settings:
|
||||
# 只开启特定的规则
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- (*os.File).Close
|
||||
- (io.Closer).Close
|
||||
- (net/http.ResponseWriter).Write
|
||||
- io.Copy
|
||||
- os.RemoveAll
|
||||
govet:
|
||||
enable:
|
||||
- shadow
|
||||
gosec:
|
||||
includes:
|
||||
- G201 # SQL query construction using format string
|
||||
- G202 # SQL query construction using string concatenation
|
||||
- G101 # Look for hard coded credentials
|
||||
- G401 # Detect the usage of DES, RC4, MD5 or SHA1
|
||||
- G402 # Look for bad TLS connection settings
|
||||
- G403 # Ensure minimum RSA key length of 2048 bits
|
||||
- G504 # Import blocklist: net/http/cgi
|
||||
misspell:
|
||||
locale: US
|
||||
revive:
|
||||
rules:
|
||||
- name: line-length-limit
|
||||
arguments:
|
||||
- 120
|
||||
- name: function-length
|
||||
arguments:
|
||||
- 80 # statements
|
||||
- 80 # lines
|
||||
- name: cyclomatic
|
||||
arguments:
|
||||
- 20
|
||||
- name: use-any
|
||||
- name: early-return
|
||||
- name: exported
|
||||
arguments:
|
||||
- checkPrivateReceivers
|
||||
- sayRepetitiveInsteadOfStutters
|
||||
- name: package-comments
|
||||
|
|
@ -18,7 +18,6 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/RichardKnop/machinery/v2/config"
|
||||
"github.com/RichardKnop/machinery/v2/tasks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindTaskKey(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import (
|
|||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/RichardKnop/machinery/v2/config"
|
||||
|
||||
"git.ifooth.com/common/pkg/task"
|
||||
etcdbackend "git.ifooth.com/common/pkg/task/backends/etcd"
|
||||
etcdbroker "git.ifooth.com/common/pkg/task/brokers/etcd"
|
||||
|
|
@ -28,7 +30,6 @@ import (
|
|||
istep "git.ifooth.com/common/pkg/task/steps/iface"
|
||||
mysqlstore "git.ifooth.com/common/pkg/task/stores/mysql"
|
||||
"git.ifooth.com/common/pkg/task/types"
|
||||
"github.com/RichardKnop/machinery/v2/config"
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
|
|||
17
task/go.mod
17
task/go.mod
|
|
@ -2,10 +2,14 @@ module git.ifooth.com/common/pkg/task
|
|||
|
||||
go 1.25
|
||||
|
||||
replace git.ifooth.com/common/pkg => ../
|
||||
|
||||
require (
|
||||
git.ifooth.com/common/pkg v0.0.0-00010101000000-000000000000
|
||||
github.com/RichardKnop/machinery/v2 v2.0.16
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/samber/lo v1.52.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/urfave/cli v1.22.17
|
||||
go.etcd.io/etcd/api/v3 v3.6.6
|
||||
|
|
@ -34,6 +38,13 @@ require (
|
|||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||
|
|
@ -47,6 +58,7 @@ require (
|
|||
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
|
|
@ -64,6 +76,11 @@ require (
|
|||
go.etcd.io/etcd/client/pkg/v3 v3.6.6 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.0 // indirect
|
||||
go.opencensus.io v0.22.5 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
|
|
|
|||
19
task/go.sum
19
task/go.sum
|
|
@ -89,13 +89,26 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
|||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
|
@ -192,6 +205,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
|
|
@ -218,6 +233,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
|
|||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
|
@ -263,6 +280,8 @@ go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
|
|||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,418 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.ifooth.com/common/pkg/rest"
|
||||
"git.ifooth.com/common/pkg/validator"
|
||||
"github.com/samber/lo"
|
||||
|
||||
istore "git.ifooth.com/common/pkg/task/stores/iface"
|
||||
"git.ifooth.com/common/pkg/task/types"
|
||||
itypes "git.ifooth.com/common/pkg/task/types"
|
||||
)
|
||||
|
||||
type service struct {
|
||||
host string
|
||||
routePrefix string
|
||||
mgr *TaskManager
|
||||
}
|
||||
|
||||
var (
|
||||
// TaskStatusSlice ...
|
||||
TaskStatusSlice = []string{
|
||||
types.TaskStatusInit,
|
||||
types.TaskStatusRunning,
|
||||
types.TaskStatusSuccess,
|
||||
types.TaskStatusFailure,
|
||||
types.TaskStatusTimeout,
|
||||
types.TaskStatusRevoked,
|
||||
types.TaskStatusNotStarted,
|
||||
}
|
||||
)
|
||||
|
||||
func NewHandler(mgr *TaskManager, host string, routePrefix string) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
s := &service{
|
||||
mgr: mgr,
|
||||
host: host,
|
||||
routePrefix: routePrefix,
|
||||
}
|
||||
|
||||
// 任务管理
|
||||
mux.HandleFunc("POST /api/v1/task/update", rest.Handle(s.Update))
|
||||
mux.HandleFunc("POST /api/v1/task/retry", rest.Handle(s.Retry))
|
||||
mux.HandleFunc("POST /api/v1/task/revoke", rest.Handle(s.Revoke))
|
||||
mux.HandleFunc("GET /api/v1/task/status", rest.Handle(s.Status))
|
||||
mux.HandleFunc("GET /api/v1/task/list", rest.Handle(s.List))
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
// Task bcs_task task with execution duration
|
||||
type Task struct {
|
||||
*itypes.Task
|
||||
Steps []*Step `json:"steps,omitempty"`
|
||||
ExecutionDuration time.Duration `json:"executionDuration" swaggertype:"string"`
|
||||
MaxExecutionDuration time.Duration `json:"maxExecutionDuration" swaggertype:"string"`
|
||||
DetailURL string `json:"detailURL,omitempty"`
|
||||
}
|
||||
|
||||
// Step bcs_task step with execution duration
|
||||
type Step struct {
|
||||
*itypes.Step
|
||||
ExecutionDuration time.Duration `json:"executionDuration" swaggertype:"string"`
|
||||
MaxExecutionDuration time.Duration `json:"maxExecutionDuration" swaggertype:"string"`
|
||||
}
|
||||
|
||||
// StepReq ...
|
||||
type StepReq struct {
|
||||
Name string `json:"name"`
|
||||
Params *map[string]string `json:"params"`
|
||||
Payload *string `json:"payload"`
|
||||
RetryCount *uint32 `json:"retryCount"`
|
||||
Status *string `json:"status"`
|
||||
}
|
||||
|
||||
// Validate ...
|
||||
func (s *StepReq) Validate() error {
|
||||
if s.Status != nil && !lo.Contains(TaskStatusSlice, *s.Status) {
|
||||
return fmt.Errorf("step status %s not valid", *s.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateReq 请求参数
|
||||
type UpdateReq struct {
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
ResetStartTime bool `json:"resetStartTime"`
|
||||
Status string `json:"status"`
|
||||
Steps []*StepReq `json:"steps"`
|
||||
}
|
||||
|
||||
// Validate ...
|
||||
func (r *UpdateReq) Validate() error {
|
||||
if r.Status != "" && !lo.Contains(TaskStatusSlice, r.Status) {
|
||||
return fmt.Errorf("task status %s not valid", r.Status)
|
||||
}
|
||||
|
||||
for _, v := range r.Steps {
|
||||
if err := v.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetryReq 请求参数
|
||||
type RetryReq struct {
|
||||
TaskID string `json:"taskID" validate:"required"`
|
||||
StepName string `json:"stepName"`
|
||||
}
|
||||
|
||||
// Update 更新任务
|
||||
//
|
||||
// @ID UpdateTask
|
||||
// @Summary 更新任务
|
||||
// @Description 更新任务
|
||||
// @Tags 任务管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body UpdateReq true "task info"
|
||||
// @Success 200 {object} rest.APIResponse{data=nil}
|
||||
// @Failure 400
|
||||
// @x-bk-apigateway {"isPublic": false, "appVerifiedRequired": true, "userVerifiedRequired": false}
|
||||
// @Router /api/v1/remotedevenv/task/update [post]
|
||||
func (s *service) Update(ctx context.Context, req *UpdateReq) (*rest.EmptyResp, error) {
|
||||
if err := validator.Struct(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := s.mgr.GetTaskWithID(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.ResetStartTime {
|
||||
t.Start = time.Now()
|
||||
}
|
||||
if req.Status != "" {
|
||||
t.Status = req.Status
|
||||
}
|
||||
|
||||
for _, v := range req.Steps {
|
||||
step, ok := t.GetStep(v.Name)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("step %s not found", v.Name)
|
||||
}
|
||||
if v.Params != nil {
|
||||
step.Params = *v.Params
|
||||
}
|
||||
if v.RetryCount != nil {
|
||||
step.RetryCount = *v.RetryCount
|
||||
}
|
||||
if v.Status != nil {
|
||||
step.Status = *v.Status
|
||||
}
|
||||
if v.Payload != nil {
|
||||
body, err := base64.StdEncoding.DecodeString(*v.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
step.Payload = string(body)
|
||||
}
|
||||
|
||||
t.SetCurrentStep(v.Name)
|
||||
// 只更新DB
|
||||
if err := s.mgr.UpdateTask(ctx, t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := &rest.EmptyResp{}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Retry 重试任务
|
||||
//
|
||||
// @ID RetryTask
|
||||
// @Summary 重试任务
|
||||
// @Description 重试任务
|
||||
// @Tags 任务管理,clouddev,cloudpc
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body RetryReq true "task info"
|
||||
// @Success 200 {object} rest.APIResponse{data=nil}
|
||||
// @Failure 400
|
||||
// @x-bk-apigateway {"isPublic": false, "appVerifiedRequired": true, "userVerifiedRequired": false}
|
||||
// @Router /api/v1/remotedevenv/task/retry [post]
|
||||
func (s *service) Retry(ctx context.Context, req *RetryReq) (*rest.EmptyResp, error) {
|
||||
if err := validator.Struct(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := s.mgr.GetTaskWithID(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &rest.EmptyResp{}
|
||||
|
||||
if t.Status != itypes.TaskStatusFailure &&
|
||||
t.Status != itypes.TaskStatusRevoked &&
|
||||
t.Status != itypes.TaskStatusTimeout {
|
||||
return nil, fmt.Errorf("task %s already in process %s", req.TaskID, t.Status)
|
||||
}
|
||||
|
||||
if t.Status == itypes.TaskStatusTimeout {
|
||||
t.Start = time.Now()
|
||||
}
|
||||
|
||||
// 只重试单步骤
|
||||
if req.StepName != "" {
|
||||
step, ok := t.GetStep(req.StepName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("step %s not found", req.StepName)
|
||||
}
|
||||
if step.Status == itypes.TaskStatusSuccess {
|
||||
return nil, fmt.Errorf("step %s already success", req.StepName)
|
||||
}
|
||||
|
||||
if step.Status == itypes.TaskStatusFailure {
|
||||
step.RetryCount = 0
|
||||
step.Status = itypes.TaskStatusNotStarted
|
||||
}
|
||||
t.SetCurrentStep(req.StepName)
|
||||
if err := s.mgr.RetryAt(t, req.StepName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// 全量重试必须有失败的任务
|
||||
notSuccessStep := lo.Filter(t.Steps, func(v *itypes.Step, _ int) bool {
|
||||
return v.Status != itypes.TaskStatusSuccess
|
||||
})
|
||||
if len(notSuccessStep) == 0 {
|
||||
return nil, fmt.Errorf("task %s all step already success", req.TaskID)
|
||||
}
|
||||
|
||||
// 错误步骤重试次数置为0, 只当前步骤有效
|
||||
for _, step := range t.Steps {
|
||||
if step.Status == itypes.TaskStatusFailure {
|
||||
step.RetryCount = 0
|
||||
step.Status = itypes.TaskStatusNotStarted
|
||||
}
|
||||
}
|
||||
// 任务级重试
|
||||
if err := s.mgr.RetryAll(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// commonReq 任务请求参数
|
||||
type commonReq struct {
|
||||
TaskID string `json:"taskID" in:"query=taskID" validate:"required"`
|
||||
}
|
||||
|
||||
// Revoke 取消任务
|
||||
//
|
||||
// @ID RevokeTask
|
||||
// @Summary 取消任务
|
||||
// @Description 取消任务
|
||||
// @Tags 任务管理,clouddev,cloudpc
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body commonReq true "common info"
|
||||
// @Success 200 {object} rest.APIResponse{data=nil}
|
||||
// @Failure 400
|
||||
// @x-bk-apigateway {"isPublic": false, "appVerifiedRequired": true, "userVerifiedRequired": false}
|
||||
// @Router /api/v1/remotedevenv/task/revoke [post]
|
||||
func (s *service) Revoke(ctx context.Context, req *commonReq) (*rest.EmptyResp, error) {
|
||||
if err := validator.Struct(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t, err := s.mgr.GetTaskWithID(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.Status != itypes.TaskStatusRunning {
|
||||
return nil, fmt.Errorf("task %s not running", req.TaskID)
|
||||
}
|
||||
|
||||
if err := s.mgr.Revoke(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rest.EmptyResp{}, nil
|
||||
}
|
||||
|
||||
// Status 任务状态
|
||||
//
|
||||
// @ID TaskStatus
|
||||
// @Summary 任务状态
|
||||
// @Description 获取任务状态
|
||||
// @Tags 任务管理,clouddev,cloudpc
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param taskID query string true "task id"
|
||||
// @Success 200 {object} rest.APIResponse{data=Task}
|
||||
// @Failure 400
|
||||
// @x-bk-apigateway {"isPublic": false, "appVerifiedRequired": true, "userVerifiedRequired": false}
|
||||
// @Router /api/v1/remotedevenv/task/status [get]
|
||||
func (s *service) Status(ctx context.Context, req *commonReq) (*Task, error) {
|
||||
if err := validator.Struct(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
taskData, err := s.mgr.GetTaskWithID(ctx, req.TaskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
steps := lo.Map(taskData.Steps, func(v *itypes.Step, _ int) *Step {
|
||||
return &Step{
|
||||
Step: v,
|
||||
ExecutionDuration: time.Duration(v.ExecutionTime) * time.Millisecond,
|
||||
MaxExecutionDuration: time.Duration(v.MaxExecutionSeconds) * time.Second,
|
||||
}
|
||||
})
|
||||
|
||||
t := &Task{
|
||||
Task: taskData,
|
||||
Steps: steps,
|
||||
ExecutionDuration: time.Duration(taskData.ExecutionTime) * time.Millisecond,
|
||||
MaxExecutionDuration: time.Duration(taskData.MaxExecutionSeconds) * time.Second,
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// ListReq ...
|
||||
type ListReq struct {
|
||||
rest.PaginationReq
|
||||
TaskID string `json:"taskID" in:"query=taskID"`
|
||||
TaskType string `json:"taskType" in:"query=taskType"`
|
||||
TaskName string `json:"taskName" in:"query=taskName"`
|
||||
TaskIndex string `json:"taskIndex" in:"query=taskIndex"`
|
||||
CurrentStep string `json:"currentStep" in:"query=currentStep"`
|
||||
Status string `json:"status" in:"query=status" validate:"oneof='' SUCCESS FAILURE RUNNING TIMEOUT REVOKED NOTSTARTED INITIALIZING"` // nolint
|
||||
Creator string `json:"creator" in:"query=creator"`
|
||||
}
|
||||
|
||||
// List 任务分页列表
|
||||
//
|
||||
// @ID ListTask
|
||||
// @Summary 任务列表
|
||||
// @Description 获取任务列表
|
||||
// @Tags 任务管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param offset query int false "offset"
|
||||
// @Param limit query int false "limit"
|
||||
// @Param taskID query string false "task id"
|
||||
// @Param taskType query string false "task type"
|
||||
// @Param taskName query string false "task name"
|
||||
// @Param taskIndex query string false "task id"
|
||||
// @Param currentStep query string false "current step"
|
||||
// @Param status query string false "status"
|
||||
// @Param creator query string false "creator"
|
||||
// @Success 200 {object} rest.APIResponse{data=rest.PaginationResp[Task]}
|
||||
// @Failure 400
|
||||
// @x-bk-apigateway {"isPublic": false, "appVerifiedRequired": true, "userVerifiedRequired": false}
|
||||
// @Router /api/v1/task/list [get]
|
||||
func (s *service) List(ctx context.Context, req *ListReq) (*rest.PaginationResp[Task], error) {
|
||||
if err := validator.Struct(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statusURL := "/api/v1/task/status"
|
||||
detailURL, err := url.JoinPath(s.host, s.routePrefix, statusURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 默认返回20条数据
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 20
|
||||
}
|
||||
|
||||
listReq := &istore.ListOption{
|
||||
TaskID: req.TaskID,
|
||||
TaskType: req.TaskType,
|
||||
TaskName: req.TaskName,
|
||||
TaskIndex: req.TaskIndex,
|
||||
CurrentStep: req.CurrentStep,
|
||||
Status: req.Status,
|
||||
Creator: req.Creator,
|
||||
Limit: int64(req.Limit),
|
||||
Offset: int64(req.Offset),
|
||||
}
|
||||
|
||||
result, err := s.mgr.ListTask(ctx, listReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := lo.Map(result.Items, func(v *itypes.Task, _ int) Task {
|
||||
return Task{
|
||||
Task: v,
|
||||
ExecutionDuration: time.Duration(v.ExecutionTime) * time.Millisecond,
|
||||
MaxExecutionDuration: time.Duration(v.MaxExecutionSeconds) * time.Second,
|
||||
DetailURL: fmt.Sprintf("%s?taskID=%s", detailURL, v.TaskID),
|
||||
}
|
||||
})
|
||||
|
||||
resp := &rest.PaginationResp[Task]{
|
||||
Items: items,
|
||||
Count: result.Count,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
Loading…
Reference in New Issue