add handler

main
git 2025-12-09 21:01:21 +08:00
parent 960176c863
commit c0a27b8d1f
Signed by: git
GPG Key ID: 3F65EFFA44207ADD
7 changed files with 554 additions and 5 deletions

96
task/.golangci.yml Normal file
View File

@ -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

View File

@ -18,7 +18,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.etcd.io/etcd/api/v3/mvccpb" "go.etcd.io/etcd/api/v3/mvccpb"
) )

View File

@ -19,11 +19,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/RichardKnop/machinery/v2/config" "github.com/RichardKnop/machinery/v2/config"
"github.com/RichardKnop/machinery/v2/tasks" "github.com/RichardKnop/machinery/v2/tasks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestFindTaskKey(t *testing.T) { func TestFindTaskKey(t *testing.T) {

View File

@ -21,6 +21,8 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/RichardKnop/machinery/v2/config"
"git.ifooth.com/common/pkg/task" "git.ifooth.com/common/pkg/task"
etcdbackend "git.ifooth.com/common/pkg/task/backends/etcd" etcdbackend "git.ifooth.com/common/pkg/task/backends/etcd"
etcdbroker "git.ifooth.com/common/pkg/task/brokers/etcd" etcdbroker "git.ifooth.com/common/pkg/task/brokers/etcd"
@ -28,7 +30,6 @@ import (
istep "git.ifooth.com/common/pkg/task/steps/iface" istep "git.ifooth.com/common/pkg/task/steps/iface"
mysqlstore "git.ifooth.com/common/pkg/task/stores/mysql" mysqlstore "git.ifooth.com/common/pkg/task/stores/mysql"
"git.ifooth.com/common/pkg/task/types" "git.ifooth.com/common/pkg/task/types"
"github.com/RichardKnop/machinery/v2/config"
) )
/* /*

View File

@ -2,10 +2,14 @@ module git.ifooth.com/common/pkg/task
go 1.25 go 1.25
replace git.ifooth.com/common/pkg => ../
require ( require (
git.ifooth.com/common/pkg v0.0.0-00010101000000-000000000000
github.com/RichardKnop/machinery/v2 v2.0.16 github.com/RichardKnop/machinery/v2 v2.0.16
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/samber/lo v1.52.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/urfave/cli v1.22.17 github.com/urfave/cli v1.22.17
go.etcd.io/etcd/api/v3 v3.6.6 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/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.1 // 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/go-sql-driver/mysql v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // 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/jstemmer/go-junit-report v0.9.1 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.17.9 // 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/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opentracing/opentracing-go v1.2.0 // 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.etcd.io/etcd/client/pkg/v3 v3.6.6 // indirect
go.mongodb.org/mongo-driver v1.17.0 // indirect go.mongodb.org/mongo-driver v1.17.0 // indirect
go.opencensus.io v0.22.5 // 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/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.36.0 // indirect golang.org/x/crypto v0.36.0 // indirect

View File

@ -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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 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/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 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-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-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 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 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 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 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= 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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 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/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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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.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 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 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 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= 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= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=

418
task/handler.go Normal file
View File

@ -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
}