Skip to content

Commit cbc5871

Browse files
add watchdog goroutine for docker (#898)
1 parent ba6d0bf commit cbc5871

File tree

9 files changed

+122
-3
lines changed

9 files changed

+122
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ All notable changes to `src-cli` are documented in this file.
1313

1414
### Added
1515

16+
- Batch Changes: Watchdog that checks for docker responsiveness while running commands has been added. [#898](https://github.com/sourcegraph/src-cli/pull/898)
17+
1618
### Changed
1719

1820
### Fixed

cmd/src/batch_common.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ import (
3232
"github.com/sourcegraph/src-cli/internal/batches/repozip"
3333
"github.com/sourcegraph/src-cli/internal/batches/service"
3434
"github.com/sourcegraph/src-cli/internal/batches/ui"
35+
"github.com/sourcegraph/src-cli/internal/batches/watchdog"
3536
"github.com/sourcegraph/src-cli/internal/batches/workspace"
3637
"github.com/sourcegraph/src-cli/internal/cmderrors"
3738
)
3839

40+
// We check for docker responsiveness every minute
41+
const dockerWatchDuration = 1 * time.Minute
42+
3943
// batchExecutionFlags are common to batch changes that are executed both
4044
// locally and remotely.
4145
type batchExecutionFlags struct {
@@ -251,6 +255,15 @@ type executeBatchSpecOpts struct {
251255
client api.Client
252256
}
253257

258+
func createDockerWatchdog(ctx context.Context, execUI ui.ExecUI) *watchdog.WatchDog {
259+
return watchdog.New(dockerWatchDuration, func() {
260+
_, err := docker.NCPU(ctx)
261+
if err != nil {
262+
execUI.DockerWatchDogWarning(errors.Wrap(err, "docker watchdog"))
263+
}
264+
})
265+
}
266+
254267
// executeBatchSpec performs all the steps required to upload the batch spec to
255268
// Sourcegraph, including execution as needed and applying the resulting batch
256269
// spec if specified.
@@ -263,7 +276,11 @@ func executeBatchSpec(ctx context.Context, opts executeBatchSpecOpts) (err error
263276
execUI = &ui.TUI{Out: out}
264277
}
265278

279+
w := createDockerWatchdog(ctx, execUI)
280+
go w.Start()
281+
266282
defer func() {
283+
w.Stop()
267284
if err != nil {
268285
execUI.ExecutionError(err)
269286
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ require (
2222
github.com/sourcegraph/go-diff v0.6.1
2323
github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf
2424
github.com/sourcegraph/scip v0.2.3
25-
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221129022201-2804729b069c
25+
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221207201520-eeeaf1f74c51
2626
github.com/stretchr/testify v1.8.0
2727
golang.org/x/net v0.2.0
2828
golang.org/x/sync v0.1.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@ github.com/sourcegraph/log v0.0.0-20220901143117-fc0516a694c9 h1:JjFyvx9hCD5+Jpu
378378
github.com/sourcegraph/log v0.0.0-20220901143117-fc0516a694c9/go.mod h1:UxiwB6C3xk3xOySJpW1R0MDUyfGuJRFS5Z8C+SA5p2I=
379379
github.com/sourcegraph/scip v0.2.3 h1:6nRsTlNTELn+1V7JxLrDVbBpb9H1gAbUF48N7x5B8Y4=
380380
github.com/sourcegraph/scip v0.2.3/go.mod h1:3D/RLCMvrrSam85RtFmC67SE/jhV9++hpCR5MTDzbA0=
381-
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221129022201-2804729b069c h1:bjTcIdEx6FvUXIbD1Ckjp7SfvV4jTyJ7RozZH/HVj9M=
382-
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221129022201-2804729b069c/go.mod h1:PmWmo2ysXWCHcbtp08cDXKGD+8NQzrkj2UjdRBu9H0k=
381+
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221207201520-eeeaf1f74c51 h1:G9XMyQ0SKC53PP9WPSkQqRFvwkZNYRdMHvdm5K3b8Lw=
382+
github.com/sourcegraph/sourcegraph/lib v0.0.0-20221207201520-eeeaf1f74c51/go.mod h1:pLXoYrwkmERkPVv1dpLcq0Epo21Kh4EgrdZXdnafmyQ=
383383
github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152 h1:z/MpntplPaW6QW95pzcAR/72Z5TWDyDnSo0EOcyij9o=
384384
github.com/sourcegraph/yaml v1.0.1-0.20200714132230-56936252f152/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
385385
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=

internal/batches/ui/exec_ui.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,6 @@ type ExecUI interface {
5252
UploadingWorkspaceFiles()
5353
UploadingWorkspaceFilesWarning(error)
5454
UploadingWorkspaceFilesSuccess()
55+
56+
DockerWatchDogWarning(error)
5557
}

internal/batches/ui/json_lines.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ func (ui *JSONLines) WriteAfterStepResult(key string, value execution.AfterStepR
177177
})
178178
}
179179

180+
func (ui *JSONLines) DockerWatchDogWarning(err error) {
181+
message := fmt.Sprintf(`It seems your Docker engine might be frozen.
182+
If there's no progress in the next couple minutes, you may want to try restarting Docker and running the command again.
183+
Error: %s
184+
`, err.Error())
185+
logOperationFailure(batcheslib.LogEventOperationDockerWatchDog, &batcheslib.DockerWatchDogMetadata{Error: message})
186+
}
187+
180188
type taskExecutionJSONLines struct {
181189
linesTasks map[*executor.Task]batcheslib.JSONLinesTask
182190
binaryDiffs bool

internal/batches/ui/tui.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ func (ui *TUI) CreatingBatchSpecError(err error) error {
221221
return prettyPrintBatchUnlicensedError(ui.Out, err)
222222
}
223223

224+
func (ui *TUI) DockerWatchDogWarning(err error) {
225+
dockerWatchDogWarning(ui.Out, err)
226+
}
227+
224228
func (ui *TUI) PreviewBatchSpec(batchSpecURL string) {
225229
ui.Out.Write("")
226230
block := ui.Out.Block(output.Line(batchSuccessEmoji, batchSuccessColor, "To preview or apply the batch spec, go to:"))
@@ -449,3 +453,11 @@ func batchCompletePending(p output.Pending, message string) {
449453
func batchCompleteWarning(p output.Pending, message string) {
450454
p.Complete(output.Line(batchWarningEmoji, batchWarningColor, message))
451455
}
456+
457+
func dockerWatchDogWarning(out *output.Output, err error) {
458+
block := out.Block(output.Line("🐳", output.StyleWarning, "It seems your Docker engine might be frozen."))
459+
block.WriteLine(output.Line("", output.StyleWarning, "If there's no progress in the next couple minutes, you may want to try restarting Docker and running the command again."))
460+
block.WriteLine(output.Linef("", output.StyleWarning, "Error: %s", err.Error()))
461+
block.Write("")
462+
block.Close()
463+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package watchdog
2+
3+
import (
4+
"time"
5+
6+
"github.com/derision-test/glock"
7+
)
8+
9+
type WatchDog struct {
10+
ticker glock.Ticker
11+
callback func()
12+
done chan struct{}
13+
}
14+
15+
func New(interval time.Duration, callback func()) *WatchDog {
16+
ticker := glock.NewRealTicker(interval)
17+
return &WatchDog{
18+
ticker: ticker,
19+
callback: callback,
20+
done: make(chan struct{}, 1),
21+
}
22+
}
23+
24+
func (w *WatchDog) Stop() {
25+
close(w.done)
26+
}
27+
28+
func (w *WatchDog) Start() {
29+
for {
30+
select {
31+
case <-w.ticker.Chan():
32+
go w.callback()
33+
case <-w.done:
34+
w.ticker.Stop()
35+
return
36+
}
37+
}
38+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package watchdog
2+
3+
import (
4+
"sync"
5+
"sync/atomic"
6+
"testing"
7+
"time"
8+
9+
"github.com/derision-test/glock"
10+
)
11+
12+
func TestWatchDog(t *testing.T) {
13+
ticker := glock.NewMockTicker(5 * time.Minute)
14+
var count uint32
15+
expectedCount := 100
16+
var wg sync.WaitGroup
17+
18+
mockCallback := func() {
19+
atomic.AddUint32(&count, 1)
20+
wg.Done()
21+
}
22+
23+
w := &WatchDog{
24+
ticker: ticker,
25+
callback: mockCallback,
26+
done: make(chan struct{}, 1),
27+
}
28+
29+
go w.Start()
30+
for i := 0; i < expectedCount; i++ {
31+
wg.Add(1)
32+
ticker.BlockingAdvance(5 * time.Minute)
33+
}
34+
wg.Wait()
35+
w.Stop()
36+
37+
if count != uint32(expectedCount) {
38+
t.Errorf("expected mock callback to be called %d times, got %d", expectedCount, count)
39+
}
40+
}

0 commit comments

Comments
 (0)