google.golang.org/grpc@v1.72.2/xds/server_ext_test.go (about)

     1  /*
     2   *
     3   * Copyright 2024 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 xds_test
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/google/go-cmp/cmp"
    33  	"github.com/google/uuid"
    34  	"google.golang.org/grpc"
    35  	"google.golang.org/grpc/codes"
    36  	"google.golang.org/grpc/connectivity"
    37  	"google.golang.org/grpc/credentials/insecure"
    38  	"google.golang.org/grpc/internal/grpctest"
    39  	"google.golang.org/grpc/internal/stubserver"
    40  	"google.golang.org/grpc/internal/testutils"
    41  	"google.golang.org/grpc/internal/testutils/xds/e2e"
    42  	"google.golang.org/grpc/internal/xds/bootstrap"
    43  	"google.golang.org/grpc/peer"
    44  	"google.golang.org/grpc/status"
    45  	"google.golang.org/grpc/xds"
    46  	"google.golang.org/grpc/xds/internal/xdsclient"
    47  
    48  	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
    49  	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
    50  	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    51  	testgrpc "google.golang.org/grpc/interop/grpc_testing"
    52  	testpb "google.golang.org/grpc/interop/grpc_testing"
    53  )
    54  
    55  type s struct {
    56  	grpctest.Tester
    57  }
    58  
    59  func Test(t *testing.T) {
    60  	grpctest.RunSubTests(t, s{})
    61  }
    62  
    63  const (
    64  	defaultTestTimeout      = 10 * time.Second
    65  	defaultTestShortTimeout = 10 * time.Millisecond // For events expected to *not* happen.
    66  )
    67  
    68  func hostPortFromListener(lis net.Listener) (string, uint32, error) {
    69  	host, p, err := net.SplitHostPort(lis.Addr().String())
    70  	if err != nil {
    71  		return "", 0, fmt.Errorf("net.SplitHostPort(%s) failed: %v", lis.Addr().String(), err)
    72  	}
    73  	port, err := strconv.ParseInt(p, 10, 32)
    74  	if err != nil {
    75  		return "", 0, fmt.Errorf("strconv.ParseInt(%s, 10, 32) failed: %v", p, err)
    76  	}
    77  	return host, uint32(port), nil
    78  }
    79  
    80  // servingModeChangeHandler handles changes to the serving mode of an
    81  // xDS-enabled gRPC server. It logs the changes and sends the new mode and any
    82  // errors on appropriate channels for the test to consume.
    83  type servingModeChangeHandler struct {
    84  	logger interface {
    85  		Logf(format string, args ...any)
    86  	}
    87  	modeCh chan connectivity.ServingMode
    88  	errCh  chan error
    89  }
    90  
    91  func newServingModeChangeHandler(t *testing.T) *servingModeChangeHandler {
    92  	return &servingModeChangeHandler{
    93  		logger: t,
    94  		modeCh: make(chan connectivity.ServingMode, 1),
    95  		errCh:  make(chan error, 1),
    96  	}
    97  }
    98  
    99  func (m *servingModeChangeHandler) modeChangeCallback(addr net.Addr, args xds.ServingModeChangeArgs) {
   100  	m.logger.Logf("Serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
   101  	m.modeCh <- args.Mode
   102  	if args.Err != nil {
   103  		m.errCh <- args.Err
   104  	}
   105  }
   106  
   107  // createStubServer creates a new xDS-enabled gRPC server and returns a
   108  // stubserver.StubServer that can be used for testing. The server is configured
   109  // with the provided modeChangeOpt and xdsclient.Pool.
   110  func createStubServer(t *testing.T, lis net.Listener, opts ...grpc.ServerOption) *stubserver.StubServer {
   111  	stub := &stubserver.StubServer{
   112  		Listener: lis,
   113  		EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
   114  			return &testpb.Empty{}, nil
   115  		},
   116  		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
   117  			for {
   118  				if _, err := stream.Recv(); err == io.EOF {
   119  					return nil
   120  				} else if err != nil {
   121  					return err
   122  				}
   123  			}
   124  		},
   125  	}
   126  	server, err := xds.NewGRPCServer(opts...)
   127  	if err != nil {
   128  		t.Fatalf("Failed to create an xDS enabled gRPC server: %v", err)
   129  	}
   130  	stub.S = server
   131  	stubserver.StartTestService(t, stub)
   132  	t.Cleanup(stub.Stop)
   133  	return stub
   134  }
   135  
   136  // waitForSuccessfulRPC waits for an RPC to succeed, repeatedly calling the
   137  // EmptyCall RPC on the provided client connection until the call succeeds.
   138  // If the context is canceled or the expected error is not before the context
   139  // timeout expires, the test will fail.
   140  func waitForSuccessfulRPC(ctx context.Context, t *testing.T, cc *grpc.ClientConn, opts ...grpc.CallOption) {
   141  	t.Helper()
   142  
   143  	client := testgrpc.NewTestServiceClient(cc)
   144  	for {
   145  		select {
   146  		case <-ctx.Done():
   147  			t.Fatalf("Timeout waiting for RPCs to succeed")
   148  		case <-time.After(defaultTestShortTimeout):
   149  			if _, err := client.EmptyCall(ctx, &testpb.Empty{}, opts...); err == nil {
   150  				return
   151  			}
   152  		}
   153  	}
   154  }
   155  
   156  // waitForFailedRPCWithStatus waits for an RPC to fail with the expected status
   157  // code, error message, and node ID.  It repeatedly calls the EmptyCall RPC on
   158  // the provided client connection until the error matches the expected values.
   159  // If the context is canceled or the expected error is not before the context
   160  // timeout expires, the test will fail.
   161  func waitForFailedRPCWithStatus(ctx context.Context, t *testing.T, cc *grpc.ClientConn, wantCode codes.Code, wantErr, wantNodeID string) {
   162  	t.Helper()
   163  
   164  	client := testgrpc.NewTestServiceClient(cc)
   165  	var err error
   166  	for {
   167  		select {
   168  		case <-ctx.Done():
   169  			t.Fatalf("RPCs failed with most recent error: %v. Want status code %v, error: %s, node id: %s", err, wantCode, wantErr, wantNodeID)
   170  		case <-time.After(defaultTestShortTimeout):
   171  			_, err = client.EmptyCall(ctx, &testpb.Empty{})
   172  			if gotCode := status.Code(err); gotCode != wantCode {
   173  				continue
   174  			}
   175  			if gotErr := err.Error(); !strings.Contains(gotErr, wantErr) {
   176  				continue
   177  			}
   178  			if !strings.Contains(err.Error(), wantNodeID) {
   179  				continue
   180  			}
   181  			t.Logf("Most recent error happy case: %v", err.Error())
   182  			return
   183  		}
   184  	}
   185  }
   186  
   187  // Tests the basic scenario for an xDS enabled gRPC server.
   188  //
   189  //   - Verifies that the xDS enabled gRPC server requests for the expected
   190  //     listener resource.
   191  //   - Once the listener resource is received from the management server, it
   192  //     verifies that the xDS enabled gRPC server requests for the appropriate
   193  //     route configuration name. Also verifies that at this point, the server has
   194  //     not yet started serving RPCs
   195  //   - Once the route configuration is received from the management server, it
   196  //     verifies that the server can serve RPCs successfully.
   197  func (s) TestServer_Basic(t *testing.T) {
   198  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   199  	defer cancel()
   200  
   201  	// Start an xDS management server.
   202  	listenerNamesCh := make(chan []string, 1)
   203  	routeNamesCh := make(chan []string, 1)
   204  	managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
   205  		OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
   206  			switch req.GetTypeUrl() {
   207  			case "type.googleapis.com/envoy.config.listener.v3.Listener":
   208  				select {
   209  				case listenerNamesCh <- req.GetResourceNames():
   210  				case <-ctx.Done():
   211  				}
   212  			case "type.googleapis.com/envoy.config.route.v3.RouteConfiguration":
   213  				select {
   214  				case routeNamesCh <- req.GetResourceNames():
   215  				case <-ctx.Done():
   216  				}
   217  			}
   218  			return nil
   219  		},
   220  		AllowResourceSubset: true,
   221  	})
   222  
   223  	// Create bootstrap configuration pointing to the above management server.
   224  	nodeID := uuid.New().String()
   225  	bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address)
   226  
   227  	// Create a listener on a local port to act as the xDS enabled gRPC server.
   228  	lis, err := testutils.LocalTCPListener()
   229  	if err != nil {
   230  		t.Fatalf("Failed to listen to local port: %v", err)
   231  	}
   232  	host, port, err := hostPortFromListener(lis)
   233  	if err != nil {
   234  		t.Fatalf("Failed to retrieve host and port of server: %v", err)
   235  	}
   236  
   237  	// Configure the managegement server with a listener resource for the above
   238  	// xDS enabled gRPC server.
   239  	const routeConfigName = "routeName"
   240  	resources := e2e.UpdateOptions{
   241  		NodeID:         nodeID,
   242  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultServerListenerWithRouteConfigName(host, port, e2e.SecurityLevelNone, "routeName")},
   243  		SkipValidation: true,
   244  	}
   245  	if err := managementServer.Update(ctx, resources); err != nil {
   246  		t.Fatal(err)
   247  	}
   248  
   249  	// Start an xDS-enabled gRPC server with the above bootstrap configuration.
   250  	config, err := bootstrap.NewConfigFromContents(bootstrapContents)
   251  	if err != nil {
   252  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err)
   253  	}
   254  	pool := xdsclient.NewPool(config)
   255  	modeChangeHandler := newServingModeChangeHandler(t)
   256  	modeChangeOpt := xds.ServingModeCallback(modeChangeHandler.modeChangeCallback)
   257  	createStubServer(t, lis, modeChangeOpt, xds.ClientPoolForTesting(pool))
   258  
   259  	// Wait for the expected listener resource to be requested.
   260  	wantLisResourceNames := []string{fmt.Sprintf(e2e.ServerListenerResourceNameTemplate, net.JoinHostPort(host, strconv.Itoa(int(port))))}
   261  	select {
   262  	case <-ctx.Done():
   263  		t.Fatal("Timeout waiting for the expected listener resource to be requested")
   264  	case gotLisResourceName := <-listenerNamesCh:
   265  		if !cmp.Equal(gotLisResourceName, wantLisResourceNames) {
   266  			t.Fatalf("Got unexpected listener resource names: %v, want %v", gotLisResourceName, wantLisResourceNames)
   267  		}
   268  	}
   269  
   270  	// Wait for the expected route config resource to be requested.
   271  	select {
   272  	case <-ctx.Done():
   273  		t.Fatal("Timeout waiting for the expected route config resource to be requested")
   274  	case gotRouteNames := <-routeNamesCh:
   275  		if !cmp.Equal(gotRouteNames, []string{routeConfigName}) {
   276  			t.Fatalf("Got unexpected route config resource names: %v, want %v", gotRouteNames, []string{routeConfigName})
   277  		}
   278  	}
   279  
   280  	// Ensure that the server is not serving RPCs yet.
   281  	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
   282  	defer sCancel()
   283  	select {
   284  	case <-sCtx.Done():
   285  	case <-modeChangeHandler.modeCh:
   286  		t.Fatal("Server started serving RPCs before the route config was received")
   287  	}
   288  
   289  	// Create a gRPC channel to the xDS enabled server.
   290  	cc, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   291  	if err != nil {
   292  		t.Fatalf("grpc.NewClient(%q) failed: %v", lis.Addr(), err)
   293  	}
   294  	defer cc.Close()
   295  
   296  	// Ensure that the server isnt't serving RPCs successfully.
   297  	client := testgrpc.NewTestServiceClient(cc)
   298  	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err == nil || status.Code(err) != codes.Unavailable {
   299  		t.Fatalf("EmptyCall() returned %v, want %v", err, codes.Unavailable)
   300  	}
   301  
   302  	// Configure the management server with the expected route config resource,
   303  	// and expext RPCs to succeed.
   304  	resources.Routes = []*v3routepb.RouteConfiguration{e2e.RouteConfigNonForwardingAction(routeConfigName)}
   305  	if err := managementServer.Update(ctx, resources); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	select {
   309  	case <-ctx.Done():
   310  		t.Fatal("Timeout waiting for the server to start serving RPCs")
   311  	case gotMode := <-modeChangeHandler.modeCh:
   312  		if gotMode != connectivity.ServingModeServing {
   313  			t.Fatalf("Mode changed to %v, want %v", gotMode, connectivity.ServingModeServing)
   314  		}
   315  	}
   316  	waitForSuccessfulRPC(ctx, t, cc)
   317  }
   318  
   319  // Tests that the xDS-enabled gRPC server cleans up all its resources when all
   320  // connections to it are closed.
   321  func (s) TestServer_ConnectionCleanup(t *testing.T) {
   322  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   323  	defer cancel()
   324  
   325  	// Start an xDS management server.
   326  	managementServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
   327  
   328  	// Create bootstrap configuration pointing to the above management server.
   329  	nodeID := uuid.New().String()
   330  	bootstrapContents := e2e.DefaultBootstrapContents(t, nodeID, managementServer.Address)
   331  
   332  	// Create a listener on a local port to act as the xDS enabled gRPC server.
   333  	lis, err := testutils.LocalTCPListener()
   334  	if err != nil {
   335  		t.Fatalf("Failed to listen to local port: %v", err)
   336  	}
   337  	host, port, err := hostPortFromListener(lis)
   338  	if err != nil {
   339  		t.Fatalf("Failed to retrieve host and port of server: %v", err)
   340  	}
   341  
   342  	// Configure the managegement server with a listener and route configuration
   343  	// resource for the above xDS enabled gRPC server.
   344  	const routeConfigName = "routeName"
   345  	resources := e2e.UpdateOptions{
   346  		NodeID:         nodeID,
   347  		Listeners:      []*v3listenerpb.Listener{e2e.DefaultServerListenerWithRouteConfigName(host, port, e2e.SecurityLevelNone, "routeName")},
   348  		Routes:         []*v3routepb.RouteConfiguration{e2e.RouteConfigNonForwardingAction(routeConfigName)},
   349  		SkipValidation: true,
   350  	}
   351  	if err := managementServer.Update(ctx, resources); err != nil {
   352  		t.Fatal(err)
   353  	}
   354  
   355  	// Start an xDS-enabled gRPC server with the above bootstrap configuration.
   356  	config, err := bootstrap.NewConfigFromContents(bootstrapContents)
   357  	if err != nil {
   358  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err)
   359  	}
   360  	pool := xdsclient.NewPool(config)
   361  	modeChangeOpt := xds.ServingModeCallback(func(addr net.Addr, args xds.ServingModeChangeArgs) {
   362  		t.Logf("Serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
   363  	})
   364  	createStubServer(t, lis, modeChangeOpt, xds.ClientPoolForTesting(pool))
   365  
   366  	// Create a gRPC channel and verify that RPCs succeed.
   367  	cc, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   368  	if err != nil {
   369  		t.Fatalf("grpc.NewClient(%q) failed: %v", lis.Addr(), err)
   370  	}
   371  	defer cc.Close()
   372  	waitForSuccessfulRPC(ctx, t, cc)
   373  
   374  	// Create multiple channels to the server, and make an RPC on each one. When
   375  	// everything is closed, the server should have cleaned up all its resources
   376  	// as well (and this will be verified by the leakchecker).
   377  	const numConns = 100
   378  	var wg sync.WaitGroup
   379  	wg.Add(numConns)
   380  	for range numConns {
   381  		go func() {
   382  			defer wg.Done()
   383  			cc, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   384  			if err != nil {
   385  				t.Errorf("grpc.NewClient failed with err: %v", err)
   386  			}
   387  			client := testgrpc.NewTestServiceClient(cc)
   388  			if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
   389  				t.Errorf("EmptyCall() failed: %v", err)
   390  			}
   391  			cc.Close()
   392  		}()
   393  	}
   394  	wg.Wait()
   395  }
   396  
   397  // Tests that multiple xDS-enabled gRPC servers can be created with different
   398  // bootstrap configurations, and that they correctly request different LDS
   399  // resources from the management server based on their respective listening
   400  // ports.  It also ensures that gRPC clients can connect to the intended server
   401  // and that RPCs function correctly. The test uses the grpc.Peer() call option
   402  // to validate that the client is connected to the correct server.
   403  func (s) TestServer_MultipleServers_DifferentBootstrapConfigurations(t *testing.T) {
   404  	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
   405  	defer cancel()
   406  	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
   407  
   408  	// Create two bootstrap configurations pointing to the above management server.
   409  	nodeID1 := uuid.New().String()
   410  	bootstrapContents1 := e2e.DefaultBootstrapContents(t, nodeID1, mgmtServer.Address)
   411  	nodeID2 := uuid.New().String()
   412  	bootstrapContents2 := e2e.DefaultBootstrapContents(t, nodeID2, mgmtServer.Address)
   413  
   414  	// Create two xDS-enabled gRPC servers using the above bootstrap configs.
   415  	lis1, err := testutils.LocalTCPListener()
   416  	if err != nil {
   417  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
   418  	}
   419  	lis2, err := testutils.LocalTCPListener()
   420  	if err != nil {
   421  		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
   422  	}
   423  
   424  	modeChangeHandler1 := newServingModeChangeHandler(t)
   425  	modeChangeOpt1 := xds.ServingModeCallback(modeChangeHandler1.modeChangeCallback)
   426  	modeChangeHandler2 := newServingModeChangeHandler(t)
   427  	modeChangeOpt2 := xds.ServingModeCallback(modeChangeHandler2.modeChangeCallback)
   428  	config1, err := bootstrap.NewConfigFromContents(bootstrapContents1)
   429  	if err != nil {
   430  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents1), err)
   431  	}
   432  	pool1 := xdsclient.NewPool(config1)
   433  	config2, err := bootstrap.NewConfigFromContents(bootstrapContents2)
   434  	if err != nil {
   435  		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents2), err)
   436  	}
   437  	pool2 := xdsclient.NewPool(config2)
   438  	createStubServer(t, lis1, modeChangeOpt1, xds.ClientPoolForTesting(pool1))
   439  	createStubServer(t, lis2, modeChangeOpt2, xds.ClientPoolForTesting(pool2))
   440  
   441  	// Update the management server with the listener resources pointing to the
   442  	// corresponding gRPC servers.
   443  	host1, port1, err := hostPortFromListener(lis1)
   444  	if err != nil {
   445  		t.Fatalf("Failed to retrieve host and port of server: %v", err)
   446  	}
   447  	host2, port2, err := hostPortFromListener(lis2)
   448  	if err != nil {
   449  		t.Fatalf("Failed to retrieve host and port of server: %v", err)
   450  	}
   451  
   452  	resources1 := e2e.UpdateOptions{
   453  		NodeID:    nodeID1,
   454  		Listeners: []*v3listenerpb.Listener{e2e.DefaultServerListener(host1, port1, e2e.SecurityLevelNone, "routeName")},
   455  	}
   456  	if err := mgmtServer.Update(ctx, resources1); err != nil {
   457  		t.Fatal(err)
   458  	}
   459  
   460  	resources2 := e2e.UpdateOptions{
   461  		NodeID:    nodeID2,
   462  		Listeners: []*v3listenerpb.Listener{e2e.DefaultServerListener(host2, port2, e2e.SecurityLevelNone, "routeName")},
   463  	}
   464  	if err := mgmtServer.Update(ctx, resources2); err != nil {
   465  		t.Fatal(err)
   466  	}
   467  
   468  	select {
   469  	case <-ctx.Done():
   470  		t.Fatal("Timeout waiting for the xDS-enabled gRPC server to go SERVING")
   471  	case gotMode := <-modeChangeHandler1.modeCh:
   472  		if gotMode != connectivity.ServingModeServing {
   473  			t.Fatalf("Mode changed to %v, want %v", gotMode, connectivity.ServingModeServing)
   474  		}
   475  	}
   476  	select {
   477  	case <-ctx.Done():
   478  		t.Fatal("Timeout waiting for the xDS-enabled gRPC server to go SERVING")
   479  	case gotMode := <-modeChangeHandler2.modeCh:
   480  		if gotMode != connectivity.ServingModeServing {
   481  			t.Fatalf("Mode changed to %v, want %v", gotMode, connectivity.ServingModeServing)
   482  		}
   483  	}
   484  
   485  	// Create two gRPC clients, one for each server.
   486  	cc1, err := grpc.NewClient(lis1.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   487  	if err != nil {
   488  		t.Fatalf("Failed to create client for test server 1: %s, %v", lis1.Addr().String(), err)
   489  	}
   490  	defer cc1.Close()
   491  
   492  	cc2, err := grpc.NewClient(lis2.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
   493  	if err != nil {
   494  		t.Fatalf("Failed to create client for test server 2: %s, %v", lis2.Addr().String(), err)
   495  	}
   496  	defer cc2.Close()
   497  
   498  	// Both unary RPCs should work once the servers transitions into serving.
   499  	var peer1 peer.Peer
   500  	waitForSuccessfulRPC(ctx, t, cc1, grpc.Peer(&peer1))
   501  	if peer1.Addr.String() != lis1.Addr().String() {
   502  		t.Errorf("Connected to wrong peer: %s, want %s", peer1.Addr, lis1.Addr())
   503  	}
   504  
   505  	var peer2 peer.Peer
   506  	waitForSuccessfulRPC(ctx, t, cc2, grpc.Peer(&peer2))
   507  	if peer2.Addr.String() != lis2.Addr().String() {
   508  		t.Errorf("Connected to wrong peer: %s, want %s", peer2.Addr, lis2.Addr())
   509  	}
   510  }