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  }