github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/testutils/e2e/server.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package e2e provides utilities for end2end testing of xDS functionality.
    20  package e2e
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"net"
    26  	"reflect"
    27  	"strconv"
    28  
    29  	v3clusterpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/cluster/v3"
    30  	v3endpointpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/endpoint/v3"
    31  	v3listenerpb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3"
    32  	v3routepb "github.com/hxx258456/ccgo/go-control-plane/envoy/config/route/v3"
    33  	v3discoverygrpc "github.com/hxx258456/ccgo/go-control-plane/envoy/service/discovery/v3"
    34  	"github.com/hxx258456/ccgo/go-control-plane/pkg/cache/types"
    35  	v3cache "github.com/hxx258456/ccgo/go-control-plane/pkg/cache/v3"
    36  	v3resource "github.com/hxx258456/ccgo/go-control-plane/pkg/resource/v3"
    37  	v3server "github.com/hxx258456/ccgo/go-control-plane/pkg/server/v3"
    38  
    39  	grpc "github.com/hxx258456/ccgo/grpc"
    40  	"github.com/hxx258456/ccgo/grpc/grpclog"
    41  )
    42  
    43  var logger = grpclog.Component("xds-e2e")
    44  
    45  // serverLogger implements the Logger interface defined at
    46  // envoyproxy/go-control-plane/pkg/log. This is passed to the Snapshot cache.
    47  type serverLogger struct{}
    48  
    49  func (l serverLogger) Debugf(format string, args ...interface{}) {
    50  	msg := fmt.Sprintf(format, args...)
    51  	logger.InfoDepth(1, msg)
    52  }
    53  func (l serverLogger) Infof(format string, args ...interface{}) {
    54  	msg := fmt.Sprintf(format, args...)
    55  	logger.InfoDepth(1, msg)
    56  }
    57  func (l serverLogger) Warnf(format string, args ...interface{}) {
    58  	msg := fmt.Sprintf(format, args...)
    59  	logger.WarningDepth(1, msg)
    60  }
    61  func (l serverLogger) Errorf(format string, args ...interface{}) {
    62  	msg := fmt.Sprintf(format, args...)
    63  	logger.ErrorDepth(1, msg)
    64  }
    65  
    66  // ManagementServer is a thin wrapper around the xDS control plane
    67  // implementation provided by envoyproxy/go-control-plane.
    68  type ManagementServer struct {
    69  	// Address is the host:port on which the management server is listening for
    70  	// new connections.
    71  	Address string
    72  
    73  	cancel  context.CancelFunc    // To stop the v3 ADS service.
    74  	xs      v3server.Server       // v3 implementation of ADS.
    75  	gs      *grpc.Server          // gRPC server which exports the ADS service.
    76  	cache   v3cache.SnapshotCache // Resource snapshot.
    77  	version int                   // Version of resource snapshot.
    78  }
    79  
    80  // StartManagementServer initializes a management server which implements the
    81  // AggregatedDiscoveryService endpoint. The management server is initialized
    82  // with no resources. Tests should call the Update() method to change the
    83  // resource snapshot held by the management server, as required by the test
    84  // logic. When the test is done, it should call the Stop() method to cleanup
    85  // resources allocated by the management server.
    86  func StartManagementServer() (*ManagementServer, error) {
    87  	// Create a snapshot cache.
    88  	cache := v3cache.NewSnapshotCache(true, v3cache.IDHash{}, serverLogger{})
    89  	logger.Infof("Created new snapshot cache...")
    90  
    91  	lis, err := net.Listen("tcp", "localhost:0")
    92  	if err != nil {
    93  		return nil, fmt.Errorf("failed to start xDS management server: %v", err)
    94  	}
    95  
    96  	// Create an xDS management server and register the ADS implementation
    97  	// provided by it on a gRPC server. Cancelling the context passed to the
    98  	// server is the only way of stopping it at the end of the test.
    99  	ctx, cancel := context.WithCancel(context.Background())
   100  	xs := v3server.NewServer(ctx, cache, v3server.CallbackFuncs{})
   101  	gs := grpc.NewServer()
   102  	v3discoverygrpc.RegisterAggregatedDiscoveryServiceServer(gs, xs)
   103  	logger.Infof("Registered Aggregated Discovery Service (ADS)...")
   104  
   105  	// Start serving.
   106  	go gs.Serve(lis)
   107  	logger.Infof("xDS management server serving at: %v...", lis.Addr().String())
   108  
   109  	return &ManagementServer{
   110  		Address: lis.Addr().String(),
   111  		cancel:  cancel,
   112  		version: 0,
   113  		gs:      gs,
   114  		xs:      xs,
   115  		cache:   cache,
   116  	}, nil
   117  }
   118  
   119  // UpdateOptions wraps parameters to be passed to the Update() method.
   120  type UpdateOptions struct {
   121  	// NodeID is the id of the client to which this update is to be pushed.
   122  	NodeID string
   123  	// Endpoints, Clusters, Routes, and Listeners are the updated list of xds
   124  	// resources for the server.  All must be provided with each Update.
   125  	Endpoints []*v3endpointpb.ClusterLoadAssignment
   126  	Clusters  []*v3clusterpb.Cluster
   127  	Routes    []*v3routepb.RouteConfiguration
   128  	Listeners []*v3listenerpb.Listener
   129  	// SkipValidation indicates whether we want to skip validation (by not
   130  	// calling snapshot.Consistent()). It can be useful for negative tests,
   131  	// where we send updates that the client will NACK.
   132  	SkipValidation bool
   133  }
   134  
   135  // Update changes the resource snapshot held by the management server, which
   136  // updates connected clients as required.
   137  func (s *ManagementServer) Update(ctx context.Context, opts UpdateOptions) error {
   138  	s.version++
   139  
   140  	// Create a snapshot with the passed in resources.
   141  	resources := map[v3resource.Type][]types.Resource{
   142  		v3resource.ListenerType: resourceSlice(opts.Listeners),
   143  		v3resource.RouteType:    resourceSlice(opts.Routes),
   144  		v3resource.ClusterType:  resourceSlice(opts.Clusters),
   145  		v3resource.EndpointType: resourceSlice(opts.Endpoints),
   146  	}
   147  	snapshot, err := v3cache.NewSnapshot(strconv.Itoa(s.version), resources)
   148  	if err != nil {
   149  		return fmt.Errorf("failed to create new snapshot cache: %v", err)
   150  
   151  	}
   152  	if !opts.SkipValidation {
   153  		if err := snapshot.Consistent(); err != nil {
   154  			return fmt.Errorf("failed to create new resource snapshot: %v", err)
   155  		}
   156  	}
   157  	logger.Infof("Created new resource snapshot...")
   158  
   159  	// Update the cache with the new resource snapshot.
   160  	if err := s.cache.SetSnapshot(ctx, opts.NodeID, snapshot); err != nil {
   161  		return fmt.Errorf("failed to update resource snapshot in management server: %v", err)
   162  	}
   163  	logger.Infof("Updated snapshot cache with resource snapshot...")
   164  	return nil
   165  }
   166  
   167  // Stop stops the management server.
   168  func (s *ManagementServer) Stop() {
   169  	if s.cancel != nil {
   170  		s.cancel()
   171  	}
   172  	s.gs.Stop()
   173  }
   174  
   175  // resourceSlice accepts a slice of any type of proto messages and returns a
   176  // slice of types.Resource.  Will panic if there is an input type mismatch.
   177  func resourceSlice(i interface{}) []types.Resource {
   178  	v := reflect.ValueOf(i)
   179  	rs := make([]types.Resource, v.Len())
   180  	for i := 0; i < v.Len(); i++ {
   181  		rs[i] = v.Index(i).Interface().(types.Resource)
   182  	}
   183  	return rs
   184  }