-
Notifications
You must be signed in to change notification settings - Fork 460
/
manager.go
101 lines (83 loc) · 2.43 KB
/
manager.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: AGPL-3.0-only
package continuoustest
import (
"context"
"flag"
"time"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/dskit/modules"
"golang.org/x/sync/errgroup"
)
type Test interface {
// Name returns the test name. The name is used to uniquely identify the test in metrics and logs.
Name() string
// Init initializes the test. If the initialization fails, the testing tool will terminate.
Init(ctx context.Context, now time.Time) error
// Run runs a single test cycle. This function is called multiple times, at periodic intervals.
// The returned error is ignored unless smoke-test is enabled. In that case, the error is returned to the caller.
Run(ctx context.Context, now time.Time) error
}
type ManagerConfig struct {
SmokeTest bool
RunInterval time.Duration
}
func (cfg *ManagerConfig) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&cfg.SmokeTest, "tests.smoke-test", false, "Run a smoke test, i.e. run all tests once and exit.")
f.DurationVar(&cfg.RunInterval, "tests.run-interval", 5*time.Minute, "How frequently tests should run.")
}
type Manager struct {
cfg ManagerConfig
logger log.Logger
tests []Test
}
func NewManager(cfg ManagerConfig, logger log.Logger) *Manager {
return &Manager{
cfg: cfg,
logger: logger,
}
}
func (m *Manager) AddTest(t Test) {
m.tests = append(m.tests, t)
}
func (m *Manager) Run(ctx context.Context) error {
// Initialize all tests.
for _, t := range m.tests {
if err := t.Init(ctx, time.Now()); err != nil {
return err
}
}
// Continuously run all tests. Each test is executed in a dedicated goroutine.
group, ctx := errgroup.WithContext(ctx)
for _, test := range m.tests {
t := test
group.Go(func() error {
// Run it immediately, and then every configured period.
err := t.Run(ctx, time.Now())
if m.cfg.SmokeTest {
if err != nil {
level.Info(m.logger).Log("msg", "Test failed", "test", t.Name(), "err", err)
} else {
level.Info(m.logger).Log("msg", "Test passed", "test", t.Name())
}
return err
}
ticker := time.NewTicker(m.cfg.RunInterval)
for {
select {
case <-ticker.C:
// This error is intentionally ignored because we want to
// continue running the tests forever.
_ = t.Run(ctx, time.Now())
case <-ctx.Done():
return nil
}
}
})
}
err := group.Wait()
if err == nil && m.cfg.SmokeTest {
err = modules.ErrStopProcess
}
return err
}