123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892 |
- /*
- *
- * Copyright 2020 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- package xds
- import (
- "context"
- "errors"
- "fmt"
- "net"
- "reflect"
- "strings"
- "testing"
- "time"
- "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/internal/testutils/xds/e2e"
- _ "google.golang.org/grpc/xds/internal/httpfilter/router"
- xdstestutils "google.golang.org/grpc/xds/internal/testutils"
- "google.golang.org/grpc/xds/internal/testutils/fakeclient"
- "google.golang.org/grpc/xds/internal/xdsclient"
- "google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
- "google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
- 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"
- )
- 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) {
- // The xds package adds a couple of server options (unary and stream
- // interceptors) to the server options passed in by the user.
- 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)
- }
- // Verify that the user passed ServerOptions are forwarded as is.
- 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)
- }
- // fakeProviderBuilder builds new instances of fakeProvider and interprets the
- // config provided to it as a string.
- 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
- }
- // fakeProvider is an implementation of the Provider interface which provides a
- // method for tests to invoke to push new key materials.
- type fakeProvider struct {
- *certprovider.Distributor
- config string
- }
- // Close helps implement the Provider interface.
- func (p *fakeProvider) Close() {
- p.Distributor.Stop()
- }
- // setupOverrides sets up overrides for bootstrap config, new xdsClient creation
- // and new gRPC.Server creation.
- func setupOverrides(t *testing.T) (*fakeGRPCServer, *testutils.Channel, func()) {
- clientCh := testutils.NewChannel()
- origNewXDSClient := newXDSClient
- newXDSClient = func() (xdsclient.XDSClient, func(), error) {
- c := fakeclient.NewClient()
- c.SetBootstrapConfig(&bootstrap.Config{
- XDSServer: xdstestutils.ServerConfigForAddress(t, "server-address"),
- NodeProto: xdstestutils.EmptyNodeProtoV3,
- ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
- CertProviderConfigs: certProviderConfigs,
- })
- clientCh.Send(c)
- return c, func() {}, nil
- }
- fs := newFakeGRPCServer()
- origNewGRPCServer := newGRPCServer
- newGRPCServer = func(opts ...grpc.ServerOption) grpcServer { return fs }
- return fs, clientCh, func() {
- newXDSClient = origNewXDSClient
- newGRPCServer = origNewGRPCServer
- }
- }
- // setupOverridesForXDSCreds overrides only the xdsClient creation with a fake
- // one. Tests that use xdsCredentials need a real grpc.Server instead of a fake
- // one, because the xDS-enabled server needs to read configured creds from the
- // underlying grpc.Server to confirm whether xdsCreds were configured.
- func setupOverridesForXDSCreds(t *testing.T, includeCertProviderCfg bool) (*testutils.Channel, func()) {
- clientCh := testutils.NewChannel()
- origNewXDSClient := newXDSClient
- newXDSClient = func() (xdsclient.XDSClient, func(), error) {
- c := fakeclient.NewClient()
- bc := &bootstrap.Config{
- XDSServer: xdstestutils.ServerConfigForAddress(t, "server-address"),
- NodeProto: xdstestutils.EmptyNodeProtoV3,
- ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
- }
- if includeCertProviderCfg {
- bc.CertProviderConfigs = certProviderConfigs
- }
- c.SetBootstrapConfig(bc)
- clientCh.Send(c)
- return c, func() {}, nil
- }
- return clientCh, func() { newXDSClient = origNewXDSClient }
- }
- // TestServeSuccess tests the successful case of calling Serve().
- // The following sequence of events happen:
- // 1. Create a new GRPCServer and call Serve() in a goroutine.
- // 2. Make sure an xdsClient is created, and an LDS watch is registered.
- // 3. Push an error response from the xdsClient, and make sure that Serve() does
- // not exit.
- // 4. Push a good response from the xdsClient, and make sure that Serve() on the
- // underlying grpc.Server is called.
- func (s) TestServeSuccess(t *testing.T) {
- fs, clientCh, cleanup := setupOverrides(t)
- defer cleanup()
- // Create a new xDS-enabled gRPC server and pass it a server option to get
- // notified about serving mode changes.
- 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 := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
- }
- // Call Serve() in a goroutine, and push on a channel when Serve returns.
- serveDone := testutils.NewChannel()
- go func() {
- if err := server.Serve(lis); err != nil {
- t.Error(err)
- }
- serveDone.Send(nil)
- }()
- // Wait for an xdsClient to be created.
- 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)
- // Wait for a listener watch to be registered on the xdsClient.
- 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)
- }
- // Push an error to the registered listener watch callback and make sure
- // that Serve does not return.
- client.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{}, xdsresource.NewErrorf(xdsresource.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")
- }
- // Make sure the serving mode changes appropriately.
- 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)
- }
- // Push a good LDS response, and wait for Serve() to be invoked on the
- // underlying grpc.Server.
- fcm, err := xdsresource.NewFilterChainManager(listenerWithFilterChains)
- if err != nil {
- t.Fatalf("xdsclient.NewFilterChainManager() failed with error: %v", err)
- }
- addr, port := splitHostPort(lis.Addr().String())
- client.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{
- RouteConfigName: "routeconfig",
- InboundListenerCfg: &xdsresource.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")
- }
- // Make sure the serving mode changes appropriately.
- 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)
- }
- // Push an update to the registered listener watch callback with a Listener
- // resource whose host:port does not match the actual listening address and
- // port. This will push the listener to "not-serving" mode.
- client.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{
- RouteConfigName: "routeconfig",
- InboundListenerCfg: &xdsresource.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")
- }
- // Make sure the serving mode changes appropriately.
- 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)
- }
- }
- // TestServeWithStop tests the case where Stop() is called before an LDS update
- // is received. This should cause Serve() to exit before calling Serve() on the
- // underlying grpc.Server.
- func (s) TestServeWithStop(t *testing.T) {
- fs, clientCh, cleanup := setupOverrides(t)
- defer cleanup()
- // Note that we are not deferring the Stop() here since we explicitly call
- // it after the LDS watch has been registered.
- server := NewGRPCServer()
- lis, err := testutils.LocalTCPListener()
- if err != nil {
- server.Stop()
- t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
- }
- // Call Serve() in a goroutine, and push on a channel when Serve returns.
- serveDone := testutils.NewChannel()
- go func() {
- if err := server.Serve(lis); err != nil {
- t.Error(err)
- }
- serveDone.Send(nil)
- }()
- // Wait for an xdsClient to be created.
- ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
- defer cancel()
- c, err := clientCh.Receive(ctx)
- if err != nil {
- server.Stop()
- t.Fatalf("error when waiting for new xdsClient to be created: %v", err)
- }
- client := c.(*fakeclient.Client)
- // Wait for a listener watch to be registered on the xdsClient.
- 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)
- }
- // Call Stop() on the server before a listener update is received, and
- // expect Serve() to exit.
- server.Stop()
- if _, err := serveDone.Receive(ctx); err != nil {
- t.Fatalf("error when waiting for Serve() to exit")
- }
- // Make sure that Serve() on the underlying grpc.Server is not called.
- 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")
- }
- }
- // TestServeBootstrapFailure tests the case where xDS bootstrap fails and
- // verifies that Serve() exits with a non-nil error.
- func (s) TestServeBootstrapFailure(t *testing.T) {
- // Since we have not setup fakes for anything, this will attempt to do real
- // xDS bootstrap and that will fail because the bootstrap environment
- // variable is not set.
- server := NewGRPCServer()
- defer server.Stop()
- lis, err := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.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")
- }
- }
- // TestServeBootstrapConfigInvalid tests the cases where the bootstrap config
- // does not contain expected fields. Verifies that the call to Serve() fails.
- 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{
- XDSServer: xdstestutils.ServerConfigForAddress(t, "server-address"),
- NodeProto: xdstestutils.EmptyNodeProtoV3,
- ServerListenerResourceNameTemplate: testServerListenerResourceNameTemplate,
- },
- },
- {
- desc: "server_listener_resource_name_template is missing",
- bootstrapConfig: &bootstrap.Config{
- XDSServer: xdstestutils.ServerConfigForAddress(t, "server-address"),
- NodeProto: xdstestutils.EmptyNodeProtoV3,
- CertProviderConfigs: certProviderConfigs,
- },
- },
- }
- for _, test := range tests {
- t.Run(test.desc, func(t *testing.T) {
- // Override the xdsClient creation with one that returns a fake
- // xdsClient with the specified bootstrap configuration.
- clientCh := testutils.NewChannel()
- origNewXDSClient := newXDSClient
- newXDSClient = func() (xdsclient.XDSClient, func(), error) {
- c := fakeclient.NewClient()
- c.SetBootstrapConfig(test.bootstrapConfig)
- clientCh.Send(c)
- return c, func() {}, 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 := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.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")
- }
- })
- }
- }
- // TestServeNewClientFailure tests the case where xds client creation fails and
- // verifies that Server() exits with a non-nil error.
- func (s) TestServeNewClientFailure(t *testing.T) {
- origNewXDSClient := newXDSClient
- newXDSClient = func() (xdsclient.XDSClient, func(), error) {
- return nil, nil, errors.New("xdsClient creation failed")
- }
- defer func() { newXDSClient = origNewXDSClient }()
- server := NewGRPCServer()
- defer server.Stop()
- lis, err := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.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")
- }
- }
- // TestHandleListenerUpdate_NoXDSCreds tests the case where an xds-enabled gRPC
- // server is not configured with xDS credentials. Verifies that the security
- // config received as part of a Listener update is not acted upon.
- func (s) TestHandleListenerUpdate_NoXDSCreds(t *testing.T) {
- fs, clientCh, cleanup := setupOverrides(t)
- defer cleanup()
- // Create a server option to get notified about serving mode changes. We don't
- // do anything other than throwing a log entry here. But this is required,
- // since the server code emits a log entry at the default level (which is
- // ERROR) if no callback is registered for serving mode changes. Our
- // testLogger fails the test if there is any log entry at ERROR level. It does
- // provide an ExpectError() method, but that takes a string and it would be
- // painful to construct the exact error message expected here. Instead this
- // works just fine.
- modeChangeOpt := ServingModeCallback(func(addr net.Addr, args ServingModeChangeArgs) {
- t.Logf("Serving mode for listener %q changed to %q, err: %v", addr.String(), args.Mode, args.Err)
- })
- server := NewGRPCServer(modeChangeOpt)
- defer server.Stop()
- lis, err := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
- }
- // Call Serve() in a goroutine, and push on a channel when Serve returns.
- serveDone := testutils.NewChannel()
- go func() {
- if err := server.Serve(lis); err != nil {
- t.Error(err)
- }
- serveDone.Send(nil)
- }()
- // Wait for an xdsClient to be created.
- 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)
- // Wait for a listener watch to be registered on the xdsClient.
- 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)
- }
- // Push a good LDS response with security config, and wait for Serve() to be
- // invoked on the underlying grpc.Server. Also make sure that certificate
- // providers are not created.
- fcm, err := xdsresource.NewFilterChainManager(&v3listenerpb.Listener{
- FilterChains: []*v3listenerpb.FilterChain{
- {
- 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},
- }),
- },
- },
- },
- },
- },
- })
- if err != nil {
- t.Fatalf("xdsclient.NewFilterChainManager() failed with error: %v", err)
- }
- addr, port := splitHostPort(lis.Addr().String())
- client.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{
- RouteConfigName: "routeconfig",
- InboundListenerCfg: &xdsresource.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")
- }
- // Make sure the security configuration is not acted upon.
- if err := verifyCertProviderNotCreated(); err != nil {
- t.Fatal(err)
- }
- }
- // TestHandleListenerUpdate_ErrorUpdate tests the case where an xds-enabled gRPC
- // server is configured with xDS credentials, but receives a Listener update
- // with an error. Verifies that no certificate providers are created.
- func (s) TestHandleListenerUpdate_ErrorUpdate(t *testing.T) {
- clientCh, cleanup := setupOverridesForXDSCreds(t, true)
- defer cleanup()
- 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 := testutils.LocalTCPListener()
- if err != nil {
- t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
- }
- // Call Serve() in a goroutine, and push on a channel when Serve returns.
- serveDone := testutils.NewChannel()
- go func() {
- if err := server.Serve(lis); err != nil {
- t.Error(err)
- }
- serveDone.Send(nil)
- }()
- // Wait for an xdsClient to be created.
- 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)
- // Wait for a listener watch to be registered on the xdsClient.
- 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)
- }
- // Push an error to the registered listener watch callback and make sure
- // that Serve does not return.
- client.InvokeWatchListenerCallback(xdsresource.ListenerUpdate{}, errors.New("LDS error"))
- 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")
- }
- // Also make sure that no certificate providers are created.
- if err := verifyCertProviderNotCreated(); err != nil {
- t.Fatal(err)
- }
- }
- func verifyCertProviderNotCreated() error {
- sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
- defer sCancel()
- if _, err := fpb1.buildCh.Receive(sCtx); err != context.DeadlineExceeded {
- return errors.New("certificate provider created when no xDS creds were specified")
- }
- sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
- defer sCancel()
- if _, err := fpb2.buildCh.Receive(sCtx); err != context.DeadlineExceeded {
- return errors.New("certificate provider created when no xDS creds were specified")
- }
- return nil
- }
|