Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/tools/smoke/internal/smoke.go
4095 views
1
package smoke
2
3
import (
4
"context"
5
"flag"
6
"fmt"
7
"math/rand"
8
"net/http"
9
"time"
10
11
"github.com/go-kit/log"
12
"github.com/go-kit/log/level"
13
"github.com/oklog/run"
14
"k8s.io/client-go/kubernetes"
15
"k8s.io/client-go/tools/clientcmd"
16
)
17
18
// Smoke is the top level object for a smoke test.
19
type Smoke struct {
20
cfg *Config
21
logger log.Logger
22
tasks []repeatingTask
23
24
fakeRemoteWriteHandler http.HandlerFunc
25
}
26
27
// Config struct to pass configuration to the Smoke constructor.
28
type Config struct {
29
Kubeconfig string
30
Namespace string
31
PodPrefix string
32
FakeRemoteWrite bool
33
SimulateErrors bool
34
ErrorPercentage float64
35
ChaosFrequency time.Duration
36
MutationFrequency time.Duration
37
}
38
39
// RegisterFlags registers flags for the config to the given FlagSet.
40
func (c *Config) RegisterFlags(f *flag.FlagSet) {
41
f.StringVar(&c.Namespace, "namespace", DefaultConfig.Namespace, "namespace smoke test should run in")
42
f.StringVar(&c.Kubeconfig, "kubeconfig", DefaultConfig.Kubeconfig, "absolute path to the kubeconfig file")
43
f.StringVar(&c.PodPrefix, "pod-prefix", DefaultConfig.PodPrefix, "prefix for grafana agent pods")
44
f.BoolVar(&c.FakeRemoteWrite, "fake-remote-write", DefaultConfig.FakeRemoteWrite, "remote write endpoint for series which are discarded, useful for testing and not storing metrics")
45
f.BoolVar(&c.SimulateErrors, "simulate-errors", DefaultConfig.SimulateErrors, "remote write endpoint will return a 500 error response randomly")
46
f.Float64Var(&c.ErrorPercentage, "simulate-errors-percentage", DefaultConfig.ErrorPercentage, "percentage chance a request will result in an error")
47
f.DurationVar(&c.ChaosFrequency, "chaos-frequency", DefaultConfig.ChaosFrequency, "chaos frequency duration")
48
f.DurationVar(&c.MutationFrequency, "mutation-frequency", DefaultConfig.MutationFrequency, "mutation frequency duration")
49
}
50
51
// DefaultConfig holds defaults for Smoke settings.
52
var DefaultConfig = Config{
53
Kubeconfig: "",
54
Namespace: "default",
55
PodPrefix: "grafana-agent",
56
FakeRemoteWrite: false,
57
SimulateErrors: false,
58
ErrorPercentage: 0.01,
59
ChaosFrequency: 30 * time.Minute,
60
MutationFrequency: 5 * time.Minute,
61
}
62
63
// New is the constructor for a Smoke object.
64
func New(logger log.Logger, cfg Config) (*Smoke, error) {
65
s := &Smoke{
66
cfg: &cfg,
67
logger: logger,
68
}
69
if s.logger == nil {
70
s.logger = log.NewNopLogger()
71
}
72
73
// use the current context in kubeconfig. this falls back to in-cluster config if kubeconfig is empty
74
config, err := clientcmd.BuildConfigFromFlags("", cfg.Kubeconfig)
75
if err != nil {
76
return nil, err
77
}
78
// creates the clientset
79
clientset, err := kubernetes.NewForConfig(config)
80
if err != nil {
81
return nil, err
82
}
83
84
if cfg.FakeRemoteWrite {
85
s.fakeRemoteWriteHandler = func(w http.ResponseWriter, _ *http.Request) {
86
w.WriteHeader(http.StatusOK)
87
}
88
if cfg.SimulateErrors {
89
rand.Seed(time.Now().UnixNano()) //nolint:staticcheck
90
s.fakeRemoteWriteHandler = func(w http.ResponseWriter, _ *http.Request) {
91
if rnd := rand.Float64(); cfg.ErrorPercentage > rnd {
92
w.WriteHeader(http.StatusInternalServerError)
93
return
94
}
95
w.WriteHeader(http.StatusOK)
96
}
97
}
98
}
99
100
// add default tasks
101
s.tasks = append(s.tasks,
102
repeatingTask{
103
Task: &deletePodTask{
104
logger: log.With(s.logger, "task", "delete_pod", "pod", "grafana-agent-0"),
105
clientset: clientset,
106
namespace: cfg.Namespace,
107
pod: fmt.Sprintf("%s-0", cfg.PodPrefix),
108
},
109
frequency: cfg.ChaosFrequency,
110
},
111
repeatingTask{
112
Task: &deletePodBySelectorTask{
113
logger: log.With(s.logger, "task", "delete_pod_by_selector"),
114
clientset: clientset,
115
namespace: cfg.Namespace,
116
selector: fmt.Sprintf("name=%s-cluster", cfg.PodPrefix),
117
},
118
frequency: cfg.ChaosFrequency,
119
},
120
repeatingTask{
121
Task: &scaleDeploymentTask{
122
logger: log.With(s.logger, "task", "scale_deployment", "deployment", "avalanche"),
123
clientset: clientset,
124
namespace: cfg.Namespace,
125
deployment: "avalanche",
126
maxReplicas: 11,
127
minReplicas: 2,
128
},
129
frequency: cfg.MutationFrequency,
130
})
131
132
return s, nil
133
}
134
135
func (s *Smoke) ServeHTTP(w http.ResponseWriter, r *http.Request) {
136
s.fakeRemoteWriteHandler(w, r)
137
}
138
139
// Run starts the smoke test and runs the tasks concurrently.
140
func (s *Smoke) Run(ctx context.Context) error {
141
var g run.Group
142
ctx, cancel := context.WithCancel(ctx)
143
defer cancel()
144
taskFn := func(t repeatingTask) func() error {
145
return func() error {
146
tick := time.NewTicker(t.frequency)
147
defer tick.Stop()
148
for {
149
select {
150
case <-tick.C:
151
if err := t.Run(ctx); err != nil {
152
return err
153
}
154
case <-ctx.Done():
155
return nil
156
}
157
}
158
}
159
}
160
for _, task := range s.tasks {
161
g.Add(taskFn(task), func(_ error) {
162
cancel()
163
})
164
}
165
166
if s.cfg.FakeRemoteWrite && s.fakeRemoteWriteHandler != nil {
167
level.Info(s.logger).Log("msg", "serving fake remote-write endpoint on :19090")
168
g.Add(func() error {
169
return http.ListenAndServe(":19090", s)
170
}, func(_ error) {
171
cancel()
172
})
173
}
174
175
return g.Run()
176
}
177
178