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)
	}
}
TestCertproviderStoreError tests the case where the certprovider.Store
returns an error when the CDS balancer attempts to create a provider.
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)
		}
	}
}