Skip to content

Commit 08e662f

Browse files
committed
Add gocron scheduler for devbuild polling
- Integrated gocron scheduler to handle periodic polling of devbuild status. - Updated `NewRootForMessage` to accept and initialize the scheduler. - Modified `runCommandDevbuildTrigger` to schedule polling tasks. - Refactored devbuild polling logic into separate functions.
1 parent b009fdc commit 08e662f

File tree

7 files changed

+93
-32
lines changed

7 files changed

+93
-32
lines changed

chatops-lark/cmd/server/main.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"os"
88

9+
"github.com/jasonlvhit/gocron"
910
lark "github.com/larksuite/oapi-sdk-go/v3"
1011
larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
1112
"github.com/larksuite/oapi-sdk-go/v3/event/dispatcher"
@@ -68,8 +69,9 @@ func main() {
6869
}
6970
}
7071

72+
cronScheduler := gocron.NewScheduler()
7173
eventHandler := dispatcher.NewEventDispatcher("", "").
72-
OnP2MessageReceiveV1(handler.NewRootForMessage(producerCli, cfg))
74+
OnP2MessageReceiveV1(handler.NewRootForMessage(producerCli, cfg, cronScheduler))
7375

7476
consumerOpts := []larkws.ClientOption{larkws.WithEventHandler(eventHandler)}
7577
if *debugMode {
@@ -78,11 +80,12 @@ func main() {
7880
larkws.WithAutoReconnect(true))
7981
}
8082
consumerOpts = append(consumerOpts, larkws.WithLogLevel(larkcore.LogLevelInfo))
81-
8283
consumerCli := larkws.NewClient(*appID, *appSecret, consumerOpts...)
84+
csStop := cronScheduler.Start()
85+
8386
// Now start the WebSocket client (blocking call)
84-
err := consumerCli.Start(context.Background())
85-
if err != nil {
87+
if err := consumerCli.Start(context.Background()); err != nil {
88+
csStop <- true
8689
log.Fatal().Err(err).Msg("run failed for Lark WebSocket client")
8790
}
8891
}

chatops-lark/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/allegro/bigcache/v3 v3.1.0
99
github.com/go-resty/resty/v2 v2.16.5
1010
github.com/google/go-github/v68 v68.0.0
11+
github.com/jasonlvhit/gocron v0.0.1
1112
github.com/larksuite/oapi-sdk-go/v3 v3.4.7
1213
github.com/rs/zerolog v1.33.0
1314
gopkg.in/yaml.v3 v3.0.1

chatops-lark/go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
2121
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2222
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
2323
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
24+
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
25+
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
2426
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
2527
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
2628
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
2729
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
2830
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
31+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
2932
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3033
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
3134
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -40,8 +43,11 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
4043
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
4144
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
4245
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
46+
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
4347
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
4448
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
49+
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
50+
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
4551
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
4652
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
4753
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -70,6 +76,9 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
7076
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
7177
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
7278
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
79+
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
80+
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
81+
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
7382
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
7483
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7584
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
@@ -105,16 +114,19 @@ golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
105114
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
106115
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
107116
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
117+
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
108118
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
109119
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
110120
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
111121
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
112122
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
113123
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
114124
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
125+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
115126
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
116127
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
117128
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
129+
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
118130
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
119131
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
120132
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -138,6 +150,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
138150
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
139151
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
140152
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
153+
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
154+
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
155+
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
141156
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
142157
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
143158
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

