github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-control-plane/pkg/test/main/main.go (about) 1 // Copyright 2018 Envoyproxy 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 main contains the test driver for testing xDS manually. 16 package main 17 18 import ( 19 "context" 20 cryptotls "crypto/tls" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "log" 25 "net/http" 26 "os" 27 "runtime" 28 "runtime/pprof" 29 "time" 30 31 "github.com/hxx258456/ccgo/go-control-plane/pkg/cache/v3" 32 "github.com/hxx258456/ccgo/go-control-plane/pkg/server/v3" 33 "github.com/hxx258456/ccgo/go-control-plane/pkg/test" 34 "github.com/hxx258456/ccgo/go-control-plane/pkg/test/resource/v3" 35 testv3 "github.com/hxx258456/ccgo/go-control-plane/pkg/test/v3" 36 ) 37 38 var ( 39 debug bool 40 41 port uint 42 gatewayPort uint 43 upstreamPort uint 44 upstreamMessage string 45 basePort uint 46 alsPort uint 47 48 delay time.Duration 49 requests int 50 updates int 51 52 mode string 53 clusters int 54 httpListeners int 55 tcpListeners int 56 runtimes int 57 tls bool 58 mux bool 59 extensionNum int 60 61 nodeID string 62 63 pprofEnabled bool 64 ) 65 66 func init() { 67 flag.BoolVar(&debug, "debug", false, "Use debug logging") 68 69 // 70 // These parameters control the ports that the integration test 71 // components use to talk to one another 72 // 73 74 // The port that the Envoy xDS client uses to talk to the control 75 // plane xDS server (part of this program) 76 flag.UintVar(&port, "port", 18000, "xDS management server port") 77 78 // The port that the Envoy REST client uses to talk to the control 79 // plane gateway (which translates from REST to xDS) 80 flag.UintVar(&gatewayPort, "gateway", 18001, "Management HTTP gateway (from HTTP to xDS) server port") 81 82 // The port that Envoy uses to talk to the upstream http "echo" 83 // server 84 flag.UintVar(&upstreamPort, "upstream", 18080, "Upstream HTTP/1.1 port") 85 86 // The port that the tests below use to talk to Envoy's proxy of the 87 // upstream server 88 flag.UintVar(&basePort, "base", 9000, "Envoy Proxy listener port") 89 90 // The control plane accesslog server port (currently unused) 91 flag.UintVar(&alsPort, "als", 18090, "Control plane accesslog server port") 92 93 // 94 // These parameters control Envoy configuration 95 // 96 97 // Tell Envoy to request configurations from the control plane using 98 // this protocol 99 flag.StringVar(&mode, "xds", resource.Ads, "Management protocol to test (ADS, xDS, REST, DELTA, DELTA-ADS)") 100 101 // Tell Envoy to use this Node ID 102 flag.StringVar(&nodeID, "nodeID", "test-id", "Node ID") 103 104 // Tell Envoy to use TLS to talk to the control plane 105 flag.BoolVar(&tls, "tls", false, "Enable TLS on all listeners and use SDS for secret delivery") 106 107 // Tell Envoy to configure this many clusters for each snapshot 108 flag.IntVar(&clusters, "clusters", 4, "Number of clusters") 109 110 // Tell Envoy to configure this many Runtime Discovery Service 111 // layers for each snapshot 112 flag.IntVar(&runtimes, "runtimes", 1, "Number of RTDS layers") 113 114 // 115 // These parameters control the test harness 116 // 117 118 // The message that the tests expect to receive from the upstream 119 // server 120 flag.StringVar(&upstreamMessage, "message", "Default message", "Upstream HTTP server response message") 121 122 // Time to wait between test request batches 123 flag.DurationVar(&delay, "delay", 500*time.Millisecond, "Interval between request batch retries") 124 125 // Each test loads a configuration snapshot into the control plane 126 // which is then picked up by Envoy. This parameter specifies how 127 // many snapshots to test 128 flag.IntVar(&updates, "u", 3, "Number of snapshot updates") 129 130 // Each snapshot test sends this many requests to the upstream 131 // server for each snapshot for each listener port 132 flag.IntVar(&requests, "r", 5, "Number of requests between snapshot updates") 133 134 // Test this many HTTP listeners per snapshot 135 flag.IntVar(&httpListeners, "http", 2, "Number of HTTP listeners (and RDS configs)") 136 // Test this many TCP listeners per snapshot 137 flag.IntVar(&tcpListeners, "tcp", 2, "Number of TCP pass-through listeners") 138 139 // Enable a muxed cache with partial snapshots 140 flag.BoolVar(&mux, "mux", false, "Enable muxed linear cache for EDS") 141 142 // Number of ExtensionConfig 143 flag.IntVar(&extensionNum, "extension", 1, "Number of Extension") 144 // 145 // These parameters control the the use of the pprof profiler 146 // 147 148 // Enable use of the pprof profiler 149 flag.BoolVar(&pprofEnabled, "pprof", false, "Enable use of the pprof profiler") 150 151 } 152 153 // main returns code 1 if any of the batches failed to pass all requests 154 func main() { 155 flag.Parse() 156 ctx := context.Background() 157 158 if pprofEnabled { 159 runtime.SetBlockProfileRate(1) 160 for _, prof := range []string{"block", "goroutine", "mutex"} { 161 log.Printf("turn on pprof %s profiler", prof) 162 if pprof.Lookup(prof) == nil { 163 pprof.NewProfile(prof) 164 } 165 } 166 } 167 168 // create a cache 169 signal := make(chan struct{}) 170 cb := &testv3.Callbacks{Signal: signal, Debug: debug} 171 172 // mux integration 173 config := cache.NewSnapshotCache(mode == resource.Ads, cache.IDHash{}, logger{}) 174 var configCache cache.Cache = config 175 typeURL := "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment" 176 eds := cache.NewLinearCache(typeURL) 177 if mux { 178 configCache = &cache.MuxCache{ 179 Classify: func(req *cache.Request) string { 180 if req.TypeUrl == typeURL { 181 return "eds" 182 } 183 return "default" 184 }, 185 Caches: map[string]cache.Cache{ 186 "default": config, 187 "eds": eds, 188 }, 189 } 190 } 191 srv := server.NewServer(context.Background(), configCache, cb) 192 als := &testv3.AccessLogService{} 193 194 // create a test snapshot 195 snapshots := resource.TestSnapshot{ 196 Xds: mode, 197 UpstreamPort: uint32(upstreamPort), 198 BasePort: uint32(basePort), 199 NumClusters: clusters, 200 NumHTTPListeners: httpListeners, 201 NumTCPListeners: tcpListeners, 202 TLS: tls, 203 NumRuntimes: runtimes, 204 NumExtension: extensionNum, 205 } 206 207 // start the xDS server 208 go test.RunAccessLogServer(ctx, als, alsPort) 209 go test.RunManagementServer(ctx, srv, port) 210 go test.RunManagementGateway(ctx, srv, gatewayPort, logger{}) 211 212 log.Println("waiting for the first request...") 213 select { 214 case <-signal: 215 break 216 case <-time.After(1 * time.Minute): 217 log.Println("timeout waiting for the first request") 218 os.Exit(1) 219 } 220 log.Printf("initial snapshot %+v\n", snapshots) 221 log.Printf("executing sequence updates=%d request=%d\n", updates, requests) 222 223 for i := 0; i < updates; i++ { 224 snapshots.Version = fmt.Sprintf("v%d", i) 225 log.Printf("update snapshot %v\n", snapshots.Version) 226 227 snapshot := snapshots.Generate() 228 if err := snapshot.Consistent(); err != nil { 229 log.Printf("snapshot inconsistency: %+v\n", snapshot) 230 } 231 232 err := config.SetSnapshot(context.Background(), nodeID, snapshot) 233 if err != nil { 234 log.Printf("snapshot error %q for %+v\n", err, snapshot) 235 os.Exit(1) 236 } 237 238 if mux { 239 for name, res := range snapshot.GetResources(typeURL) { 240 if err := eds.UpdateResource(name, res); err != nil { 241 log.Printf("update error %q for %+v\n", err, name) 242 os.Exit(1) 243 244 } 245 } 246 } 247 248 // pass is true if all requests succeed at least once in a run 249 pass := false 250 for j := 0; j < requests; j++ { 251 ok, failed := callEcho() 252 if failed == 0 && !pass { 253 pass = true 254 } 255 log.Printf("request batch %d, ok %v, failed %v, pass %v\n", j, ok, failed, pass) 256 select { 257 case <-time.After(delay): 258 case <-ctx.Done(): 259 return 260 } 261 } 262 263 als.Dump(func(s string) { 264 if debug { 265 log.Println(s) 266 } 267 }) 268 cb.Report() 269 270 if !pass { 271 log.Printf("failed all requests in a run %d\n", i) 272 os.Exit(1) 273 } 274 } 275 276 if pprofEnabled { 277 for _, prof := range []string{"block", "goroutine", "mutex"} { 278 p := pprof.Lookup(prof) 279 filePath := fmt.Sprintf("%s_profile_%s.pb.gz", prof, mode) 280 log.Printf("storing %s profile for %s in %s", prof, mode, filePath) 281 f, err := os.Create(filePath) 282 if err != nil { 283 log.Fatalf("could not create %s profile %s: %s", prof, filePath, err) 284 } 285 p.WriteTo(f, 1) // nolint:errcheck 286 f.Close() 287 } 288 } 289 290 log.Printf("Test for %s passed!\n", mode) 291 } 292 293 // callEcho calls upstream echo service on all listener ports and returns an error 294 // if any of the listeners returned an error. 295 func callEcho() (int, int) { 296 total := httpListeners + tcpListeners 297 ok, failed := 0, 0 298 ch := make(chan error, total) 299 300 // spawn requests 301 for i := 0; i < total; i++ { 302 go func(i int) { 303 client := http.Client{ 304 Timeout: 100 * time.Millisecond, 305 Transport: &http.Transport{ 306 TLSClientConfig: &cryptotls.Config{InsecureSkipVerify: true}, // nolint:gosec 307 }, 308 } 309 proto := "http" 310 if tls { 311 proto = "https" 312 } 313 req, err := client.Get(fmt.Sprintf("%s://127.0.0.1:%d", proto, basePort+uint(i))) 314 if err != nil { 315 ch <- err 316 return 317 } 318 body, err := ioutil.ReadAll(req.Body) 319 if err != nil { 320 req.Body.Close() 321 ch <- err 322 return 323 } 324 if err := req.Body.Close(); err != nil { 325 ch <- err 326 return 327 } 328 if string(body) != upstreamMessage { 329 ch <- fmt.Errorf("unexpected return %q", string(body)) 330 return 331 } 332 ch <- nil 333 }(i) 334 } 335 336 for { 337 out := <-ch 338 if out == nil { 339 ok++ 340 } else { 341 failed++ 342 } 343 if ok+failed == total { 344 return ok, failed 345 } 346 } 347 } 348 349 type logger struct{} 350 351 func (logger logger) Debugf(format string, args ...interface{}) { 352 if debug { 353 log.Printf(format+"\n", args...) 354 } 355 } 356 357 func (logger logger) Infof(format string, args ...interface{}) { 358 if debug { 359 log.Printf(format+"\n", args...) 360 } 361 } 362 363 func (logger logger) Warnf(format string, args ...interface{}) { 364 log.Printf(format+"\n", args...) 365 } 366 367 func (logger logger) Errorf(format string, args ...interface{}) { 368 log.Printf(format+"\n", args...) 369 }