Skip to content
This repository was archived by the owner on Dec 10, 2024. It is now read-only.

Commit 3c60759

Browse files
committed
Logging cleanup, add abandoned task checker.
1 parent 3ddd79a commit 3c60759

File tree

6 files changed

+83
-49
lines changed

6 files changed

+83
-49
lines changed

crew/redis_task_storage.go

+1-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7-
"fmt"
87
"os"
98
"strings"
109
"time"
@@ -117,8 +116,6 @@ func (storage *RedisTaskStorage) SaveTask(task *Task, create bool) (err error) {
117116
}
118117

119118
if canWrite {
120-
fmt.Println("~~ save task", task.Id, task.IsComplete)
121-
122119
redisSetErr := storage.Client.Set(context.Background(), key, taskJsonStr, storage.GetExpiration()).Err()
123120
if redisSetErr != nil {
124121
return redisSetErr
@@ -191,25 +188,12 @@ func (storage *RedisTaskStorage) TryLockTask(taskId string) (unlocker func() err
191188
}
192189

193190
unlocker = func() error {
194-
state, unlockErr := mux.Unlock()
195-
if unlockErr != nil {
196-
fmt.Println("~~ Error unlocking task mutex", unlockErr, state)
197-
} else {
198-
fmt.Println("~~ Success unlocking task mutex", unlockErr, state)
199-
}
191+
_, unlockErr := mux.Unlock()
200192
return unlockErr
201193
}
202194
return
203195
}
204196

205-
// func (storage *RedisTaskStorage) UnlockTask(taskId string) (err error) {
206-
// state, unlockErr := storage.RedSync.NewMutex(storage.TaskMutexKey(taskId)).Unlock()
207-
// if unlockErr != nil {
208-
// fmt.Println("~~ Error unlocking task mutex", unlockErr, state)
209-
// }
210-
// return unlockErr
211-
// }
212-
213197
// Delete task deletes a task by task id.
214198
func (storage *RedisTaskStorage) DeleteTask(taskId string) (err error) {
215199
key := storage.TaskKey(taskId)
@@ -289,7 +273,6 @@ func (storage *RedisTaskStorage) FindTaskGroup(taskGroupId string) (taskGroup *T
289273

290274
// All TaskGroups returns all task groups.
291275
func (storage *RedisTaskStorage) AllTaskGroups() (taskGroups []*TaskGroup, err error) {
292-
fmt.Println("~~ AllTaskGroups 1", storage.TaskGroupsPrefix())
293276
ctx := context.Background()
294277
iter := storage.Client.Scan(ctx, 0, storage.TaskGroupsPrefix()+"*", 0).Iterator()
295278
taskGroups = make([]*TaskGroup, 0)

crew/rest_api.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323

2424
func getFileSystem(useOS bool, embededFiles embed.FS) http.FileSystem {
2525
if useOS {
26-
log.Print("~~ using live mode for static files")
26+
log.Print("using live mode for static files")
2727
return http.FS(os.DirFS("crew-go-ui/dist/spa"))
2828
}
2929

30-
log.Print("~~ using embed mode for static files")
30+
log.Print("using embed mode for static files")
3131
fsys, err := fs.Sub(embededFiles, "crew-go-ui/dist/spa")
3232
if err != nil {
3333
panic(err)
@@ -367,7 +367,7 @@ func ServeRestApi(wg *sync.WaitGroup, controller *TaskController, embededFiles e
367367

368368
// Demo worker endpoints
369369
e.POST("/demo/worker-a", func(c echo.Context) error {
370-
fmt.Println("~~ Demo worker A has been called!")
370+
log.Println("Demo worker A has been called!")
371371
time.Sleep(5 * time.Second)
372372

373373
payload := map[string]interface{}{}
@@ -391,7 +391,7 @@ func ServeRestApi(wg *sync.WaitGroup, controller *TaskController, embededFiles e
391391
})
392392
// Worker B is pretty much identical to worker A
393393
e.POST("/demo/worker-b", func(c echo.Context) error {
394-
fmt.Println("~~ Demo worker B has been called!")
394+
log.Println("Demo worker B has been called!")
395395
time.Sleep(7 * time.Second)
396396

397397
payload := map[string]interface{}{}
@@ -415,7 +415,7 @@ func ServeRestApi(wg *sync.WaitGroup, controller *TaskController, embededFiles e
415415
})
416416
// Worker C returns child (continuation) tasks
417417
e.POST("/demo/worker-c", func(c echo.Context) error {
418-
fmt.Println("~~ Demo worker C has been called!")
418+
log.Println("Demo worker C has been called!")
419419
time.Sleep(2 * time.Second)
420420

421421
payload := map[string]interface{}{}
@@ -510,7 +510,7 @@ func ServeRestApi(wg *sync.WaitGroup, controller *TaskController, embededFiles e
510510
}
511511
}
512512
default:
513-
fmt.Printf("I don't know how to handle feed message of type %T!\n", v)
513+
log.Printf("I don't know how to handle feed message of type %T!\n", v)
514514
}
515515
}
516516
}()

crew/task_client.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"log"
910
"net/http"
1011
"os"
1112
"time"
@@ -54,7 +55,7 @@ func NewHttpPostClient() *HttpPostClient {
5455
port = "8090"
5556
}
5657
baseUrl = "http://localhost:" + port + "/demo/"
57-
fmt.Println("CREW_WORKER_BASE_URL not set, defaulting to " + baseUrl)
58+
log.Println("CREW_WORKER_BASE_URL not set, defaulting to " + baseUrl)
5859

5960
}
6061
return baseUrl + task.Worker, nil

crew/task_controller.go

+53-24
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package crew
22

33
import (
44
"fmt"
5+
"log"
56
"sort"
67
"strings"
78
"sync"
89
"time"
10+
11+
"github.com/go-co-op/gocron"
912
)
1013

1114
// A ThrottlePushQuery is a request to the throttler to see if there is enough bandwidth for a worker to run.
@@ -28,11 +31,13 @@ type Throttler struct {
2831

2932
// TaskGroup represents a group of tasks.
3033
type TaskController struct {
31-
Storage TaskStorage
32-
Client TaskClient
33-
Feed chan interface{}
34-
Throttler *Throttler
35-
Pending *sync.WaitGroup
34+
Storage TaskStorage
35+
Client TaskClient
36+
Feed chan interface{}
37+
Throttler *Throttler
38+
Pending *sync.WaitGroup
39+
AbandonedCheckScheduler *gocron.Scheduler
40+
AbandonedCheckMutex *sync.Mutex
3641
}
3742

3843
// NewTaskController returns a new TaskController.
@@ -43,6 +48,8 @@ func NewTaskController(storage TaskStorage, client TaskClient, throttler *Thrott
4348
Feed: make(chan interface{}, 8),
4449
Throttler: throttler,
4550
Pending: &sync.WaitGroup{},
51+
// AbandonedCheckScheduler is created in startup
52+
AbandonedCheckMutex: &sync.Mutex{},
4653
}
4754
}
4855

@@ -492,53 +499,75 @@ func (controller *TaskController) UpdateTask(id string, update map[string]interf
492499
}
493500

494501
func (controller *TaskController) Startup() (err error) {
495-
// Fire an evaluate for any tasks that are incomplete and unpaused
502+
// Restart tasks on startup and/or check for tasks that may have been abandoned due to crashes (or power outages) during execution.
503+
// Note that for this to work for abandonments the storage mechanism must have expirations on task locks.
504+
// Only the redis storage mechanism currently supports this.
505+
s := gocron.NewScheduler(time.UTC)
506+
// TODO - configure interval with an env var?
507+
s.Every(15).Minutes().Do(func() {
508+
log.Println("Abandoned task scan starting")
509+
510+
// Use a mutex to make sure this doesn't run more than once at a time
511+
locked := controller.AbandonedCheckMutex.TryLock()
512+
if !locked {
513+
log.Println("Previous abandoned task scan still running, bailing out.")
514+
return
515+
}
516+
defer controller.AbandonedCheckMutex.Unlock()
496517

497-
go func() {
498-
groups, groupsError := controller.Storage.AllTaskGroups()
499-
if groupsError == nil {
500-
for _, group := range groups {
501-
fmt.Println("~~ Bootstrapping tasks for group", group.Id)
518+
taskGroups, taskGroupsError := controller.Storage.AllTaskGroups()
519+
if taskGroupsError == nil {
520+
for _, group := range taskGroups {
502521
tasks, tasksError := controller.Storage.AllTasksInGroup(group.Id)
503522
if tasksError == nil {
504523
for _, task := range tasks {
505-
if !task.IsComplete && !task.IsPaused {
524+
// Do a couple of quick checks to prevent unecessary evaluates
525+
if !task.IsComplete && !task.IsPaused && task.RunAfter.After(time.Now()) && task.RemainingAttempts > 0 {
506526
controller.TriggerTaskEvaluate(task.Id)
507527
// Slight pause here to prevent a flood of evaluates
508528
time.Sleep(time.Second / 10)
509529
}
510530
}
511531
} else {
512-
fmt.Println("~~ Error bootstrapping tasks", tasksError)
532+
log.Println("Error scanning for abandoned tasks", tasksError)
513533
}
514-
// Longer pause between groups to prevent overloading ourselves.
534+
535+
// Pause between groups to prevent overloading ourselves.
515536
time.Sleep(time.Second * 1)
516537
}
517538
} else {
518-
fmt.Println("~~ Error bootstrapping task groups", groupsError)
539+
log.Println("Error scanning for abandoned tasks (fetch groups)", taskGroupsError)
519540
}
520-
}()
541+
542+
log.Println("Abandoned task scan completed")
543+
})
544+
s.StartAsync()
545+
controller.AbandonedCheckScheduler = s
521546

522547
return nil
523548
}
524549

525550
func (controller *TaskController) Shutdown() (err error) {
551+
if controller.AbandonedCheckScheduler != nil {
552+
controller.AbandonedCheckScheduler.Stop()
553+
}
554+
526555
// Wait till all pending task executions are complete
527556
controller.Pending.Wait()
528557
return nil
529558
}
530559

531560
func (controller *TaskController) Evaluate(task *Task) {
532561
parents, _ := controller.Storage.GetTaskParents(task.Id)
533-
fmt.Println("~~ Evaluating task", task.Id, len(parents))
562+
log.Println("Evaluating task", task.Id, len(parents))
534563
canExecute := task.CanExecute(parents)
535564
if canExecute {
536565
controller.Execute(task)
537566
}
538567
}
539568

540569
func (controller *TaskController) Execute(taskToExecute *Task) {
541-
fmt.Println("~~ Executing task", taskToExecute.Id)
570+
log.Println("Executing task", taskToExecute.Id)
542571
parents, _ := controller.Storage.GetTaskParents(taskToExecute.Id)
543572

544573
timer := time.NewTimer(1000 * time.Second)
@@ -548,10 +577,10 @@ func (controller *TaskController) Execute(taskToExecute *Task) {
548577
go func() {
549578
defer controller.Pending.Done()
550579

551-
fmt.Println("~~ Waiting for task start time (go routine)", taskToExecute.Id)
580+
log.Println("Waiting for task start time (go routine)", taskToExecute.Id)
552581
<-timer.C
553582

554-
fmt.Println("~~ Executing task (go routine)", taskToExecute.Id)
583+
log.Println("Executing task (go routine)", taskToExecute.Id)
555584

556585
// Lock is as close to worker request send as possible (in case task delay is longer than lock timeout)
557586
unlocker, lockError := controller.Storage.TryLockTask(taskToExecute.Id)
@@ -564,7 +593,7 @@ func (controller *TaskController) Execute(taskToExecute *Task) {
564593

565594
if lockError != nil {
566595
// Couldn't lock task, do not execute
567-
fmt.Println("~~ Executing task (lock fail)", taskToExecute.Id)
596+
log.Println("Executing task (lock fail)", taskToExecute.Id)
568597
return
569598
}
570599

@@ -619,10 +648,10 @@ func (controller *TaskController) Execute(taskToExecute *Task) {
619648
task.BusyExecuting = false
620649

621650
if err != nil {
622-
fmt.Println("~~ Got standard error", err)
651+
log.Println("Got standard error", task.Id, err)
623652
controller.HandleExecuteError(task, fmt.Sprintf("%v", err))
624653
} else if workerResponse.Error != nil {
625-
fmt.Println("~~ Got worker response error", workerResponse.Error)
654+
log.Println("Got worker response error", task.Id, workerResponse.Error)
626655
controller.HandleExecuteError(task, fmt.Sprintf("%v", workerResponse.Error))
627656
} else {
628657
// No error!
@@ -674,7 +703,7 @@ func (controller *TaskController) Execute(taskToExecute *Task) {
674703

675704
// Because children failed we have to fail the task so that users will know something went wrong.
676705
task.IsComplete = false
677-
fmt.Println("~~ Got child creation error", errorCreatingChildren)
706+
log.Println("Got child creation error", task.Id, errorCreatingChildren)
678707
controller.HandleExecuteError(task, fmt.Sprintf("Child create failure : %v", errorCreatingChildren))
679708
} else {
680709
for _, child := range createdChildren {

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ require (
1414
require (
1515
github.com/cespare/xxhash/v2 v2.2.0 // indirect
1616
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
17+
github.com/go-co-op/gocron v1.27.1 // indirect
1718
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
1819
github.com/hashicorp/errwrap v1.1.0 // indirect
1920
github.com/hashicorp/go-multierror v1.1.1 // indirect
2021
github.com/labstack/gommon v0.4.0 // indirect
2122
github.com/mattn/go-colorable v0.1.13 // indirect
2223
github.com/mattn/go-isatty v0.0.17 // indirect
24+
github.com/robfig/cron/v3 v3.0.1 // indirect
2325
github.com/valyala/bytebufferpool v1.0.0 // indirect
2426
github.com/valyala/fasttemplate v1.2.2 // indirect
27+
go.uber.org/atomic v1.9.0 // indirect
2528
golang.org/x/crypto v0.6.0 // indirect
2629
golang.org/x/sys v0.5.0 // indirect
2730
golang.org/x/text v0.7.0 // indirect

go.sum

+18
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
55
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
66
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
77
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
8+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
89
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
910
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1011
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1112
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
1213
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
1314
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
1415
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
16+
github.com/go-co-op/gocron v1.27.1 h1:fYmK6COvF3rdFBbB4yQGWaf6TKIMjPv+1oaFrVx9bl8=
17+
github.com/go-co-op/gocron v1.27.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y=
1518
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
1619
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
1720
github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
@@ -49,8 +52,11 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
4952
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
5053
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
5154
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
55+
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
56+
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
5257
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
5358
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
59+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
5460
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
5561
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
5662
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
@@ -72,20 +78,27 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
7278
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
7379
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
7480
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
81+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
7582
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7683
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7784
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
7885
github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc=
7986
github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
87+
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
88+
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
89+
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
90+
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
8091
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8192
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
8293
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
94+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
8395
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
8496
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8597
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8698
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
8799
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
88100
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
101+
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
89102
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
90103
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
91104
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -94,6 +107,8 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
94107
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
95108
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
96109
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
110+
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
111+
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
97112
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
98113
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
99114
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -157,7 +172,10 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
157172
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
158173
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
159174
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
175+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
160176
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
177+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
178+
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
161179
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
162180
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
163181
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

0 commit comments

Comments
 (0)