chatops-lark/pkg/events/handler/devbuild.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ import (
88
// TODO: get it from cli args.
99
const devBuildURL = "https://tibuild.pingcap.net/api/devbuilds"
1010

11+
const (
12+
ctxKeyDevbuildPollScheduler = "devbuild.poll.scheduler"
13+
ctxKeyDevbuildPollTask = "devbuild.poll.task"
14+
)
15+
1116
const (
1217
devBuildHelpText = `missing subcommand
1318

chatops-lark/pkg/events/handler/devbuild_poll.go

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"fmt"
77
"html/template"
88
"net/url"
9+
"slices"
910
"strings"
11+
"time"
1012

1113
"github.com/Masterminds/sprig/v3"
1214
"github.com/go-resty/resty/v2"
@@ -18,6 +20,8 @@ import (
1820
//go:embed devbuild_poll.md.tmpl
1921
var devBuildPollResponseTmpl string
2022

23+
const loopPollInterval = 10 * time.Second
24+
2125
type pollParams struct {
2226
buildID string
2327
}
@@ -51,25 +55,15 @@ func runCommandDevbuildPoll(_ context.Context, args []string) (string, error) {
5155
return "", fmt.Errorf("failed to parse poll command: %v", err)
5256
}
5357

54-
client := resty.New()
55-
reqUrl, err := url.JoinPath(devBuildURL, params.buildID)
58+
result, err := pollDevbuildStatus(params.buildID)
5659
if err != nil {
5760
return "", err
5861
}
5962

60-
resp, err := client.R().
61-
SetResult(pollResult{}).
62-
// TODO: add auth in header.
63-
Get(reqUrl)
64-
if err != nil {
65-
return "", err
66-
}
67-
if !resp.IsSuccess() {
68-
return "", fmt.Errorf("poll devbuild failed: %s", resp.String())
69-
}
70-
result := resp.Result().(*pollResult)
63+
return renderDevbuildStatusForLark(result)
64+
}
7165

72-
// Create a new template and add a custom function to format JSON
66+
func renderDevbuildStatusForLark(result *pollResult) (string, error) {
7367
t := template.Must(template.New("markdown").
7468
Funcs(sprig.FuncMap()).
7569
Funcs(template.FuncMap{"toYaml": func(v any) string {
@@ -89,3 +83,37 @@ func runCommandDevbuildPoll(_ context.Context, args []string) (string, error) {
8983

9084
return sb.String(), nil
9185
}
86+
87+
func loopPollDevbuildStatus(buildID string) (*pollResult, error) {
88+
for {
89+
result, err := pollDevbuildStatus(buildID)
90+
if err != nil {
91+
return nil, err
92+
}
93+
if slices.Contains([]string{"ABORTED", "SUCCESS", "FAILURE", "ERROR"}, result.Status.Status) {
94+
return result, nil
95+
}
96+
time.Sleep(loopPollInterval)
97+
}
98+
}
99+
100+
func pollDevbuildStatus(buildID string) (*pollResult, error) {
101+
client := resty.New()
102+
reqUrl, err := url.JoinPath(devBuildURL, buildID)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
req := client.R().
108+
SetResult(pollResult{})
109+
110+
resp, err := req.Get(reqUrl)
111+
if err != nil {
112+
return nil, err
113+
}
114+
if !resp.IsSuccess() {
115+
return nil, fmt.Errorf("poll devbuild failed: %s", resp.String())
116+
}
117+
result := resp.Result().(*pollResult)
118+
return result, nil
119+
}

chatops-lark/pkg/events/handler/devbuild_trigger.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/go-resty/resty/v2"
10+
"github.com/jasonlvhit/gocron"
1011

1112
"github.com/PingCAP-QE/ee-apps/tibuild/pkg/rest/service"
1213
)
@@ -92,6 +93,10 @@ func runCommandDevbuildTrigger(ctx context.Context, args []string) (string, erro
9293

9394
result := resp.Result().(*triggerResult)
9495

96+
pollScheduler := ctx.Value("").(*gocron.Scheduler)
97+
pollFunc := ctx.Value("").(func(string))
98+
pollScheduler.Every(1).Minute().Do(pollFunc, result.ID)
99+
95100
return fmt.Sprintf("build id is %d\npolling: %s/%d", result.ID, devBuildURL, result.ID), nil
96101
}
97102

chatops-lark/pkg/events/handler/root.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/PingCAP-QE/ee-apps/chatops-lark/pkg/audit"
1212
"github.com/PingCAP-QE/ee-apps/chatops-lark/pkg/response"
1313
"github.com/allegro/bigcache/v3"
14+
"github.com/jasonlvhit/gocron"
1415
lark "github.com/larksuite/oapi-sdk-go/v3"
1516
larkcontact "github.com/larksuite/oapi-sdk-go/v3/service/contact/v3"
1617
larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"
@@ -30,10 +31,11 @@ const (
3031
type CommandHandler func(context.Context, []string) (string, error)
3132

3233
type CommandConfig struct {
33-
Handler CommandHandler
34-
NeedsAudit bool
35-
AuditWebhook string
36-
SetupContext func(ctx context.Context, config map[string]any, sender *CommandSender) context.Context
34+
Handler CommandHandler
35+
AsyncNotifyHandler CommandHandler
36+
NeedsAudit bool
37+
AuditWebhook string
38+
SetupContext func(ctx context.Context, config map[string]any, sender *CommandSender) context.Context
3739
}
3840

3941
// TODO: support command /sync_docker_image
@@ -77,10 +79,11 @@ type commandLarkMsgContent struct {
7779

7880
type rootHandler struct {
7981
*lark.Client
80-
eventCache *bigcache.BigCache
81-
Config map[string]any
82-
botName string
83-
logger zerolog.Logger
82+
eventCache *bigcache.BigCache
83+
Config map[string]any
84+
botName string
85+
logger zerolog.Logger
86+
cronScheduler *gocron.Scheduler
8487
}
8588

8689
// InformationError represents an information level error that occurred during command execution.
@@ -125,7 +128,7 @@ func init() {
125128
availableCommandsHelpText = helpText
126129
}
127130

128-
func NewRootForMessage(respondCli *lark.Client, cfg map[string]any) func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
131+
func NewRootForMessage(respondCli *lark.Client, cfg map[string]any, cronScheduler *gocron.Scheduler) func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {
129132
cacheCfg := bigcache.DefaultConfig(10 * time.Minute)
130133
cacheCfg.Logger = &log.Logger
131134
cache, _ := bigcache.New(context.Background(), cacheCfg)
@@ -140,11 +143,12 @@ func NewRootForMessage(respondCli *lark.Client, cfg map[string]any) func(ctx con
140143
}
141144

142145
h := &rootHandler{
143-
Client: respondCli,
144-
Config: cfg,
145-
eventCache: cache,
146-
botName: botName,
147-
logger: baseLogger,
146+
Client: respondCli,
147+
Config: cfg,
148+
eventCache: cache,
149+
botName: botName,
150+
logger: baseLogger,
151+
cronScheduler: cronScheduler,
148152
}
149153
return h.Handle
150154
}

0 commit comments

Comments
 (0)