func (s) TestServeBootstrapConfigInvalid(t *testing.T) {
	tests := []struct {
		desc            string
		bootstrapConfig *bootstrap.Config
	}{
		{
			desc:            "bootstrap config is missing",
			bootstrapConfig: nil,
		},
		{
			desc: "certificate provider config is missing",
			bootstrapConfig: &bootstrap.Config{
				BalancerName:                       "dummyBalancer",
				Creds:                              grpc.WithTransportCredentials(insecure.NewCredentials()),
				NodeProto:                          xdstestutils.EmptyNodeProtoV3,
				ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
			},
		},
		{
			desc: "server_listener_resource_name_template is missing",
			bootstrapConfig: &bootstrap.Config{
				BalancerName:        "dummyBalancer",
				Creds:               grpc.WithTransportCredentials(insecure.NewCredentials()),
				NodeProto:           xdstestutils.EmptyNodeProtoV3,
				CertProviderConfigs: certProviderConfigs,
			},
		},
	}
	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			clientCh := testutils.NewChannel()
			origNewXDSClient := newXDSClient
			newXDSClient = func() (xdsclient.XDSClient, error) {
				c := fakeclient.NewClient()
				c.SetBootstrapConfig(test.bootstrapConfig)
				clientCh.Send(c)
				return c, nil
			}
			defer func() { newXDSClient = origNewXDSClient }()
			xdsCreds, err := xds.NewServerCredentials(xds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
			if err != nil {
				t.Fatalf("failed to create xds server credentials: %v", err)
			}
			server := NewGRPCServer(grpc.Creds(xdsCreds))
			defer server.Stop()
			lis, err := xdstestutils.LocalTCPListener()
			if err != nil {
				t.Fatalf("xdstestutils.LocalTCPListener() failed: %v", err)
			}
			serveDone := testutils.NewChannel()
			go func() {
				err := server.Serve(lis)
				serveDone.Send(err)
			}()
			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
			defer cancel()
			v, err := serveDone.Receive(ctx)
			if err != nil {
				t.Fatalf("error when waiting for Serve() to exit: %v", err)
			}
			if err, ok := v.(error); !ok || err == nil {
				t.Fatal("Serve() did not exit with error")
			}
		})
	}
}
TestServeBootstrapConfigInvalid tests the cases where the bootstrap config
does not contain expected fields. Verifies that the call to Serve() fails.
package xds
import (
	"context"
	"errors"
	"fmt"
	"net"
	"reflect"
	"strings"
	"testing"
	"time"
	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
	v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
	wrapperspb "github.com/golang/protobuf/ptypes/wrappers"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/credentials/tls/certprovider"
	"google.golang.org/grpc/credentials/xds"
	"google.golang.org/grpc/internal/grpctest"
	"google.golang.org/grpc/internal/testutils"
	_ "google.golang.org/grpc/xds/internal/httpfilter/router"
	xdstestutils "google.golang.org/grpc/xds/internal/testutils"
	"google.golang.org/grpc/xds/internal/testutils/e2e"
	"google.golang.org/grpc/xds/internal/testutils/fakeclient"
	"google.golang.org/grpc/xds/internal/xdsclient"
	"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
)
const (
	defaultTestTimeout                     = 5 * time.Second
	defaultTestShortTimeout                = 10 * time.Millisecond
	testServerListenerResourceNameTemplate = "/path/to/resource/%s/%s"
)
var listenerWithFilterChains = &v3listenerpb.Listener{
	FilterChains: []*v3listenerpb.FilterChain{
		{
			FilterChainMatch: &v3listenerpb.FilterChainMatch{
				PrefixRanges: []*v3corepb.CidrRange{
					{
						AddressPrefix: "192.168.0.0",
						PrefixLen: &wrapperspb.UInt32Value{
							Value: uint32(16),
						},
					},
				},
				SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK,
				SourcePrefixRanges: []*v3corepb.CidrRange{
					{
						AddressPrefix: "192.168.0.0",
						PrefixLen: &wrapperspb.UInt32Value{
							Value: uint32(16),
						},
					},
				},
				SourcePorts: []uint32{80},
			},
			TransportSocket: &v3corepb.TransportSocket{
				Name: "envoy.transport_sockets.tls",
				ConfigType: &v3corepb.TransportSocket_TypedConfig{
					TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{
						CommonTlsContext: &v3tlspb.CommonTlsContext{
							TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{
								InstanceName:    "identityPluginInstance",
								CertificateName: "identityCertName",
							},
						},
					}),
				},
			},
			Filters: []*v3listenerpb.Filter{
				{
					Name: "filter-1",
					ConfigType: &v3listenerpb.Filter_TypedConfig{
						TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{
							RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{
								RouteConfig: &v3routepb.RouteConfiguration{
									Name: "routeName",
									VirtualHosts: []*v3routepb.VirtualHost{{
										Domains: []string{"lds.target.good:3333"},
										Routes: []*v3routepb.Route{{
											Match: &v3routepb.RouteMatch{
												PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"},
											},
											Action: &v3routepb.Route_NonForwardingAction{},
										}}}}},
							},
							HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter},
						}),
					},
				},
			},
		},
	},
}
type s struct {
	grpctest.Tester
}
func Test(t *testing.T) {
	grpctest.RunSubTests(t, s{})
}
type fakeGRPCServer struct {
	done              chan struct{}
	registerServiceCh *testutils.Channel
	serveCh           *testutils.Channel
	stopCh            *testutils.Channel
	gracefulStopCh    *testutils.Channel
}
func (f *fakeGRPCServer) RegisterService(*grpc.ServiceDesc, interface{}) {
	f.registerServiceCh.Send(nil)
}
func (f *fakeGRPCServer) Serve(lis net.Listener) error {
	f.serveCh.Send(nil)
	<-f.done
	lis.Close()
	return nil
}
func (f *fakeGRPCServer) Stop() {
	close(f.done)
	f.stopCh.Send(nil)
}
func (f *fakeGRPCServer) GracefulStop() {
	close(f.done)
	f.gracefulStopCh.Send(nil)
}
func (f *fakeGRPCServer) GetServiceInfo() map[string]grpc.ServiceInfo {
	panic("implement me")
}
func newFakeGRPCServer() *fakeGRPCServer {
	return &fakeGRPCServer{
		done:              make(chan struct{}),
		registerServiceCh: testutils.NewChannel(),
		serveCh:           testutils.NewChannel(),
		stopCh:            testutils.NewChannel(),
		gracefulStopCh:    testutils.NewChannel(),
	}
}
func splitHostPort(hostport string) (string, string) {
	addr, port, err := net.SplitHostPort(hostport)
	if err != nil {
		panic(fmt.Sprintf("listener address %q does not parse: %v", hostport, err))
	}
	return addr, port
}
func (s) TestNewServer(t *testing.T) {
	xdsCreds, err := xds.NewServerCredentials(xds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
	if err != nil {
		t.Fatalf("failed to create xds server credentials: %v", err)
	}
	tests := []struct {
		desc              string
		serverOpts        []grpc.ServerOption
		wantXDSCredsInUse bool
	}{
		{
			desc:       "without_xds_creds",
			serverOpts: []grpc.ServerOption{grpc.Creds(insecure.NewCredentials())},
		},
		{
			desc:              "with_xds_creds",
			serverOpts:        []grpc.ServerOption{grpc.Creds(xdsCreds)},
			wantXDSCredsInUse: true,
		},
	}
	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			wantServerOpts := len(test.serverOpts) + 2
			origNewGRPCServer := newGRPCServer
			newGRPCServer = func(opts ...grpc.ServerOption) grpcServer {
				if got := len(opts); got != wantServerOpts {
					t.Fatalf("%d ServerOptions passed to grpc.Server, want %d", got, wantServerOpts)
				}
				if !reflect.DeepEqual(opts[2:], test.serverOpts) {
					t.Fatalf("got ServerOptions %v, want %v", opts[2:], test.serverOpts)
				}
				return grpc.NewServer(opts...)
			}
			defer func() {
				newGRPCServer = origNewGRPCServer
			}()
			s := NewGRPCServer(test.serverOpts...)
			defer s.Stop()
			if s.xdsCredsInUse != test.wantXDSCredsInUse {
				t.Fatalf("xdsCredsInUse is %v, want %v", s.xdsCredsInUse, test.wantXDSCredsInUse)
			}
		})
	}
}
func (s) TestRegisterService(t *testing.T) {
	fs := newFakeGRPCServer()
	origNewGRPCServer := newGRPCServer
	newGRPCServer = func(opts ...grpc.ServerOption) grpcServer { return fs }
	defer func() { newGRPCServer = origNewGRPCServer }()
	s := NewGRPCServer()
	defer s.Stop()
	s.RegisterService(&grpc.ServiceDesc{}, nil)
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	if _, err := fs.registerServiceCh.Receive(ctx); err != nil {
		t.Fatalf("timeout when expecting RegisterService() to called on grpc.Server: %v", err)
	}
}
const (
	fakeProvider1Name = "fake-certificate-provider-1"
	fakeProvider2Name = "fake-certificate-provider-2"
	fakeConfig        = "my fake config"
)
var (
	fpb1, fpb2          *fakeProviderBuilder
	certProviderConfigs map[string]*certprovider.BuildableConfig
)
func init() {
	fpb1 = &fakeProviderBuilder{
		name:    fakeProvider1Name,
		buildCh: testutils.NewChannel(),
	}
	fpb2 = &fakeProviderBuilder{
		name:    fakeProvider2Name,
		buildCh: testutils.NewChannel(),
	}
	cfg1, _ := fpb1.ParseConfig(fakeConfig + "1111")
	cfg2, _ := fpb2.ParseConfig(fakeConfig + "2222")
	certProviderConfigs = map[string]*certprovider.BuildableConfig{
		"default1": cfg1,
		"default2": cfg2,
	}
	certprovider.Register(fpb1)
	certprovider.Register(fpb2)
}
type fakeProviderBuilder struct {
	name    string
	buildCh *testutils.Channel
}
func (b *fakeProviderBuilder) ParseConfig(config interface{}) (*certprovider.BuildableConfig, error) {
	s, ok := config.(string)
	if !ok {
		return nil, fmt.Errorf("providerBuilder %s received config of type %T, want string", b.name, config)
	}
	return certprovider.NewBuildableConfig(b.name, []byte(s), func(certprovider.BuildOptions) certprovider.Provider {
		b.buildCh.Send(nil)
		return &fakeProvider{
			Distributor: certprovider.NewDistributor(),
			config:      s,
		}
	}), nil
}
func (b *fakeProviderBuilder) Name() string {
	return b.name
}
type fakeProvider struct {
	*certprovider.Distributor
	config string
}
func (p *fakeProvider) Close() {
	p.Distributor.Stop()
}
func setupOverrides() (*fakeGRPCServer, *testutils.Channel, func()) {
	clientCh := testutils.NewChannel()
	origNewXDSClient := newXDSClient
	newXDSClient = func() (xdsclient.XDSClient, error) {
		c := fakeclient.NewClient()
		c.SetBootstrapConfig(&bootstrap.Config{
			BalancerName:                       "dummyBalancer",
			Creds:                              grpc.WithTransportCredentials(insecure.NewCredentials()),
			NodeProto:                          xdstestutils.EmptyNodeProtoV3,
			ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
			CertProviderConfigs:                certProviderConfigs,
		})
		clientCh.Send(c)
		return c, nil
	}
	fs := newFakeGRPCServer()
	origNewGRPCServer := newGRPCServer
	newGRPCServer = func(opts ...grpc.ServerOption) grpcServer { return fs }
	return fs, clientCh, func() {
		newXDSClient = origNewXDSClient
		newGRPCServer = origNewGRPCServer
	}
}
func setupOverridesForXDSCreds(includeCertProviderCfg bool) (*testutils.Channel, func()) {
	clientCh := testutils.NewChannel()
	origNewXDSClient := newXDSClient
	newXDSClient = func() (xdsclient.XDSClient, error) {
		c := fakeclient.NewClient()
		bc := &bootstrap.Config{
			BalancerName:                       "dummyBalancer",
			Creds:                              grpc.WithTransportCredentials(insecure.NewCredentials()),
			NodeProto:                          xdstestutils.EmptyNodeProtoV3,
			ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
		}
		if includeCertProviderCfg {
			bc.CertProviderConfigs = certProviderConfigs
		}
		c.SetBootstrapConfig(bc)
		clientCh.Send(c)
		return c, nil
	}
	return clientCh, func() { newXDSClient = origNewXDSClient }
}
func (s) TestServeSuccess(t *testing.T) {
	fs, clientCh, cleanup := setupOverrides()
	defer cleanup()
	modeChangeCh := testutils.NewChannel()
	modeChangeOption := ServingModeCallback(func(addr net.Addr, args ServingModeChangeArgs) {
		t.Logf("server mode change callback invoked for listener %q with mode %q and error %v", addr.String(), args.Mode, args.Err)
		modeChangeCh.Send(args.Mode)
	})
	server := NewGRPCServer(modeChangeOption)
	defer server.Stop()
	lis, err := xdstestutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("xdstestutils.LocalTCPListener() failed: %v", err)
	}
	serveDone := testutils.NewChannel()
	go func() {
		if err := server.Serve(lis); err != nil {
			t.Error(err)
		}
		serveDone.Send(nil)
	}()
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	c, err := clientCh.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for new xdsClient to be created: %v", err)
	}
	client := c.(*fakeclient.Client)
	name, err := client.WaitForWatchListener(ctx)
	if err != nil {
		t.Fatalf("error when waiting for a ListenerWatch: %v", err)
	}
	wantName := strings.Replace(testServerListenerResourceNameTemplate, "%s", lis.Addr().String(), -1)
	if name != wantName {
		t.Fatalf("LDS watch registered for name %q, want %q", name, wantName)
	}
	client.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{}, xdsclient.NewErrorf(xdsclient.ErrorTypeResourceNotFound, "LDS resource not found"))
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := serveDone.Receive(sCtx); err != context.DeadlineExceeded {
		t.Fatal("Serve() returned after a bad LDS response")
	}
	v, err := modeChangeCh.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for serving mode to change: %v", err)
	}
	if mode := v.(connectivity.ServingMode); mode != connectivity.ServingModeNotServing {
		t.Fatalf("server mode is %q, want %q", mode, connectivity.ServingModeNotServing)
	}
	fcm, err := xdsclient.NewFilterChainManager(listenerWithFilterChains)
	if err != nil {
		t.Fatalf("xdsclient.NewFilterChainManager() failed with error: %v", err)
	}
	addr, port := splitHostPort(lis.Addr().String())
	client.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{
		RouteConfigName: "routeconfig",
		InboundListenerCfg: &xdsclient.InboundListenerConfig{
			Address:      addr,
			Port:         port,
			FilterChains: fcm,
		},
	}, nil)
	if _, err := fs.serveCh.Receive(ctx); err != nil {
		t.Fatalf("error when waiting for Serve() to be invoked on the grpc.Server")
	}
	v, err = modeChangeCh.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for serving mode to change: %v", err)
	}
	if mode := v.(connectivity.ServingMode); mode != connectivity.ServingModeServing {
		t.Fatalf("server mode is %q, want %q", mode, connectivity.ServingModeServing)
	}
	client.InvokeWatchListenerCallback(xdsclient.ListenerUpdate{
		RouteConfigName: "routeconfig",
		InboundListenerCfg: &xdsclient.InboundListenerConfig{
			Address:      "10.20.30.40",
			Port:         "666",
			FilterChains: fcm,
		},
	}, nil)
	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := serveDone.Receive(sCtx); err != context.DeadlineExceeded {
		t.Fatal("Serve() returned after a bad LDS response")
	}
	v, err = modeChangeCh.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for serving mode to change: %v", err)
	}
	if mode := v.(connectivity.ServingMode); mode != connectivity.ServingModeNotServing {
		t.Fatalf("server mode is %q, want %q", mode, connectivity.ServingModeNotServing)
	}
}
func (s) TestServeWithStop(t *testing.T) {
	fs, clientCh, cleanup := setupOverrides()
	defer cleanup()
	server := NewGRPCServer()
	lis, err := xdstestutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("xdstestutils.LocalTCPListener() failed: %v", err)
	}
	serveDone := testutils.NewChannel()
	go func() {
		if err := server.Serve(lis); err != nil {
			t.Error(err)
		}
		serveDone.Send(nil)
	}()
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	c, err := clientCh.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for new xdsClient to be created: %v", err)
	}
	client := c.(*fakeclient.Client)
	name, err := client.WaitForWatchListener(ctx)
	if err != nil {
		server.Stop()
		t.Fatalf("error when waiting for a ListenerWatch: %v", err)
	}
	wantName := strings.Replace(testServerListenerResourceNameTemplate, "%s", lis.Addr().String(), -1)
	if name != wantName {
		server.Stop()
		t.Fatalf("LDS watch registered for name %q, wantPrefix %q", name, wantName)
	}
	server.Stop()
	if _, err := serveDone.Receive(ctx); err != nil {
		t.Fatalf("error when waiting for Serve() to exit")
	}
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := fs.serveCh.Receive(sCtx); err != context.DeadlineExceeded {
		t.Fatal("Serve() called on underlying grpc.Server")
	}
}
func (s) TestServeBootstrapFailure(t *testing.T) {
	server := NewGRPCServer()
	defer server.Stop()
	lis, err := xdstestutils.LocalTCPListener()
	if err != nil {
		t.Fatalf("xdstestutils.LocalTCPListener() failed: %v", err)
	}
	serveDone := testutils.NewChannel()
	go func() { serveDone.Send(server.Serve(lis)) }()
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer cancel()
	v, err := serveDone.Receive(ctx)
	if err != nil {
		t.Fatalf("error when waiting for Serve() to exit: %v", err)
	}
	if err, ok := v.(error); !ok || err == nil {
		t.Fatal("Serve() did not exit with error")
	}
}