func (s) TestSecurityConfigUpdate_GoodToBad(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	xdsC.SetBootstrapConfig(bootstrapConfig)
	wantCCS := edsCCS(serviceName, nil, false, nil)
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdateWithGoodSecurityCfg, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, false); err != nil {
		t.Fatal(err)
	}
	xdsC.InvokeWatchClusterCallback(cdsUpdateWithMissingSecurityCfg, nil)
	gotErr, err := edsB.resolverErrCh.Receive(ctx)
	if err != nil {
		t.Fatal("timeout waiting for CDS balancer to forward error to EDS balancer upon receipt of bad security config")
	}
	if gotErr == nil {
		t.Fatal("CDS balancer did not forward error to EDS balancer upon receipt of bad security config")
	}
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := xdsC.WaitForCancelClusterWatch(sCtx); err != context.DeadlineExceeded {
		t.Fatal("cluster watch cancelled for a non-resource-not-found-error")
	}
}
TestSecurityConfigUpdate_GoodToBad tests the case where the first security
config returned by the xDS server is successful, but the second update cannot
be resolved based on the contents of the bootstrap config. Verifies that the
error is forwarded to the EDS balancer (which was created as part of the
first successful update).
package cdsbalancer
import (
	"context"
	"errors"
	"fmt"
	"regexp"
	"testing"
	"github.com/google/go-cmp/cmp"
	"google.golang.org/grpc/attributes"
	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/credentials/local"
	"google.golang.org/grpc/credentials/tls/certprovider"
	"google.golang.org/grpc/credentials/xds"
	"google.golang.org/grpc/internal"
	xdscredsinternal "google.golang.org/grpc/internal/credentials/xds"
	"google.golang.org/grpc/internal/testutils"
	"google.golang.org/grpc/internal/xds/matcher"
	"google.golang.org/grpc/resolver"
	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"
)
const (
	fakeProvider1Name = "fake-certificate-provider-1"
	fakeProvider2Name = "fake-certificate-provider-2"
	fakeConfig        = "my fake config"
	testSAN           = "test-san"
)
var (
	testSANMatchers = []matcher.StringMatcher{
		matcher.StringMatcherForTesting(newStringP(testSAN), nil, nil, nil, nil, true),
		matcher.StringMatcherForTesting(nil, newStringP(testSAN), nil, nil, nil, false),
		matcher.StringMatcherForTesting(nil, nil, newStringP(testSAN), nil, nil, false),
		matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(testSAN), false),
		matcher.StringMatcherForTesting(nil, nil, nil, newStringP(testSAN), nil, false),
	}
	fpb1, fpb2                   *fakeProviderBuilder
	bootstrapConfig              *bootstrap.Config
	cdsUpdateWithGoodSecurityCfg = xdsclient.ClusterUpdate{
		ClusterName: serviceName,
		SecurityCfg: &xdsclient.SecurityConfig{
			RootInstanceName:       "default1",
			IdentityInstanceName:   "default2",
			SubjectAltNameMatchers: testSANMatchers,
		},
	}
	cdsUpdateWithMissingSecurityCfg = xdsclient.ClusterUpdate{
		ClusterName: serviceName,
		SecurityCfg: &xdsclient.SecurityConfig{
			RootInstanceName: "not-default",
		},
	}
)
func newStringP(s string) *string {
	return &s
}
func init() {
	fpb1 = &fakeProviderBuilder{name: fakeProvider1Name}
	fpb2 = &fakeProviderBuilder{name: fakeProvider2Name}
	cfg1, _ := fpb1.ParseConfig(fakeConfig + "1111")
	cfg2, _ := fpb2.ParseConfig(fakeConfig + "2222")
	bootstrapConfig = &bootstrap.Config{
		CertProviderConfigs: map[string]*certprovider.BuildableConfig{
			"default1": cfg1,
			"default2": cfg2,
		},
	}
	certprovider.Register(fpb1)
	certprovider.Register(fpb2)
}
type fakeProviderBuilder struct {
	name string
}
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 {
		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 setupWithXDSCreds(t *testing.T) (*fakeclient.Client, *cdsBalancer, *testEDSBalancer, *xdstestutils.TestClientConn, func()) {
	t.Helper()
	xdsC := fakeclient.NewClient()
	builder := balancer.Get(cdsName)
	if builder == nil {
		t.Fatalf("balancer.Get(%q) returned nil", cdsName)
	}
	creds, err := xds.NewClientCredentials(xds.ClientOptions{
		FallbackCreds: local.NewCredentials(),  
	})
	if err != nil {
		t.Fatalf("Failed to create xDS client creds: %v", err)
	}
	tcc := xdstestutils.NewTestClientConn(t)
	cdsB := builder.Build(tcc, balancer.BuildOptions{DialCreds: creds})
	edsB := newTestEDSBalancer()
	oldEDSBalancerBuilder := newChildBalancer
	newChildBalancer = func(cc balancer.ClientConn, opts balancer.BuildOptions) (balancer.Balancer, error) {
		edsB.parentCC = cc
		return edsB, nil
	}
	if err := cdsB.UpdateClientConnState(cdsCCS(clusterName, xdsC)); err != nil {
		t.Fatalf("cdsBalancer.UpdateClientConnState failed with error: %v", err)
	}
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	gotCluster, err := xdsC.WaitForWatchCluster(ctx)
	if err != nil {
		t.Fatalf("xdsClient.WatchCDS failed with error: %v", err)
	}
	if gotCluster != clusterName {
		t.Fatalf("xdsClient.WatchCDS called for cluster: %v, want: %v", gotCluster, clusterName)
	}
	return xdsC, cdsB.(*cdsBalancer), edsB, tcc, func() {
		newChildBalancer = oldEDSBalancerBuilder
		xdsC.Close()
	}
}
func makeNewSubConn(ctx context.Context, edsCC balancer.ClientConn, parentCC *xdstestutils.TestClientConn, wantFallback bool) (balancer.SubConn, error) {
	dummyAddr := "foo-address"
	addrs := []resolver.Address{{Addr: dummyAddr}}
	sc, err := edsCC.NewSubConn(addrs, balancer.NewSubConnOptions{})
	if err != nil {
		return nil, fmt.Errorf("NewSubConn(%+v) on parent ClientConn failed: %v", addrs, err)
	}
	select {
	case <-ctx.Done():
		return nil, errors.New("timeout when waiting for new SubConn")
	case gotAddrs := <-parentCC.NewSubConnAddrsCh:
		if len(gotAddrs) != 1 {
			return nil, fmt.Errorf("NewSubConn expected 1 address, got %d", len(gotAddrs))
		}
		if got, want := gotAddrs[0].Addr, addrs[0].Addr; got != want {
			return nil, fmt.Errorf("resolver.Address passed to parent ClientConn has address %q, want %q", got, want)
		}
		getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *xdscredsinternal.HandshakeInfo)
		hi := getHI(gotAddrs[0].Attributes)
		if hi == nil {
			return nil, errors.New("resolver.Address passed to parent ClientConn doesn't contain attributes")
		}
		if gotFallback := hi.UseFallbackCreds(); gotFallback != wantFallback {
			return nil, fmt.Errorf("resolver.Address HandshakeInfo uses fallback creds? %v, want %v", gotFallback, wantFallback)
		}
		if !wantFallback {
			if diff := cmp.Diff(testSANMatchers, hi.GetSANMatchersForTesting(), cmp.AllowUnexported(regexp.Regexp{})); diff != "" {
				return nil, fmt.Errorf("unexpected diff in the list of SAN matchers (-got, +want):\n%s", diff)
			}
		}
	}
	return sc, nil
}
func (s) TestSecurityConfigWithoutXDSCreds(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithWatch(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	providerCh := testutils.NewChannel()
	origBuildProvider := buildProvider
	buildProvider = func(c map[string]*certprovider.BuildableConfig, id, cert string, wi, wr bool) (certprovider.Provider, error) {
		p, err := origBuildProvider(c, id, cert, wi, wr)
		providerCh.Send(nil)
		return p, err
	}
	defer func() { buildProvider = origBuildProvider }()
	cdsUpdate := xdsclient.ClusterUpdate{ClusterName: serviceName}
	wantCCS := edsCCS(serviceName, nil, false, nil)
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, true); err != nil {
		t.Fatal(err)
	}
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := providerCh.Receive(sCtx); err != context.DeadlineExceeded {
		t.Fatal("cds balancer created certificate providers when not using xds credentials")
	}
}
func (s) TestNoSecurityConfigWithXDSCreds(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	providerCh := testutils.NewChannel()
	origBuildProvider := buildProvider
	buildProvider = func(c map[string]*certprovider.BuildableConfig, id, cert string, wi, wr bool) (certprovider.Provider, error) {
		p, err := origBuildProvider(c, id, cert, wi, wr)
		providerCh.Send(nil)
		return p, err
	}
	defer func() { buildProvider = origBuildProvider }()
	cdsUpdate := xdsclient.ClusterUpdate{ClusterName: serviceName}
	wantCCS := edsCCS(serviceName, nil, false, nil)
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, true); err != nil {
		t.Fatal(err)
	}
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if _, err := providerCh.Receive(sCtx); err != context.DeadlineExceeded {
		t.Fatal("cds balancer created certificate providers when not using xds credentials")
	}
}
func (s) TestSecurityConfigNotFoundInBootstrap(t *testing.T) {
	for i := 0; i < 2; i++ {
		xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
		defer func() {
			cancel()
			cdsB.Close()
		}()
		if i == 0 {
			xdsC.SetBootstrapConfig(bootstrapConfig)
		}
		xdsC.InvokeWatchClusterCallback(cdsUpdateWithMissingSecurityCfg, nil)
		sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
		defer sCancel()
		if err := edsB.waitForResolverError(sCtx, nil); err != context.DeadlineExceeded {
			t.Fatal("eds balancer shouldn't get error (shouldn't be built yet)")
		}
		ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
		defer ctxCancel()
		if err := tcc.WaitForErrPicker(ctx); err != nil {
			t.Fatal(err)
		}
	}
}
func (s) TestCertproviderStoreError(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	origBuildProvider := buildProvider
	buildProvider = func(c map[string]*certprovider.BuildableConfig, id, cert string, wi, wr bool) (certprovider.Provider, error) {
		return nil, errors.New("certprovider store error")
	}
	defer func() { buildProvider = origBuildProvider }()
	xdsC.SetBootstrapConfig(bootstrapConfig)
	xdsC.InvokeWatchClusterCallback(cdsUpdateWithGoodSecurityCfg, nil)
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if err := edsB.waitForResolverError(sCtx, nil); err != context.DeadlineExceeded {
		t.Fatal("eds balancer shouldn't get error (shouldn't be built yet)")
	}
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := tcc.WaitForErrPicker(ctx); err != nil {
		t.Fatal(err)
	}
}
func (s) TestSecurityConfigUpdate_BadToGood(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	xdsC.SetBootstrapConfig(bootstrapConfig)
	xdsC.InvokeWatchClusterCallback(cdsUpdateWithMissingSecurityCfg, nil)
	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
	defer sCancel()
	if err := edsB.waitForResolverError(sCtx, nil); err != context.DeadlineExceeded {
		t.Fatal("eds balancer shouldn't get error (shouldn't be built yet)")
	}
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := tcc.WaitForErrPicker(ctx); err != nil {
		t.Fatal(err)
	}
	wantCCS := edsCCS(serviceName, nil, false, nil)
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdateWithGoodSecurityCfg, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, false); err != nil {
		t.Fatal(err)
	}
}
func (s) TestGoodSecurityConfig(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	xdsC.SetBootstrapConfig(bootstrapConfig)
	wantCCS := edsCCS(serviceName, nil, false, nil)
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdateWithGoodSecurityCfg, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	sc, err := makeNewSubConn(ctx, edsB.parentCC, tcc, false)
	if err != nil {
		t.Fatal(err)
	}
	dummyAddr := "bar-address"
	addrs := []resolver.Address{{Addr: dummyAddr}}
	edsB.parentCC.UpdateAddresses(sc, addrs)
	select {
	case <-ctx.Done():
		t.Fatal("timeout when waiting for addresses to be updated on the subConn")
	case gotAddrs := <-tcc.UpdateAddressesAddrsCh:
		if len(gotAddrs) != 1 {
			t.Fatalf("UpdateAddresses expected 1 address, got %d", len(gotAddrs))
		}
		if got, want := gotAddrs[0].Addr, addrs[0].Addr; got != want {
			t.Fatalf("resolver.Address passed to parent ClientConn through UpdateAddresses() has address %q, want %q", got, want)
		}
		getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *xdscredsinternal.HandshakeInfo)
		hi := getHI(gotAddrs[0].Attributes)
		if hi == nil {
			t.Fatal("resolver.Address passed to parent ClientConn through UpdateAddresses() doesn't contain attributes")
		}
	}
}
func (s) TestSecurityConfigUpdate_GoodToFallback(t *testing.T) {
	xdsC, cdsB, edsB, tcc, cancel := setupWithXDSCreds(t)
	defer func() {
		cancel()
		cdsB.Close()
	}()
	xdsC.SetBootstrapConfig(bootstrapConfig)
	wantCCS := edsCCS(serviceName, nil, false, nil)
	ctx, ctxCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer ctxCancel()
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdateWithGoodSecurityCfg, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, false); err != nil {
		t.Fatal(err)
	}
	cdsUpdate := xdsclient.ClusterUpdate{ClusterName: serviceName}
	if err := invokeWatchCbAndWait(ctx, xdsC, cdsWatchInfo{cdsUpdate, nil}, wantCCS, edsB); err != nil {
		t.Fatal(err)
	}
	if _, err := makeNewSubConn(ctx, edsB.parentCC, tcc, true); err != nil {
		t.Fatal(err)
	}
}