Skip to content

Commit f7a3eb5

Browse files
committed
custom generator
Signed-off-by: YaoZengzeng <[email protected]>
1 parent f0e9b8d commit f7a3eb5

File tree

1 file changed

+166
-3
lines changed

1 file changed

+166
-3
lines changed

Diff for: test/e2e/restart_test.go

+166-3
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,17 @@
2424
package kmesh
2525

2626
import (
27+
"bytes"
2728
"context"
2829
"fmt"
2930
"testing"
3031
"time"
3132

33+
"github.com/hashicorp/go-multierror"
34+
"istio.io/istio/pkg/test"
3235
"istio.io/istio/pkg/test/framework"
3336
"istio.io/istio/pkg/test/framework/components/echo"
34-
"istio.io/istio/pkg/test/framework/components/echo/util/traffic"
37+
"istio.io/istio/pkg/test/framework/components/echo/check"
3538
kubetest "istio.io/istio/pkg/test/kube"
3639
"istio.io/istio/pkg/test/util/retry"
3740
appsv1 "k8s.io/api/apps/v1"
@@ -54,13 +57,13 @@ func TestKmeshRestart(t *testing.T) {
5457
Retry: echo.Retry{NoRetry: true},
5558
}
5659

57-
g := traffic.NewGenerator(t, traffic.Config{
60+
g := NewGenerator(t, Config{
5861
Source: src,
5962
Options: options,
6063
Interval: 5 * time.Millisecond,
6164
}).Start()
6265

63-
for i := 0; i < 10; i++ {
66+
for i := 0; i < 30; i++ {
6467
restartKmesh(t)
6568
}
6669

@@ -107,3 +110,163 @@ func restartKmesh(t framework.TestContext) {
107110
func daemonsetsetComplete(ds *appsv1.DaemonSet) bool {
108111
return ds.Status.UpdatedNumberScheduled == ds.Status.DesiredNumberScheduled && ds.Status.NumberReady == ds.Status.DesiredNumberScheduled && ds.Status.ObservedGeneration >= ds.Generation
109112
}
113+
114+
const (
115+
defaultInterval = 1 * time.Second
116+
defaultTimeout = 15 * time.Second
117+
)
118+
119+
// Config for a traffic Generator.
120+
type Config struct {
121+
// Source of the traffic.
122+
Source echo.Caller
123+
124+
// Options for generating traffic from the Source to the target.
125+
Options echo.CallOptions
126+
127+
// Interval between successive call operations. If not set, defaults to 1 second.
128+
Interval time.Duration
129+
130+
// Maximum time to wait for traffic to complete after stopping. If not set, defaults to 15 seconds.
131+
StopTimeout time.Duration
132+
}
133+
134+
// Generator of traffic between echo instances. Every time interval
135+
// (as defined by Config.Interval), a grpc request is sent to the source pod,
136+
// causing it to send a request to the destination echo server. Results are
137+
// captured for each request for later processing.
138+
type Generator interface {
139+
// Start sending traffic.
140+
Start() Generator
141+
142+
// Stop sending traffic and wait for any in-flight requests to complete.
143+
// Returns the Result
144+
Stop() Result
145+
}
146+
147+
// NewGenerator returns a new Generator with the given configuration.
148+
func NewGenerator(t test.Failer, cfg Config) Generator {
149+
fillInDefaults(&cfg)
150+
return &generator{
151+
Config: cfg,
152+
t: t,
153+
stop: make(chan struct{}),
154+
stopped: make(chan struct{}),
155+
}
156+
}
157+
158+
var _ Generator = &generator{}
159+
160+
type generator struct {
161+
Config
162+
t test.Failer
163+
result Result
164+
stop chan struct{}
165+
stopped chan struct{}
166+
}
167+
168+
func (g *generator) Start() Generator {
169+
go func() {
170+
t := time.NewTimer(g.Interval)
171+
for {
172+
select {
173+
case <-g.stop:
174+
t.Stop()
175+
close(g.stopped)
176+
return
177+
case <-t.C:
178+
result := g.Source.CallOrFail(g.t, g.Options)
179+
for _, res := range result.Responses {
180+
if res.Code != "200" {
181+
g.t.Fatal("failed")
182+
}
183+
}
184+
g.result.add(result, nil)
185+
t.Reset(g.Interval)
186+
}
187+
}
188+
}()
189+
return g
190+
}
191+
192+
func (g *generator) Stop() Result {
193+
// Trigger the generator to stop.
194+
close(g.stop)
195+
196+
// Wait for the generator to exit.
197+
t := time.NewTimer(g.StopTimeout)
198+
select {
199+
case <-g.stopped:
200+
t.Stop()
201+
if g.result.TotalRequests == 0 {
202+
g.t.Fatal("no requests completed before stopping the traffic generator")
203+
}
204+
return g.result
205+
case <-t.C:
206+
g.t.Fatal("timed out waiting for result")
207+
}
208+
// Can never happen, but the compiler doesn't know that Fatal terminates
209+
return Result{}
210+
}
211+
212+
func fillInDefaults(cfg *Config) {
213+
if cfg.Interval == 0 {
214+
cfg.Interval = defaultInterval
215+
}
216+
if cfg.StopTimeout == 0 {
217+
cfg.StopTimeout = defaultTimeout
218+
}
219+
if cfg.Options.Check == nil {
220+
cfg.Options.Check = check.OK()
221+
}
222+
}
223+
224+
// Result of a traffic generation operation.
225+
type Result struct {
226+
TotalRequests int
227+
SuccessfulRequests int
228+
Error error
229+
}
230+
231+
func (r Result) String() string {
232+
buf := &bytes.Buffer{}
233+
234+
_, _ = fmt.Fprintf(buf, "TotalRequests: %d\n", r.TotalRequests)
235+
_, _ = fmt.Fprintf(buf, "SuccessfulRequests: %d\n", r.SuccessfulRequests)
236+
_, _ = fmt.Fprintf(buf, "PercentSuccess: %f\n", r.PercentSuccess())
237+
_, _ = fmt.Fprintf(buf, "Errors: %v\n", r.Error)
238+
239+
return buf.String()
240+
}
241+
242+
func (r *Result) add(result echo.CallResult, err error) {
243+
count := result.Responses.Len()
244+
if count == 0 {
245+
count = 1
246+
}
247+
248+
r.TotalRequests += count
249+
if err != nil {
250+
r.Error = multierror.Append(r.Error, fmt.Errorf("request %d: %v", r.TotalRequests, err))
251+
} else {
252+
r.SuccessfulRequests += count
253+
}
254+
}
255+
256+
func (r Result) PercentSuccess() float64 {
257+
return float64(r.SuccessfulRequests) / float64(r.TotalRequests)
258+
}
259+
260+
// CheckSuccessRate asserts that a minimum success threshold was met.
261+
func (r Result) CheckSuccessRate(t test.Failer, minimumPercent float64) {
262+
t.Helper()
263+
if r.PercentSuccess() < minimumPercent {
264+
t.Fatalf("Minimum success threshold, %f, was not met. %d/%d (%f) requests failed: %v",
265+
minimumPercent, r.SuccessfulRequests, r.TotalRequests, r.PercentSuccess(), r.Error)
266+
}
267+
if r.SuccessfulRequests == r.TotalRequests {
268+
t.Logf("traffic checker succeeded with all successful requests (%d/%d)", r.SuccessfulRequests, r.TotalRequests)
269+
} else {
270+
t.Logf("traffic checker met minimum threshold, with %d/%d successes, but encountered some failures: %v", r.SuccessfulRequests, r.TotalRequests, r.Error)
271+
}
272+
}

0 commit comments

Comments
 (0)