istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/echo/util/traffic/generator.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package traffic 16 17 import ( 18 "time" 19 20 "istio.io/istio/pkg/test" 21 "istio.io/istio/pkg/test/framework/components/echo" 22 "istio.io/istio/pkg/test/framework/components/echo/check" 23 ) 24 25 const ( 26 defaultInterval = 1 * time.Second 27 defaultTimeout = 15 * time.Second 28 ) 29 30 // Config for a traffic Generator. 31 type Config struct { 32 // Source of the traffic. 33 Source echo.Caller 34 35 // Options for generating traffic from the Source to the target. 36 Options echo.CallOptions 37 38 // Interval between successive call operations. If not set, defaults to 1 second. 39 Interval time.Duration 40 41 // Maximum time to wait for traffic to complete after stopping. If not set, defaults to 15 seconds. 42 StopTimeout time.Duration 43 } 44 45 // Generator of traffic between echo instances. Every time interval 46 // (as defined by Config.Interval), a grpc request is sent to the source pod, 47 // causing it to send a request to the destination echo server. Results are 48 // captured for each request for later processing. 49 type Generator interface { 50 // Start sending traffic. 51 Start() Generator 52 53 // Stop sending traffic and wait for any in-flight requests to complete. 54 // Returns the Result 55 Stop() Result 56 } 57 58 // NewGenerator returns a new Generator with the given configuration. 59 func NewGenerator(t test.Failer, cfg Config) Generator { 60 fillInDefaults(&cfg) 61 return &generator{ 62 Config: cfg, 63 t: t, 64 stop: make(chan struct{}), 65 stopped: make(chan struct{}), 66 } 67 } 68 69 var _ Generator = &generator{} 70 71 type generator struct { 72 Config 73 t test.Failer 74 result Result 75 stop chan struct{} 76 stopped chan struct{} 77 } 78 79 func (g *generator) Start() Generator { 80 go func() { 81 t := time.NewTimer(g.Interval) 82 for { 83 select { 84 case <-g.stop: 85 t.Stop() 86 close(g.stopped) 87 return 88 case <-t.C: 89 g.result.add(g.Source.Call(g.Options)) 90 t.Reset(g.Interval) 91 } 92 } 93 }() 94 return g 95 } 96 97 func (g *generator) Stop() Result { 98 // Trigger the generator to stop. 99 close(g.stop) 100 101 // Wait for the generator to exit. 102 t := time.NewTimer(g.StopTimeout) 103 select { 104 case <-g.stopped: 105 t.Stop() 106 if g.result.TotalRequests == 0 { 107 g.t.Fatal("no requests completed before stopping the traffic generator") 108 } 109 return g.result 110 case <-t.C: 111 g.t.Fatal("timed out waiting for result") 112 } 113 // Can never happen, but the compiler doesn't know that Fatal terminates 114 return Result{} 115 } 116 117 func fillInDefaults(cfg *Config) { 118 if cfg.Interval == 0 { 119 cfg.Interval = defaultInterval 120 } 121 if cfg.StopTimeout == 0 { 122 cfg.StopTimeout = defaultTimeout 123 } 124 if cfg.Options.Check == nil { 125 cfg.Options.Check = check.OK() 126 } 127 }