Repository: grpc/grpc-go

Issue:
xds: support LRS for all cluster types
When working on https://github.com/grpc/grpc-go/pull/7191, I realized that we were setting the LRS server configuration only for EDS clusters. This needs to be supported for all cluster types.

Note that for non-EDS cluster types, there is no locality info, so we default to reporting everything in a single locality name with all fields set to the empty string.

We need to also ensure that we have proper e2e test coverage for all supported cluster types.

Discussion:
@easwars: For aggregate clusters, all configuration comes from the underlying clusters, including LRS.

https://github.com/grpc/proposal/blob/master/A75-xds-aggregate-cluster-behavior-fixes.md contains the latest spec on how aggregate clusters should work. A74 is related as well.
@purnesh42H: @easwars shall we close this? I see the related PR is merged@easwars: That PR does not handle the issue mentioned in here. That PR only adds server config to the `ClusterUpdate` struct (this work was not done as part of A47, but is required for A71). This issue needs to stay open.@purnesh42H: > That PR does not handle the issue mentioned in here. That PR only adds server config to the `ClusterUpdate` struct (this work was not done as part of A47, but is required for A71). This issue needs to stay open.

Let me know if you think it could be good task for me to look at@dfawley: I believe this was fixed by #8170.

Solution patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index a0783329..00000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-# To get started with Dependabot version updates, you'll need to specify which
-# package ecosystems to update and where the package manifests are located.
-# Please see the documentation for all configuration options:
-# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-updates:
-  - package-ecosystem: "github-actions"
-    directory: "/"
-    schedule:
-      interval: "monthly"
-    groups:
-      github-actions:
-        patterns:
-          - "*"
-    ignore:
-      - dependency-name: "*"
-        update-types: ["version-update:semver-major"]
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index b67bc0a2..237137af 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -25,13 +25,13 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+      uses: actions/checkout@v4
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@a82bad71823183e5b120ab52d521460ecb0585fe # v2.24.9
+      uses: github/codeql-action/init@v2
       with:
         languages: go
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@a82bad71823183e5b120ab52d521460ecb0585fe # v2.24.9
+      uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 5324b119..d68931b9 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -9,10 +9,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Install checkout
-        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+        uses: actions/checkout@v4
 
       - name: Install checkout
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@v5
         with:
           go-version: "stable"
 
@@ -20,4 +20,4 @@ jobs:
         run: go test -coverprofile=coverage.out -coverpkg=./... ./...
 
       - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3.1.6
+        uses: codecov/codecov-action@v3
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
index ff0d743c..901db858 100644
--- a/.github/workflows/lock.yml
+++ b/.github/workflows/lock.yml
@@ -15,7 +15,7 @@ jobs:
       issues: write
       pull-requests: write
     steps:
-      - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
+      - uses: dessant/lock-threads@v5
         with:
           github-token: ${{ github.token }}
           issue-inactive-days: 180
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7fcee056..bfc6e1f7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,10 +25,10 @@ jobs:
 
     steps:
       - name: Checkout code
-        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+        uses: actions/checkout@v4
 
       - name: Set up Go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@v5
 
       - name: Download dependencies
         run: |
@@ -57,7 +57,7 @@ jobs:
           echo "name=${PACKAGE_NAME}" >> $GITHUB_OUTPUT
 
       - name: Upload asset
-        uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1.0.2
+        uses: actions/upload-release-asset@v1
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index bcb44e00..f9bae4c0 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -16,7 +16,7 @@ jobs:
       pull-requests: write
 
     steps:
-    - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0
+    - uses: actions/stale@v8
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
         days-before-stale: 6
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 648712f4..68f03131 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -22,11 +22,11 @@ jobs:
     steps:
       # Setup the environment.
       - name: Setup Go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@v5
         with:
           go-version: '1.22'
       - name: Checkout repo
-        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+        uses: actions/checkout@v4
 
       # Run the vet-proto checks.
       - name: vet-proto
@@ -83,12 +83,12 @@ jobs:
         run: echo "${{ matrix.grpcenv }}" >> $GITHUB_ENV
 
       - name: Setup Go
-        uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+        uses: actions/setup-go@v5
         with:
           go-version: ${{ matrix.goversion }}
 
       - name: Checkout repo
-        uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+        uses: actions/checkout@v4
 
       # Only run vet for 'vet' runs.
       - name: Run vet.sh
diff --git a/internal/envconfig/envconfig.go b/internal/envconfig/envconfig.go
index 685a3cb4..9c915d9e 100644
--- a/internal/envconfig/envconfig.go
+++ b/internal/envconfig/envconfig.go
@@ -28,9 +28,6 @@ import (
 var (
 	// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
 	TXTErrIgnore = boolFromEnv("GRPC_GO_IGNORE_TXT_ERRORS", true)
-	// AdvertiseCompressors is set if registered compressor should be advertised
-	// ("GRPC_GO_ADVERTISE_COMPRESSORS" is not "false").
-	AdvertiseCompressors = boolFromEnv("GRPC_GO_ADVERTISE_COMPRESSORS", true)
 	// RingHashCap indicates the maximum ring size which defaults to 4096
 	// entries but may be overridden by setting the environment variable
 	// "GRPC_RING_HASH_CAP".  This does not override the default bounds
diff --git a/internal/grpcutil/compressor.go b/internal/grpcutil/compressor.go
index 9f409096..e8d86698 100644
--- a/internal/grpcutil/compressor.go
+++ b/internal/grpcutil/compressor.go
@@ -20,8 +20,6 @@ package grpcutil
 
 import (
 	"strings"
-
-	"google.golang.org/grpc/internal/envconfig"
 )
 
 // RegisteredCompressorNames holds names of the registered compressors.
@@ -40,8 +38,5 @@ func IsCompressorNameRegistered(name string) bool {
 // RegisteredCompressors returns a string of registered compressor names
 // separated by comma.
 func RegisteredCompressors() string {
-	if !envconfig.AdvertiseCompressors {
-		return ""
-	}
 	return strings.Join(RegisteredCompressorNames, ",")
 }
diff --git a/metadata/metadata.go b/metadata/metadata.go
index 1e9485fd..6c01a9b3 100644
--- a/metadata/metadata.go
+++ b/metadata/metadata.go
@@ -90,6 +90,21 @@ func Pairs(kv ...string) MD {
 	return md
 }
 
+// String implements the Stringer interface for pretty-printing a MD.
+// Ordering of the values is non-deterministic as it ranges over a map.
+func (md MD) String() string {
+	var sb strings.Builder
+	fmt.Fprintf(&sb, "MD{")
+	for k, v := range md {
+		if sb.Len() > 3 {
+			fmt.Fprintf(&sb, ", ")
+		}
+		fmt.Fprintf(&sb, "%s=[%s]", k, strings.Join(v, ", "))
+	}
+	fmt.Fprintf(&sb, "}")
+	return sb.String()
+}
+
 // Len returns the number of items in md.
 func (md MD) Len() int {
 	return len(md)
diff --git a/peer/peer.go b/peer/peer.go
index a821ff9b..499a49c8 100644
--- a/peer/peer.go
+++ b/peer/peer.go
@@ -22,7 +22,9 @@ package peer
 
 import (
 	"context"
+	"fmt"
 	"net"
+	"strings"
 
 	"google.golang.org/grpc/credentials"
 )
@@ -39,6 +41,34 @@ type Peer struct {
 	AuthInfo credentials.AuthInfo
 }
 
+// String ensures the Peer types implements the Stringer interface in order to
+// allow to print a context with a peerKey value effectively.
+func (p *Peer) String() string {
+	if p == nil {
+		return "Peer<nil>"
+	}
+	sb := &strings.Builder{}
+	sb.WriteString("Peer{")
+	if p.Addr != nil {
+		fmt.Fprintf(sb, "Addr: '%s', ", p.Addr.String())
+	} else {
+		fmt.Fprintf(sb, "Addr: <nil>, ")
+	}
+	if p.LocalAddr != nil {
+		fmt.Fprintf(sb, "LocalAddr: '%s', ", p.LocalAddr.String())
+	} else {
+		fmt.Fprintf(sb, "LocalAddr: <nil>, ")
+	}
+	if p.AuthInfo != nil {
+		fmt.Fprintf(sb, "AuthInfo: '%s'", p.AuthInfo.AuthType())
+	} else {
+		fmt.Fprintf(sb, "AuthInfo: <nil>")
+	}
+	sb.WriteString("}")
+
+	return sb.String()
+}
+
 type peerKey struct{}
 
 // NewContext creates a new context with peer information attached.
diff --git a/picker_wrapper.go b/picker_wrapper.go
index bf56faa7..56e8aba7 100644
--- a/picker_wrapper.go
+++ b/picker_wrapper.go
@@ -20,6 +20,7 @@ package grpc
 
 import (
 	"context"
+	"fmt"
 	"io"
 	"sync"
 
@@ -117,7 +118,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
 				if lastPickErr != nil {
 					errStr = "latest balancer error: " + lastPickErr.Error()
 				} else {
-					errStr = ctx.Err().Error()
+					errStr = fmt.Sprintf("received context error while waiting for new LB policy update: %s", ctx.Err().Error())
 				}
 				switch ctx.Err() {
 				case context.DeadlineExceeded:
diff --git a/security/advancedtls/advancedtls.go b/security/advancedtls/advancedtls.go
index d7aa4f41..4becabc8 100644
--- a/security/advancedtls/advancedtls.go
+++ b/security/advancedtls/advancedtls.go
@@ -87,31 +87,52 @@ type PostHandshakeVerificationFunc func(params *HandshakeVerificationInfo) (*Pos
 // Deprecated: use PostHandshakeVerificationFunc instead.
 type CustomVerificationFunc = PostHandshakeVerificationFunc
 
-// GetRootCAsParams contains the parameters available to users when
-// implementing GetRootCAs.
-type GetRootCAsParams struct {
-	RawConn  net.Conn
+// ConnectionInfo contains the parameters available to users when
+// implementing GetRootCertificates.
+type ConnectionInfo struct {
+	// RawConn is the raw net.Conn representing a connection.
+	RawConn net.Conn
+	// RawCerts is the byte representation of the presented peer cert chain.
 	RawCerts [][]byte
 }
 
-// GetRootCAsResults contains the results of GetRootCAs.
+// GetRootCAsParams contains the parameters available to users when
+// implementing GetRootCAs.
+//
+// Deprecated: use ConnectionInfo instead.
+type GetRootCAsParams = ConnectionInfo
+
+// RootCertificates is the result of GetRootCertificates.
 // If users want to reload the root trust certificate, it is required to return
 // the proper TrustCerts in GetRootCAs.
-type GetRootCAsResults struct {
+type RootCertificates struct {
+	// TrustCerts is the pool of trusted certificates.
 	TrustCerts *x509.CertPool
 }
 
+// GetRootCAsResults contains the results of GetRootCAs.
+// If users want to reload the root trust certificate, it is required to return
+// the proper TrustCerts in GetRootCAs.
+//
+// Deprecated: use RootCertificates instead.
+type GetRootCAsResults = RootCertificates
+
 // RootCertificateOptions contains options to obtain root trust certificates
 // for both the client and the server.
 // At most one option could be set. If none of them are set, we
 // use the system default trust certificates.
 type RootCertificateOptions struct {
+	// If RootCertificates is set, it will be used every time when verifying
+	// the peer certificates, without performing root certificate reloading.
+	RootCertificates *x509.CertPool
 	// If RootCACerts is set, it will be used every time when verifying
 	// the peer certificates, without performing root certificate reloading.
+	//
+	// Deprecated: use RootCertificates instead.
 	RootCACerts *x509.CertPool
 	// If GetRootCertificates is set, it will be invoked to obtain root certs for
 	// every new connection.
-	GetRootCertificates func(params *GetRootCAsParams) (*GetRootCAsResults, error)
+	GetRootCertificates func(params *ConnectionInfo) (*RootCertificates, error)
 	// If RootProvider is set, we will use the root certs from the Provider's
 	// KeyMaterial() call in the new connections. The Provider must have initial
 	// credentials if specified. Otherwise, KeyMaterial() will block forever.
@@ -184,66 +205,18 @@ const (
 )
 
 // ClientOptions contains the fields needed to be filled by the client.
-type ClientOptions struct {
-	// IdentityOptions is OPTIONAL on client side. This field only needs to be
-	// set if mutual authentication is required on server side.
-	IdentityOptions IdentityCertificateOptions
-	// AdditionalPeerVerification is a custom verification check after certificate signature
-	// check.
-	// If this is set, we will perform this customized check after doing the
-	// normal check(s) indicated by setting VerificationType.
-	AdditionalPeerVerification PostHandshakeVerificationFunc
-	// VerifyPeer is a custom verification check after certificate signature
-	// check.
-	// If this is set, we will perform this customized check after doing the
-	// normal check(s) indicated by setting VerificationType.
-	//
-	// Deprecated: use AdditionalPeerVerification instead.
-	VerifyPeer PostHandshakeVerificationFunc
-	// RootOptions is OPTIONAL on client side. If not set, we will try to use the
-	// default trust certificates in users' OS system.
-	RootOptions RootCertificateOptions
-	// VerificationType defines what type of server verification is done. See
-	// the `VerificationType` enum for the different options.
-	// Default: CertAndHostVerification
-	VerificationType VerificationType
-	// VType is the verification type on the client side.
-	//
-	// Deprecated: use VerificationType instead.
-	VType VerificationType
-	// RevocationOptions is the configurations for certificate revocation checks.
-	// It could be nil if such checks are not needed.
-	RevocationOptions *RevocationOptions
-	// MinVersion contains the minimum TLS version that is acceptable.
-	//
-	// Deprecated: use MinTLSVersion instead.
-	MinVersion uint16
-	// MaxVersion contains the maximum TLS version that is acceptable.
-	//
-	// Deprecated: use MaxTLSVersion instead.
-	MaxVersion uint16
-	// MinTLSVersion contains the minimum TLS version that is acceptable.
-	// The value should be set using tls.VersionTLSxx from https://pkg.go.dev/crypto/tls
-	// By default, TLS 1.2 is currently used as the minimum when acting as a
-	// client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum
-	// supported by this package, both as a client and as a server.  This
-	// default may be changed over time affecting backwards compatibility.
-	MinTLSVersion uint16
-	// MaxTLSVersion contains the maximum TLS version that is acceptable.
-	// The value should be set using tls.VersionTLSxx from https://pkg.go.dev/crypto/tls
-	// By default, the maximum version supported by this package is used,
-	// which is currently TLS 1.3.  This default may be changed over time
-	// affecting backwards compatibility.
-	MaxTLSVersion uint16
-	// serverNameOverride is for testing only. If set to a non-empty string, it
-	// will override the virtual host name of authority (e.g. :authority header
-	// field) in requests and the target hostname used during server cert
-	// verification.
-	serverNameOverride string
-}
+// Deprecated: use Options instead.
+type ClientOptions = Options
 
 // ServerOptions contains the fields needed to be filled by the server.
-type ServerOptions struct {
+// Deprecated: use Options instead.
+type ServerOptions = Options
+
+// Options contains the fields a user can configure when setting up TLS clients
+// and servers
+type Options struct {
+	// IdentityOptions is OPTIONAL on client side. This field only needs to be
+	// set if mutual authentication is required on server side.
 	// IdentityOptions is REQUIRED on server side.
 	IdentityOptions IdentityCertificateOptions
 	// AdditionalPeerVerification is a custom verification check after certificate signature
@@ -261,9 +234,11 @@ type ServerOptions struct {
 	// RootOptions is OPTIONAL on server side. This field only needs to be set if
 	// mutual authentication is required(RequireClientCert is true).
 	RootOptions RootCertificateOptions
-	// If the server want the client to send certificates.
+	// If the server requires the client to send certificates. This value is only
+	// relevant when configuring options for the server. Is not used for
+	// client-side configuration.
 	RequireClientCert bool
-	// VerificationType defines what type of client verification is done. See
+	// VerificationType defines what type of peer verification is done. See
 	// the `VerificationType` enum for the different options.
 	// Default: CertAndHostVerification
 	VerificationType VerificationType
@@ -295,9 +270,14 @@ type ServerOptions struct {
 	// which is currently TLS 1.3.  This default may be changed over time
 	// affecting backwards compatibility.
 	MaxTLSVersion uint16
+	// serverNameOverride is for testing only and only relevant on the client
+	// side. If set to a non-empty string, it will override the virtual host
+	// name of authority (e.g. :authority header field) in requests and the
+	// target hostname used during server cert verification.
+	serverNameOverride string
 }
 
-func (o *ClientOptions) config() (*tls.Config, error) {
+func (o *Options) clientConfig() (*tls.Config, error) {
 	// TODO(gtcooke94) Remove this block when o.VerifyPeer is remoed.
 	// VerifyPeer is deprecated, but do this to aid the transitory migration time.
 	if o.AdditionalPeerVerification == nil {
@@ -318,6 +298,12 @@ func (o *ClientOptions) config() (*tls.Config, error) {
 	if o.MaxTLSVersion == 0 {
 		o.MaxTLSVersion = o.MaxVersion
 	}
+	// TODO(gtcooke94) RootCACerts is deprecated, eventually remove this block.
+	// This will ensure that users still explicitly setting RootCACerts will get
+	// the setting int the right place.
+	if o.RootOptions.RootCACerts != nil {
+		o.RootOptions.RootCertificates = o.RootOptions.RootCACerts
+	}
 	if o.VerificationType == SkipVerification && o.AdditionalPeerVerification == nil {
 		return nil, fmt.Errorf("client needs to provide custom verification mechanism if choose to skip default verification")
 	}
@@ -353,19 +339,19 @@ func (o *ClientOptions) config() (*tls.Config, error) {
 	}
 	// Propagate root-certificate-related fields in tls.Config.
 	switch {
-	case o.RootOptions.RootCACerts != nil:
-		config.RootCAs = o.RootOptions.RootCACerts
+	case o.RootOptions.RootCertificates != nil:
+		config.RootCAs = o.RootOptions.RootCertificates
 	case o.RootOptions.GetRootCertificates != nil:
 		// In cases when users provide GetRootCertificates callback, since this
 		// callback is not contained in tls.Config, we have nothing to set here.
 		// We will invoke the callback in ClientHandshake.
 	case o.RootOptions.RootProvider != nil:
-		o.RootOptions.GetRootCertificates = func(*GetRootCAsParams) (*GetRootCAsResults, error) {
+		o.RootOptions.GetRootCertificates = func(*ConnectionInfo) (*RootCertificates, error) {
 			km, err := o.RootOptions.RootProvider.KeyMaterial(context.Background())
 			if err != nil {
 				return nil, err
 			}
-			return &GetRootCAsResults{TrustCerts: km.Roots}, nil
+			return &RootCertificates{TrustCerts: km.Roots}, nil
 		}
 	default:
 		// No root certificate options specified by user. Use the certificates
@@ -401,7 +387,7 @@ func (o *ClientOptions) config() (*tls.Config, error) {
 	return config, nil
 }
 
-func (o *ServerOptions) config() (*tls.Config, error) {
+func (o *Options) serverConfig() (*tls.Config, error) {
 	// TODO(gtcooke94) Remove this block when o.VerifyPeer is remoed.
 	// VerifyPeer is deprecated, but do this to aid the transitory migration time.
 	if o.AdditionalPeerVerification == nil {
@@ -422,6 +408,12 @@ func (o *ServerOptions) config() (*tls.Config, error) {
 	if o.MaxTLSVersion == 0 {
 		o.MaxTLSVersion = o.MaxVersion
 	}
+	// TODO(gtcooke94) RootCACerts is deprecated, eventually remove this block.
+	// This will ensure that users still explicitly setting RootCACerts will get
+	// the setting int the right place.
+	if o.RootOptions.RootCACerts != nil {
+		o.RootOptions.RootCertificates = o.RootOptions.RootCACerts
+	}
 	if o.RequireClientCert && o.VerificationType == SkipVerification && o.AdditionalPeerVerification == nil {
 		return nil, fmt.Errorf("server needs to provide custom verification mechanism if choose to skip default verification, but require client certificate(s)")
 	}
@@ -461,19 +453,19 @@ func (o *ServerOptions) config() (*tls.Config, error) {
 	}
 	// Propagate root-certificate-related fields in tls.Config.
 	switch {
-	case o.RootOptions.RootCACerts != nil:
-		config.ClientCAs = o.RootOptions.RootCACerts
+	case o.RootOptions.RootCertificates != nil:
+		config.ClientCAs = o.RootOptions.RootCertificates
 	case o.RootOptions.GetRootCertificates != nil:
 		// In cases when users provide GetRootCertificates callback, since this
 		// callback is not contained in tls.Config, we have nothing to set here.
 		// We will invoke the callback in ServerHandshake.
 	case o.RootOptions.RootProvider != nil:
-		o.RootOptions.GetRootCertificates = func(*GetRootCAsParams) (*GetRootCAsResults, error) {
+		o.RootOptions.GetRootCertificates = func(*ConnectionInfo) (*RootCertificates, error) {
 			km, err := o.RootOptions.RootProvider.KeyMaterial(context.Background())
 			if err != nil {
 				return nil, err
 			}
-			return &GetRootCAsResults{TrustCerts: km.Roots}, nil
+			return &RootCertificates{TrustCerts: km.Roots}, nil
 		}
 	default:
 		// No root certificate options specified by user. Use the certificates
@@ -518,12 +510,12 @@ func (o *ServerOptions) config() (*tls.Config, error) {
 // advancedTLSCreds is the credentials required for authenticating a connection
 // using TLS.
 type advancedTLSCreds struct {
-	config            *tls.Config
-	verifyFunc        PostHandshakeVerificationFunc
-	getRootCAs        func(params *GetRootCAsParams) (*GetRootCAsResults, error)
-	isClient          bool
-	revocationOptions *RevocationOptions
-	verificationType  VerificationType
+	config              *tls.Config
+	verifyFunc          PostHandshakeVerificationFunc
+	getRootCertificates func(params *ConnectionInfo) (*RootCertificates, error)
+	isClient            bool
+	revocationOptions   *RevocationOptions
+	verificationType    VerificationType
 }
 
 func (c advancedTLSCreds) Info() credentials.ProtocolInfo {
@@ -589,10 +581,10 @@ func (c *advancedTLSCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credenti
 
 func (c *advancedTLSCreds) Clone() credentials.TransportCredentials {
 	return &advancedTLSCreds{
-		config:     credinternal.CloneTLSConfig(c.config),
-		verifyFunc: c.verifyFunc,
-		getRootCAs: c.getRootCAs,
-		isClient:   c.isClient,
+		config:              credinternal.CloneTLSConfig(c.config),
+		verifyFunc:          c.verifyFunc,
+		getRootCertificates: c.getRootCertificates,
+		isClient:            c.isClient,
 	}
 }
 
@@ -629,8 +621,8 @@ func buildVerifyFunc(c *advancedTLSCreds,
 				rootCAs = c.config.ClientCAs
 			}
 			// Reload root CA certs.
-			if rootCAs == nil && c.getRootCAs != nil {
-				results, err := c.getRootCAs(&GetRootCAsParams{
+			if rootCAs == nil && c.getRootCertificates != nil {
+				results, err := c.getRootCertificates(&ConnectionInfo{
 					RawConn:  rawConn,
 					RawCerts: rawCerts,
 				})
@@ -696,18 +688,18 @@ func buildVerifyFunc(c *advancedTLSCreds,
 
 // NewClientCreds uses ClientOptions to construct a TransportCredentials based
 // on TLS.
-func NewClientCreds(o *ClientOptions) (credentials.TransportCredentials, error) {
-	conf, err := o.config()
+func NewClientCreds(o *Options) (credentials.TransportCredentials, error) {
+	conf, err := o.clientConfig()
 	if err != nil {
 		return nil, err
 	}
 	tc := &advancedTLSCreds{
-		config:            conf,
-		isClient:          true,
-		getRootCAs:        o.RootOptions.GetRootCertificates,
-		verifyFunc:        o.AdditionalPeerVerification,
-		revocationOptions: o.RevocationOptions,
-		verificationType:  o.VerificationType,
+		config:              conf,
+		isClient:            true,
+		getRootCertificates: o.RootOptions.GetRootCertificates,
+		verifyFunc:          o.AdditionalPeerVerification,
+		revocationOptions:   o.RevocationOptions,
+		verificationType:    o.VerificationType,
 	}
 	tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
 	return tc, nil
@@ -715,18 +707,18 @@ func NewClientCreds(o *ClientOptions) (credentials.TransportCredentials, error)
 
 // NewServerCreds uses ServerOptions to construct a TransportCredentials based
 // on TLS.
-func NewServerCreds(o *ServerOptions) (credentials.TransportCredentials, error) {
-	conf, err := o.config()
+func NewServerCreds(o *Options) (credentials.TransportCredentials, error) {
+	conf, err := o.serverConfig()
 	if err != nil {
 		return nil, err
 	}
 	tc := &advancedTLSCreds{
-		config:            conf,
-		isClient:          false,
-		getRootCAs:        o.RootOptions.GetRootCertificates,
-		verifyFunc:        o.AdditionalPeerVerification,
-		revocationOptions: o.RevocationOptions,
-		verificationType:  o.VerificationType,
+		config:              conf,
+		isClient:            false,
+		getRootCertificates: o.RootOptions.GetRootCertificates,
+		verifyFunc:          o.AdditionalPeerVerification,
+		revocationOptions:   o.RevocationOptions,
+		verificationType:    o.VerificationType,
 	}
 	tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
 	return tc, nil
diff --git a/security/advancedtls/sni.go b/security/advancedtls/sni.go
index 3e7befb1..916e2723 100644
--- a/security/advancedtls/sni.go
+++ b/security/advancedtls/sni.go
@@ -25,7 +25,7 @@ import (
 
 // buildGetCertificates returns the certificate that matches the SNI field
 // for the given ClientHelloInfo, defaulting to the first element of o.GetCertificates.
-func buildGetCertificates(clientHello *tls.ClientHelloInfo, o *ServerOptions) (*tls.Certificate, error) {
+func buildGetCertificates(clientHello *tls.ClientHelloInfo, o *Options) (*tls.Certificate, error) {
 	if o.IdentityOptions.GetIdentityCertificatesForServer == nil {
 		return nil, fmt.Errorf("function GetCertificates must be specified")
 	}
diff --git a/xds/internal/balancer/cdsbalancer/cdsbalancer.go b/xds/internal/balancer/cdsbalancer/cdsbalancer.go
index 0d0e9d2a..8e97e104 100644
--- a/xds/internal/balancer/cdsbalancer/cdsbalancer.go
+++ b/xds/internal/balancer/cdsbalancer/cdsbalancer.go
@@ -609,21 +609,7 @@ func (b *cdsBalancer) generateDMsForCluster(name string, depth int, dms []cluste
 			Cluster:               cluster.ClusterName,
 			EDSServiceName:        cluster.EDSServiceName,
 			MaxConcurrentRequests: cluster.MaxRequests,
-		}
-		if cluster.LRSServerConfig == xdsresource.ClusterLRSServerSelf {
-			bootstrapConfig := b.xdsClient.BootstrapConfig()
-			parsedName := xdsresource.ParseName(cluster.ClusterName)
-			if parsedName.Scheme == xdsresource.FederationScheme {
-				// Is a federation resource name, find the corresponding
-				// authority server config.
-				if cfg, ok := bootstrapConfig.Authorities[parsedName.Authority]; ok {
-					dm.LoadReportingServer = cfg.XDSServer
-				}
-			} else {
-				// Not a federation resource name, use the default
-				// authority.
-				dm.LoadReportingServer = bootstrapConfig.XDSServer
-			}
+			LoadReportingServer:   cluster.LRSServerConfig,
 		}
 	case xdsresource.ClusterTypeLogicalDNS:
 		dm = clusterresolver.DiscoveryMechanism{
diff --git a/xds/internal/xdsclient/authority.go b/xds/internal/xdsclient/authority.go
index 6c82e70e..62d7a175 100644
--- a/xds/internal/xdsclient/authority.go
+++ b/xds/internal/xdsclient/authority.go
@@ -94,12 +94,6 @@ type authorityArgs struct {
 	// (although the former is part of the latter) is because authorities in the
 	// bootstrap config might contain an empty server config, and in this case,
 	// the top-level server config is to be used.
-	//
-	// There are two code paths from where a new authority struct might be
-	// created. One is when a watch is registered for a resource, and one is
-	// when load reporting needs to be started. We have the authority name in
-	// the first case, but do in the second. We only have the server config in
-	// the second case.
 	serverCfg          *bootstrap.ServerConfig
 	bootstrapCfg       *bootstrap.Config
 	serializer         *grpcsync.CallbackSerializer
@@ -156,7 +150,10 @@ func (a *authority) handleResourceUpdate(resourceUpdate transport.ResourceUpdate
 		return xdsresource.NewErrorf(xdsresource.ErrorTypeResourceTypeUnsupported, "Resource URL %v unknown in response from server", resourceUpdate.URL)
 	}
 
-	opts := &xdsresource.DecodeOptions{BootstrapConfig: a.bootstrapCfg}
+	opts := &xdsresource.DecodeOptions{
+		BootstrapConfig: a.bootstrapCfg,
+		ServerConfig:    a.serverCfg,
+	}
 	updates, md, err := decodeAllResources(opts, rType, resourceUpdate)
 	a.updateResourceStateAndScheduleCallbacks(rType, updates, md)
 	return err
diff --git a/xds/internal/xdsclient/clientimpl_authority.go b/xds/internal/xdsclient/clientimpl_authority.go
index c73e3e2e..69db79ee 100644
--- a/xds/internal/xdsclient/clientimpl_authority.go
+++ b/xds/internal/xdsclient/clientimpl_authority.go
@@ -36,7 +36,7 @@ import (
 // authority, without holding c.authorityMu.
 //
 // Caller must not hold c.authorityMu.
-func (c *clientImpl) findAuthority(n *xdsresource.Name) (_ *authority, unref func(), _ error) {
+func (c *clientImpl) findAuthority(n *xdsresource.Name) (*authority, func(), error) {
 	scheme, authority := n.Scheme, n.Authority
 
 	c.authorityMu.Lock()
diff --git a/xds/internal/xdsclient/clientimpl_watchers.go b/xds/internal/xdsclient/clientimpl_watchers.go
index 045fe77a..f64124da 100644
--- a/xds/internal/xdsclient/clientimpl_watchers.go
+++ b/xds/internal/xdsclient/clientimpl_watchers.go
@@ -48,15 +48,6 @@ func (c *clientImpl) WatchResource(rType xdsresource.Type, resourceName string,
 		return func() {}
 	}
 
-	// TODO: replace this with the code does the following when we have
-	// implemented generic watch API on the authority:
-	//  - Parse the resource name and extract the authority.
-	//  - Locate the corresponding authority object and acquire a reference to
-	//    it. If the authority is not found, error out.
-	//  - Call the watchResource() method on the authority.
-	//  - Return a cancel function to cancel the watch on the authority and to
-	//    release the reference.
-
 	// TODO: Make ParseName return an error if parsing fails, and
 	// schedule the OnError callback in that case.
 	n := xdsresource.ParseName(resourceName)
diff --git a/xds/internal/xdsclient/xdsresource/cluster_resource_type.go b/xds/internal/xdsclient/xdsresource/cluster_resource_type.go
index 183801c1..5ac7f031 100644
--- a/xds/internal/xdsclient/xdsresource/cluster_resource_type.go
+++ b/xds/internal/xdsclient/xdsresource/cluster_resource_type.go
@@ -55,7 +55,7 @@ type clusterResourceType struct {
 // Decode deserializes and validates an xDS resource serialized inside the
 // provided `Any` proto, as received from the xDS management server.
 func (clusterResourceType) Decode(opts *DecodeOptions, resource *anypb.Any) (*DecodeResult, error) {
-	name, cluster, err := unmarshalClusterResource(resource)
+	name, cluster, err := unmarshalClusterResource(resource, opts.ServerConfig)
 	switch {
 	case name == "":
 		// Name is unset only when protobuf deserialization fails.
diff --git a/xds/internal/xdsclient/xdsresource/resource_type.go b/xds/internal/xdsclient/xdsresource/resource_type.go
index cf1dff7f..a1e15e2d 100644
--- a/xds/internal/xdsclient/xdsresource/resource_type.go
+++ b/xds/internal/xdsclient/xdsresource/resource_type.go
@@ -133,9 +133,13 @@ type ResourceData interface {
 // DecodeOptions wraps the options required by ResourceType implementation for
 // decoding configuration received from the xDS management server.
 type DecodeOptions struct {
-	// BootstrapConfig contains the bootstrap configuration passed to the
-	// top-level xdsClient. This contains useful data for resource validation.
+	// BootstrapConfig contains the complete bootstrap configuration passed to
+	// the xDS client. This contains useful data for resource validation.
 	BootstrapConfig *bootstrap.Config
+	// ServerConfig contains the server config (from the above bootstrap
+	// configuration) of the xDS server from which the current resource, for
+	// which Decode() is being invoked, was received.
+	ServerConfig *bootstrap.ServerConfig
 }
 
 // DecodeResult is the result of a decode operation.
diff --git a/xds/internal/xdsclient/xdsresource/type_cds.go b/xds/internal/xdsclient/xdsresource/type_cds.go
index b782e245..ae4cb1e4 100644
--- a/xds/internal/xdsclient/xdsresource/type_cds.go
+++ b/xds/internal/xdsclient/xdsresource/type_cds.go
@@ -20,6 +20,7 @@ package xdsresource
 import (
 	"encoding/json"
 
+	"google.golang.org/grpc/internal/xds/bootstrap"
 	"google.golang.org/protobuf/types/known/anypb"
 )
 
@@ -39,18 +40,6 @@ const (
 	ClusterTypeAggregate
 )
 
-// ClusterLRSServerConfigType is the type of LRS server config.
-type ClusterLRSServerConfigType int
-
-const (
-	// ClusterLRSOff indicates LRS is off (loads are not reported for this
-	// cluster).
-	ClusterLRSOff ClusterLRSServerConfigType = iota
-	// ClusterLRSServerSelf indicates loads should be reported to the same
-	// server (the authority) where the CDS resp is received from.
-	ClusterLRSServerSelf
-)
-
 // ClusterUpdate contains information from a received CDS response, which is of
 // interest to the registered CDS watcher.
 type ClusterUpdate struct {
@@ -60,10 +49,10 @@ type ClusterUpdate struct {
 	// EDSServiceName is an optional name for EDS. If it's not set, the balancer
 	// should watch ClusterName for the EDS resources.
 	EDSServiceName string
-	// LRSServerConfig contains the server where the load reports should be sent
-	// to. This can be change to an interface, to support other types, e.g. a
-	// ServerConfig with ServerURI, creds.
-	LRSServerConfig ClusterLRSServerConfigType
+	// LRSServerConfig contains configuration about the xDS server that sent
+	// this cluster resource. This is also the server where load reports are to
+	// be sent, for this cluster.
+	LRSServerConfig *bootstrap.ServerConfig
 	// SecurityCfg contains security configuration sent by the control plane.
 	SecurityCfg *SecurityConfig
 	// MaxRequests for circuit breaking, if any (otherwise nil).
diff --git a/xds/internal/xdsclient/xdsresource/unmarshal_cds.go b/xds/internal/xdsclient/xdsresource/unmarshal_cds.go
index 276bf405..de12f478 100644
--- a/xds/internal/xdsclient/xdsresource/unmarshal_cds.go
+++ b/xds/internal/xdsclient/xdsresource/unmarshal_cds.go
@@ -34,6 +34,7 @@ import (
 	"google.golang.org/grpc/internal/envconfig"
 	"google.golang.org/grpc/internal/pretty"
 	iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
+	"google.golang.org/grpc/internal/xds/bootstrap"
 	"google.golang.org/grpc/internal/xds/matcher"
 	"google.golang.org/grpc/xds/internal/xdsclient/xdslbregistry"
 	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
@@ -50,7 +51,7 @@ var ValidateClusterAndConstructClusterUpdateForTesting = validateClusterAndConst
 // to this value by the management server.
 const transportSocketName = "envoy.transport_sockets.tls"
 
-func unmarshalClusterResource(r *anypb.Any) (string, ClusterUpdate, error) {
+func unmarshalClusterResource(r *anypb.Any, serverCfg *bootstrap.ServerConfig) (string, ClusterUpdate, error) {
 	r, err := UnwrapResource(r)
 	if err != nil {
 		return "", ClusterUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err)
@@ -64,7 +65,7 @@ func unmarshalClusterResource(r *anypb.Any) (string, ClusterUpdate, error) {
 	if err := proto.Unmarshal(r.GetValue(), cluster); err != nil {
 		return "", ClusterUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
 	}
-	cu, err := validateClusterAndConstructClusterUpdate(cluster)
+	cu, err := validateClusterAndConstructClusterUpdate(cluster, serverCfg)
 	if err != nil {
 		return cluster.GetName(), ClusterUpdate{}, err
 	}
@@ -81,7 +82,7 @@ const (
 	defaultLeastRequestChoiceCount = 2
 )
 
-func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster) (ClusterUpdate, error) {
+func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster, serverCfg *bootstrap.ServerConfig) (ClusterUpdate, error) {
 	telemetryLabels := make(map[string]string)
 	if fmd := cluster.GetMetadata().GetFilterMetadata(); fmd != nil {
 		if val, ok := fmd["com.google.csm.telemetry_labels"]; ok {
@@ -182,21 +183,11 @@ func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster) (Clu
 		TelemetryLabels:  telemetryLabels,
 	}
 
-	// Note that this is different from the gRFC (gRFC A47 says to include the
-	// full ServerConfig{URL,creds,server feature} here). This information is
-	// not available here, because this function doesn't have access to the
-	// xdsclient bootstrap information now (can be added if necessary). The
-	// ServerConfig will be read and populated by the CDS balancer when
-	// processing this field.
-	// According to A27:
-	// If the `lrs_server` field is set, it must have its `self` field set, in
-	// which case the client should use LRS for load reporting. Otherwise
-	// (the `lrs_server` field is not set), LRS load reporting will be disabled.
 	if lrs := cluster.GetLrsServer(); lrs != nil {
 		if lrs.GetSelf() == nil {
 			return ClusterUpdate{}, fmt.Errorf("unsupported config_source_specifier %T in lrs_server field", lrs.ConfigSourceSpecifier)
 		}
-		ret.LRSServerConfig = ClusterLRSServerSelf
+		ret.LRSServerConfig = serverCfg
 	}
 
 	// Validate and set cluster type from the response.



Test patch:
diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go
index fbee086f..6753764b 100644
--- a/metadata/metadata_test.go
+++ b/metadata/metadata_test.go
@@ -22,6 +22,7 @@ import (
 	"context"
 	"reflect"
 	"strconv"
+	"strings"
 	"testing"
 	"time"
 
@@ -338,6 +339,26 @@ func (s) TestAppendToOutgoingContext_FromKVSlice(t *testing.T) {
 	}
 }
 
+func TestStringerMD(t *testing.T) {
+	for _, test := range []struct {
+		md   MD
+		want []string
+	}{
+		{MD{}, []string{"MD{}"}},
+		{MD{"k1": []string{}}, []string{"MD{k1=[]}"}},
+		{MD{"k1": []string{"v1", "v2"}}, []string{"MD{k1=[v1, v2]}"}},
+		{MD{"k1": []string{"v1"}}, []string{"MD{k1=[v1]}"}},
+		{MD{"k1": []string{"v1", "v2"}, "k2": []string{}, "k3": []string{"1", "2", "3"}}, []string{"MD{", "k1=[v1, v2]", "k2=[]", "k3=[1, 2, 3]", "}"}},
+	} {
+		got := test.md.String()
+		for _, want := range test.want {
+			if !strings.Contains(got, want) {
+				t.Fatalf("Metadata string %q is missing %q", got, want)
+			}
+		}
+	}
+}
+
 // Old/slow approach to adding metadata to context
 func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) {
 	// TODO: Add in N=1-100 tests once Go1.6 support is removed.
diff --git a/peer/peer_test.go b/peer/peer_test.go
new file mode 100644
index 00000000..c763ff4a
--- /dev/null
+++ b/peer/peer_test.go
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright 2024 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 peer
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"google.golang.org/grpc/credentials"
+)
+
+// A struct that implements AuthInfo interface and implements CommonAuthInfo() method.
+type testAuthInfo struct {
+	credentials.CommonAuthInfo
+}
+
+func (ta testAuthInfo) AuthType() string {
+	return fmt.Sprintf("testAuthInfo-%d", ta.SecurityLevel)
+}
+
+type addr struct {
+	ipAddress string
+}
+
+func (addr) Network() string { return "" }
+
+func (a *addr) String() string { return a.ipAddress }
+
+func TestPeerStringer(t *testing.T) {
+	testCases := []struct {
+		name string
+		peer *Peer
+		want string
+	}{
+		{
+			name: "+Addr-LocalAddr+ValidAuth",
+			peer: &Peer{Addr: &addr{"example.com:1234"}, AuthInfo: testAuthInfo{credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}},
+			want: "Peer{Addr: 'example.com:1234', LocalAddr: <nil>, AuthInfo: 'testAuthInfo-3'}",
+		},
+		{
+			name: "+Addr+LocalAddr+ValidAuth",
+			peer: &Peer{Addr: &addr{"example.com:1234"}, LocalAddr: &addr{"example.com:1234"}, AuthInfo: testAuthInfo{credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}},
+			want: "Peer{Addr: 'example.com:1234', LocalAddr: 'example.com:1234', AuthInfo: 'testAuthInfo-3'}",
+		},
+		{
+			name: "+Addr-LocalAddr+emptyAuth",
+			peer: &Peer{Addr: &addr{"1.2.3.4:1234"}, AuthInfo: testAuthInfo{credentials.CommonAuthInfo{}}},
+			want: "Peer{Addr: '1.2.3.4:1234', LocalAddr: <nil>, AuthInfo: 'testAuthInfo-0'}",
+		},
+		{
+			name: "-Addr-LocalAddr+emptyAuth",
+			peer: &Peer{AuthInfo: testAuthInfo{}},
+			want: "Peer{Addr: <nil>, LocalAddr: <nil>, AuthInfo: 'testAuthInfo-0'}",
+		},
+		{
+			name: "zeroedPeer",
+			peer: &Peer{},
+			want: "Peer{Addr: <nil>, LocalAddr: <nil>, AuthInfo: <nil>}",
+		},
+		{
+			name: "nilPeer",
+			peer: nil,
+			want: "Peer<nil>",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := NewContext(context.Background(), tc.peer)
+			p, ok := FromContext(ctx)
+			if !ok {
+				t.Fatalf("Unable to get peer from context")
+			}
+			if p.String() != tc.want {
+				t.Fatalf("Error using peer String(): expected %q, got %q", tc.want, p.String())
+			}
+		})
+	}
+}
diff --git a/security/advancedtls/advancedtls_integration_test.go b/security/advancedtls/advancedtls_integration_test.go
index d3c5d143..1eb6a317 100644
--- a/security/advancedtls/advancedtls_integration_test.go
+++ b/security/advancedtls/advancedtls_integration_test.go
@@ -142,13 +142,13 @@ func (s) TestEnd2End(t *testing.T) {
 		clientCert             []tls.Certificate
 		clientGetCert          func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
 		clientRoot             *x509.CertPool
-		clientGetRoot          func(params *GetRootCAsParams) (*GetRootCAsResults, error)
+		clientGetRoot          func(params *ConnectionInfo) (*RootCertificates, error)
 		clientVerifyFunc       PostHandshakeVerificationFunc
 		clientVerificationType VerificationType
 		serverCert             []tls.Certificate
 		serverGetCert          func(*tls.ClientHelloInfo) ([]*tls.Certificate, error)
 		serverRoot             *x509.CertPool
-		serverGetRoot          func(params *GetRootCAsParams) (*GetRootCAsResults, error)
+		serverGetRoot          func(params *ConnectionInfo) (*RootCertificates, error)
 		serverVerifyFunc       PostHandshakeVerificationFunc
 		serverVerificationType VerificationType
 	}{
@@ -180,12 +180,12 @@ func (s) TestEnd2End(t *testing.T) {
 			},
 			clientVerificationType: CertVerification,
 			serverCert:             []tls.Certificate{cs.ServerCert1},
-			serverGetRoot: func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
+			serverGetRoot: func(params *ConnectionInfo) (*RootCertificates, error) {
 				switch stage.read() {
 				case 0, 1:
-					return &GetRootCAsResults{TrustCerts: cs.ServerTrust1}, nil
+					return &RootCertificates{TrustCerts: cs.ServerTrust1}, nil
 				default:
-					return &GetRootCAsResults{TrustCerts: cs.ServerTrust2}, nil
+					return &RootCertificates{TrustCerts: cs.ServerTrust2}, nil
 				}
 			},
 			serverVerifyFunc: func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
@@ -208,12 +208,12 @@ func (s) TestEnd2End(t *testing.T) {
 		{
 			desc:       "test the reloading feature for server identity callback and client trust callback",
 			clientCert: []tls.Certificate{cs.ClientCert1},
-			clientGetRoot: func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
+			clientGetRoot: func(params *ConnectionInfo) (*RootCertificates, error) {
 				switch stage.read() {
 				case 0, 1:
-					return &GetRootCAsResults{TrustCerts: cs.ClientTrust1}, nil
+					return &RootCertificates{TrustCerts: cs.ClientTrust1}, nil
 				default:
-					return &GetRootCAsResults{TrustCerts: cs.ClientTrust2}, nil
+					return &RootCertificates{TrustCerts: cs.ClientTrust2}, nil
 				}
 			},
 			clientVerifyFunc: func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
@@ -250,12 +250,12 @@ func (s) TestEnd2End(t *testing.T) {
 		{
 			desc:       "test client custom verification",
 			clientCert: []tls.Certificate{cs.ClientCert1},
-			clientGetRoot: func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
+			clientGetRoot: func(params *ConnectionInfo) (*RootCertificates, error) {
 				switch stage.read() {
 				case 0:
-					return &GetRootCAsResults{TrustCerts: cs.ClientTrust1}, nil
+					return &RootCertificates{TrustCerts: cs.ClientTrust1}, nil
 				default:
-					return &GetRootCAsResults{TrustCerts: cs.ClientTrust2}, nil
+					return &RootCertificates{TrustCerts: cs.ClientTrust2}, nil
 				}
 			},
 			clientVerifyFunc: func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
@@ -336,13 +336,13 @@ func (s) TestEnd2End(t *testing.T) {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
 			// Start a server using ServerOptions in another goroutine.
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates:                     test.serverCert,
 					GetIdentityCertificatesForServer: test.serverGetCert,
 				},
 				RootOptions: RootCertificateOptions{
-					RootCACerts:         test.serverRoot,
+					RootCertificates:    test.serverRoot,
 					GetRootCertificates: test.serverGetRoot,
 				},
 				RequireClientCert:          true,
@@ -363,14 +363,14 @@ func (s) TestEnd2End(t *testing.T) {
 			addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
 			pb.RegisterGreeterServer(s, greeterServer{})
 			go s.Serve(lis)
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates:                     test.clientCert,
 					GetIdentityCertificatesForClient: test.clientGetCert,
 				},
 				AdditionalPeerVerification: test.clientVerifyFunc,
 				RootOptions: RootCertificateOptions{
-					RootCACerts:         test.clientRoot,
+					RootCertificates:    test.clientRoot,
 					GetRootCertificates: test.clientGetRoot,
 				},
 				VerificationType: test.clientVerificationType,
@@ -627,7 +627,7 @@ func (s) TestPEMFileProviderEnd2End(t *testing.T) {
 			defer serverIdentityProvider.Close()
 			defer serverRootProvider.Close()
 			// Start a server and create a client using advancedtls API with Provider.
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					IdentityProvider: serverIdentityProvider,
 				},
@@ -654,7 +654,7 @@ func (s) TestPEMFileProviderEnd2End(t *testing.T) {
 			addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
 			pb.RegisterGreeterServer(s, greeterServer{})
 			go s.Serve(lis)
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					IdentityProvider: clientIdentityProvider,
 				},
@@ -764,7 +764,7 @@ func (s) TestDefaultHostNameCheck(t *testing.T) {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
 			// Start a server using ServerOptions in another goroutine.
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates: test.serverCert,
 				},
@@ -785,9 +785,9 @@ func (s) TestDefaultHostNameCheck(t *testing.T) {
 			addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
 			pb.RegisterGreeterServer(s, greeterServer{})
 			go s.Serve(lis)
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				RootOptions: RootCertificateOptions{
-					RootCACerts: test.clientRoot,
+					RootCertificates: test.clientRoot,
 				},
 				VerificationType: test.clientVerificationType,
 			}
@@ -902,7 +902,7 @@ func (s) TestTLSVersions(t *testing.T) {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
 			// Start a server using ServerOptions in another goroutine.
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates: []tls.Certificate{cs.ServerPeerLocalhost1},
 				},
@@ -925,9 +925,9 @@ func (s) TestTLSVersions(t *testing.T) {
 			addr := fmt.Sprintf("localhost:%v", lis.Addr().(*net.TCPAddr).Port)
 			pb.RegisterGreeterServer(s, greeterServer{})
 			go s.Serve(lis)
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				RootOptions: RootCertificateOptions{
-					RootCACerts: cs.ClientTrust1,
+					RootCertificates: cs.ClientTrust1,
 				},
 				VerificationType: CertAndHostVerification,
 				MinTLSVersion:    test.clientMinVersion,
diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go
index 6b0054ff..34c69058 100644
--- a/security/advancedtls/advancedtls_test.go
+++ b/security/advancedtls/advancedtls_test.go
@@ -103,8 +103,8 @@ func (s) TestClientOptionsConfigErrorCases(t *testing.T) {
 			desc:                   "More than one fields in RootCertificateOptions is specified",
 			clientVerificationType: CertVerification,
 			RootOptions: RootCertificateOptions{
-				RootCACerts:  x509.NewCertPool(),
-				RootProvider: fakeProvider{},
+				RootCertificates: x509.NewCertPool(),
+				RootProvider:     fakeProvider{},
 			},
 		},
 		{
@@ -134,14 +134,14 @@ func (s) TestClientOptionsConfigErrorCases(t *testing.T) {
 	for _, test := range tests {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				VerificationType: test.clientVerificationType,
 				IdentityOptions:  test.IdentityOptions,
 				RootOptions:      test.RootOptions,
 				MinTLSVersion:    test.MinVersion,
 				MaxTLSVersion:    test.MaxVersion,
 			}
-			_, err := clientOptions.config()
+			_, err := clientOptions.clientConfig()
 			if err == nil {
 				t.Fatalf("ClientOptions{%v}.config() returns no err, wantErr != nil", clientOptions)
 			}
@@ -154,10 +154,10 @@ func (s) TestClientOptionsConfigErrorCases(t *testing.T) {
 // VerificationType. This should error because one cannot skip default
 // verification and provide no root credentials",
 func (s) TestClientOptionsWithDeprecatedVType(t *testing.T) {
-	clientOptions := &ClientOptions{
+	clientOptions := &Options{
 		VType: SkipVerification,
 	}
-	_, err := clientOptions.config()
+	_, err := clientOptions.clientConfig()
 	if err == nil {
 		t.Fatalf("ClientOptions{%v}.config() returns no err, wantErr != nil", clientOptions)
 	}
@@ -192,20 +192,20 @@ func (s) TestClientOptionsConfigSuccessCases(t *testing.T) {
 	for _, test := range tests {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				VerificationType: test.clientVerificationType,
 				IdentityOptions:  test.IdentityOptions,
 				RootOptions:      test.RootOptions,
 				MinTLSVersion:    test.MinVersion,
 				MaxTLSVersion:    test.MaxVersion,
 			}
-			clientConfig, err := clientOptions.config()
+			clientConfig, err := clientOptions.clientConfig()
 			if err != nil {
 				t.Fatalf("ClientOptions{%v}.config() = %v, wantErr == nil", clientOptions, err)
 			}
 			// Verify that the system-provided certificates would be used
 			// when no verification method was set in clientOptions.
-			if clientOptions.RootOptions.RootCACerts == nil &&
+			if clientOptions.RootOptions.RootCertificates == nil &&
 				clientOptions.RootOptions.GetRootCertificates == nil && clientOptions.RootOptions.RootProvider == nil {
 				if clientConfig.RootCAs == nil {
 					t.Fatalf("Failed to assign system-provided certificates on the client side.")
@@ -253,8 +253,8 @@ func (s) TestServerOptionsConfigErrorCases(t *testing.T) {
 			requireClientCert:      true,
 			serverVerificationType: CertVerification,
 			RootOptions: RootCertificateOptions{
-				RootCACerts: x509.NewCertPool(),
-				GetRootCertificates: func(*GetRootCAsParams) (*GetRootCAsResults, error) {
+				RootCertificates: x509.NewCertPool(),
+				GetRootCertificates: func(*ConnectionInfo) (*RootCertificates, error) {
 					return nil, nil
 				},
 			},
@@ -288,7 +288,7 @@ func (s) TestServerOptionsConfigErrorCases(t *testing.T) {
 	for _, test := range tests {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				VerificationType:  test.serverVerificationType,
 				RequireClientCert: test.requireClientCert,
 				IdentityOptions:   test.IdentityOptions,
@@ -296,9 +296,9 @@ func (s) TestServerOptionsConfigErrorCases(t *testing.T) {
 				MinTLSVersion:     test.MinVersion,
 				MaxTLSVersion:     test.MaxVersion,
 			}
-			_, err := serverOptions.config()
+			_, err := serverOptions.serverConfig()
 			if err == nil {
-				t.Fatalf("ServerOptions{%v}.config() returns no err, wantErr != nil", serverOptions)
+				t.Fatalf("ServerOptions{%v}.serverConfig() returns no err, wantErr != nil", serverOptions)
 			}
 		})
 	}
@@ -309,10 +309,10 @@ func (s) TestServerOptionsConfigErrorCases(t *testing.T) {
 // VerificationType. This should error because one cannot skip default
 // verification and provide no root credentials",
 func (s) TestServerOptionsWithDeprecatedVType(t *testing.T) {
-	serverOptions := &ServerOptions{
+	serverOptions := &Options{
 		VType: SkipVerification,
 	}
-	_, err := serverOptions.config()
+	_, err := serverOptions.serverConfig()
 	if err == nil {
 		t.Fatalf("ClientOptions{%v}.config() returns no err, wantErr != nil", serverOptions)
 	}
@@ -355,7 +355,7 @@ func (s) TestServerOptionsConfigSuccessCases(t *testing.T) {
 	for _, test := range tests {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				VerificationType:  test.serverVerificationType,
 				RequireClientCert: test.requireClientCert,
 				IdentityOptions:   test.IdentityOptions,
@@ -363,13 +363,13 @@ func (s) TestServerOptionsConfigSuccessCases(t *testing.T) {
 				MinTLSVersion:     test.MinVersion,
 				MaxTLSVersion:     test.MaxVersion,
 			}
-			serverConfig, err := serverOptions.config()
+			serverConfig, err := serverOptions.serverConfig()
 			if err != nil {
 				t.Fatalf("ServerOptions{%v}.config() = %v, wantErr == nil", serverOptions, err)
 			}
 			// Verify that the system-provided certificates would be used
 			// when no verification method was set in serverOptions.
-			if serverOptions.RootOptions.RootCACerts == nil &&
+			if serverOptions.RootOptions.RootCertificates == nil &&
 				serverOptions.RootOptions.GetRootCertificates == nil && serverOptions.RootOptions.RootProvider == nil {
 				if serverConfig.ClientCAs == nil {
 					t.Fatalf("Failed to assign system-provided certificates on the server side.")
@@ -384,9 +384,14 @@ func (s) TestClientServerHandshake(t *testing.T) {
 	if err := cs.LoadCerts(); err != nil {
 		t.Fatalf("cs.LoadCerts() failed, err: %v", err)
 	}
-	getRootCAsForClient := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
+	getRootCertificatesForClient := func(params *ConnectionInfo) (*RootCertificates, error) {
+		return &RootCertificates{TrustCerts: cs.ClientTrust1}, nil
+	}
+
+	getRootCertificatesForClientDeprecatedTypes := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
 		return &GetRootCAsResults{TrustCerts: cs.ClientTrust1}, nil
 	}
+
 	clientVerifyFuncGood := func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
 		if params.ServerName == "" {
 			return nil, errors.New("client side server name should have a value")
@@ -401,8 +406,8 @@ func (s) TestClientServerHandshake(t *testing.T) {
 	verifyFuncBad := func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
 		return nil, fmt.Errorf("custom verification function failed")
 	}
-	getRootCAsForServer := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
-		return &GetRootCAsResults{TrustCerts: cs.ServerTrust1}, nil
+	getRootCertificatesForServer := func(params *ConnectionInfo) (*RootCertificates, error) {
+		return &RootCertificates{TrustCerts: cs.ServerTrust1}, nil
 	}
 	serverVerifyFunc := func(params *HandshakeVerificationInfo) (*PostHandshakeVerificationResults, error) {
 		if params.ServerName != "" {
@@ -415,16 +420,16 @@ func (s) TestClientServerHandshake(t *testing.T) {
 
 		return &PostHandshakeVerificationResults{}, nil
 	}
-	getRootCAsForServerBad := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
+	getRootCertificatesForServerBad := func(params *ConnectionInfo) (*RootCertificates, error) {
 		return nil, fmt.Errorf("bad root certificate reloading")
 	}
 
-	getRootCAsForClientCRL := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
-		return &GetRootCAsResults{TrustCerts: cs.ClientTrust3}, nil
+	getRootCertificatesForClientCRL := func(params *ConnectionInfo) (*RootCertificates, error) {
+		return &RootCertificates{TrustCerts: cs.ClientTrust3}, nil
 	}
 
-	getRootCAsForServerCRL := func(params *GetRootCAsParams) (*GetRootCAsResults, error) {
-		return &GetRootCAsResults{TrustCerts: cs.ServerTrust3}, nil
+	getRootCertificatesForServerCRL := func(params *ConnectionInfo) (*RootCertificates, error) {
+		return &RootCertificates{TrustCerts: cs.ServerTrust3}, nil
 	}
 
 	makeStaticCRLRevocationOptions := func(crlPath string, denyUndetermined bool) *RevocationOptions {
@@ -448,7 +453,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		clientCert                 []tls.Certificate
 		clientGetCert              func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
 		clientRoot                 *x509.CertPool
-		clientGetRoot              func(params *GetRootCAsParams) (*GetRootCAsResults, error)
+		clientGetRoot              func(params *ConnectionInfo) (*RootCertificates, error)
 		clientVerifyFunc           PostHandshakeVerificationFunc
 		clientVerificationType     VerificationType
 		clientRootProvider         certprovider.Provider
@@ -459,7 +464,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		serverCert                 []tls.Certificate
 		serverGetCert              func(*tls.ClientHelloInfo) ([]*tls.Certificate, error)
 		serverRoot                 *x509.CertPool
-		serverGetRoot              func(params *GetRootCAsParams) (*GetRootCAsResults, error)
+		serverGetRoot              func(params *ConnectionInfo) (*RootCertificates, error)
 		serverVerifyFunc           PostHandshakeVerificationFunc
 		serverVerificationType     VerificationType
 		serverRootProvider         certprovider.Provider
@@ -484,7 +489,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		// Expected Behavior: success
 		{
 			desc:                   "Client sets reload root function with verifyFuncGood; server sends peer cert",
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverCert:             []tls.Certificate{cs.ServerCert1},
@@ -496,7 +501,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		// Reason: custom verification function is bad
 		{
 			desc:                       "Client sets reload root function with verifyFuncBad; server sends peer cert",
-			clientGetRoot:              getRootCAsForClient,
+			clientGetRoot:              getRootCertificatesForClient,
 			clientVerifyFunc:           verifyFuncBad,
 			clientVerificationType:     CertVerification,
 			clientExpectHandshakeError: true,
@@ -504,13 +509,27 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			serverVerificationType:     CertVerification,
 			serverExpectError:          true,
 		},
+		// Client: set clientGetRoot with deprecated types, clientVerifyFunc and
+		// clientCert Server: set serverRoot and serverCert with mutual TLS on
+		// Expected Behavior: success
+		{
+			desc:                   "Client sets peer cert, reload root function with deprecatd types with verifyFuncGood; server sets peer cert and root cert; mutualTLS",
+			clientCert:             []tls.Certificate{cs.ClientCert1},
+			clientGetRoot:          getRootCertificatesForClientDeprecatedTypes,
+			clientVerifyFunc:       clientVerifyFuncGood,
+			clientVerificationType: CertVerification,
+			serverMutualTLS:        true,
+			serverCert:             []tls.Certificate{cs.ServerCert1},
+			serverRoot:             cs.ServerTrust1,
+			serverVerificationType: CertVerification,
+		},
 		// Client: set clientGetRoot, clientVerifyFunc and clientCert
 		// Server: set serverRoot and serverCert with mutual TLS on
 		// Expected Behavior: success
 		{
 			desc:                   "Client sets peer cert, reload root function with verifyFuncGood; server sets peer cert and root cert; mutualTLS",
 			clientCert:             []tls.Certificate{cs.ClientCert1},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
@@ -524,12 +543,12 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                   "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; mutualTLS",
 			clientCert:             []tls.Certificate{cs.ClientCert1},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
 			serverCert:             []tls.Certificate{cs.ServerCert1},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerificationType: CertVerification,
 		},
 		// Client: set clientGetRoot, clientVerifyFunc and clientCert
@@ -540,12 +559,12 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                   "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, bad reload root function; mutualTLS",
 			clientCert:             []tls.Certificate{cs.ClientCert1},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
 			serverCert:             []tls.Certificate{cs.ServerCert1},
-			serverGetRoot:          getRootCAsForServerBad,
+			serverGetRoot:          getRootCertificatesForServerBad,
 			serverVerificationType: CertVerification,
 			serverExpectError:      true,
 		},
@@ -557,14 +576,14 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			clientGetCert: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 				return &cs.ClientCert1, nil
 			},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
 			serverGetCert: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 				return []*tls.Certificate{&cs.ServerCert1}, nil
 			},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerifyFunc:       serverVerifyFunc,
 			serverVerificationType: CertVerification,
 		},
@@ -578,14 +597,14 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			clientGetCert: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 				return &cs.ServerCert1, nil
 			},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
 			serverGetCert: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 				return []*tls.Certificate{&cs.ServerCert1}, nil
 			},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerifyFunc:       serverVerifyFunc,
 			serverVerificationType: CertVerification,
 			serverExpectError:      true,
@@ -599,7 +618,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			clientGetCert: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 				return &cs.ClientCert1, nil
 			},
-			clientGetRoot:              getRootCAsForServer,
+			clientGetRoot:              getRootCertificatesForServer,
 			clientVerifyFunc:           clientVerifyFuncGood,
 			clientVerificationType:     CertVerification,
 			clientExpectHandshakeError: true,
@@ -607,7 +626,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			serverGetCert: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 				return []*tls.Certificate{&cs.ServerCert1}, nil
 			},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerifyFunc:       serverVerifyFunc,
 			serverVerificationType: CertVerification,
 			serverExpectError:      true,
@@ -622,14 +641,14 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			clientGetCert: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 				return &cs.ClientCert1, nil
 			},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			serverMutualTLS:        true,
 			serverGetCert: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 				return []*tls.Certificate{&cs.ClientCert1}, nil
 			},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerifyFunc:       serverVerifyFunc,
 			serverVerificationType: CertVerification,
 			serverExpectError:      true,
@@ -643,7 +662,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			clientGetCert: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
 				return &cs.ClientCert1, nil
 			},
-			clientGetRoot:              getRootCAsForClient,
+			clientGetRoot:              getRootCertificatesForClient,
 			clientVerifyFunc:           clientVerifyFuncGood,
 			clientVerificationType:     CertVerification,
 			clientExpectHandshakeError: true,
@@ -651,7 +670,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			serverGetCert: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 				return []*tls.Certificate{&cs.ServerCert1}, nil
 			},
-			serverGetRoot:          getRootCAsForClient,
+			serverGetRoot:          getRootCertificatesForClient,
 			serverVerifyFunc:       serverVerifyFunc,
 			serverVerificationType: CertVerification,
 			serverExpectError:      true,
@@ -663,13 +682,13 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                       "Client sets peer cert, reload root function with verifyFuncGood; Server sets bad custom check; mutualTLS",
 			clientCert:                 []tls.Certificate{cs.ClientCert1},
-			clientGetRoot:              getRootCAsForClient,
+			clientGetRoot:              getRootCertificatesForClient,
 			clientVerifyFunc:           clientVerifyFuncGood,
 			clientVerificationType:     CertVerification,
 			clientExpectHandshakeError: true,
 			serverMutualTLS:            true,
 			serverCert:                 []tls.Certificate{cs.ServerCert1},
-			serverGetRoot:              getRootCAsForServer,
+			serverGetRoot:              getRootCertificatesForServer,
 			serverVerifyFunc:           verifyFuncBad,
 			serverVerificationType:     CertVerification,
 			serverExpectError:          true,
@@ -754,7 +773,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                   "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; mutualTLS",
 			clientCert:             []tls.Certificate{cs.ClientCert1},
-			clientGetRoot:          getRootCAsForClient,
+			clientGetRoot:          getRootCertificatesForClient,
 			clientVerifyFunc:       clientVerifyFuncGood,
 			clientVerificationType: CertVerification,
 			clientRevocationOptions: &RevocationOptions{
@@ -764,7 +783,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 			},
 			serverMutualTLS:        true,
 			serverCert:             []tls.Certificate{cs.ServerCert1},
-			serverGetRoot:          getRootCAsForServer,
+			serverGetRoot:          getRootCertificatesForServer,
 			serverVerificationType: CertVerification,
 			serverRevocationOptions: &RevocationOptions{
 				RootDir:          testdata.Path("crl"),
@@ -778,13 +797,13 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                    "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; Client uses CRL; mutualTLS",
 			clientCert:              []tls.Certificate{cs.ClientCertForCRL},
-			clientGetRoot:           getRootCAsForClientCRL,
+			clientGetRoot:           getRootCertificatesForClientCRL,
 			clientVerifyFunc:        clientVerifyFuncGood,
 			clientVerificationType:  CertVerification,
 			clientRevocationOptions: makeStaticCRLRevocationOptions(testdata.Path("crl/provider_crl_empty.pem"), true),
 			serverMutualTLS:         true,
 			serverCert:              []tls.Certificate{cs.ServerCertForCRL},
-			serverGetRoot:           getRootCAsForServerCRL,
+			serverGetRoot:           getRootCertificatesForServerCRL,
 			serverVerificationType:  CertVerification,
 		},
 		// Client: set valid credentials with the revocation config
@@ -793,13 +812,13 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                    "Client sets peer cert, reload root function with verifyFuncGood; Server sets revoked cert; Client uses CRL; mutualTLS",
 			clientCert:              []tls.Certificate{cs.ClientCertForCRL},
-			clientGetRoot:           getRootCAsForClientCRL,
+			clientGetRoot:           getRootCertificatesForClientCRL,
 			clientVerifyFunc:        clientVerifyFuncGood,
 			clientVerificationType:  CertVerification,
 			clientRevocationOptions: makeStaticCRLRevocationOptions(testdata.Path("crl/provider_crl_server_revoked.pem"), true),
 			serverMutualTLS:         true,
 			serverCert:              []tls.Certificate{cs.ServerCertForCRL},
-			serverGetRoot:           getRootCAsForServerCRL,
+			serverGetRoot:           getRootCertificatesForServerCRL,
 			serverVerificationType:  CertVerification,
 			serverExpectError:       true,
 		},
@@ -810,13 +829,13 @@ func (s) TestClientServerHandshake(t *testing.T) {
 		{
 			desc:                    "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; Client uses CRL; mutualTLS",
 			clientCert:              []tls.Certificate{cs.ClientCertForCRL},
-			clientGetRoot:           getRootCAsForClientCRL,
+			clientGetRoot:           getRootCertificatesForClientCRL,
 			clientVerifyFunc:        clientVerifyFuncGood,
 			clientVerificationType:  CertVerification,
 			clientRevocationOptions: makeStaticCRLRevocationOptions(testdata.Path("crl/provider_malicious_crl_empty.pem"), true),
 			serverMutualTLS:         true,
 			serverCert:              []tls.Certificate{cs.ServerCertForCRL},
-			serverGetRoot:           getRootCAsForServerCRL,
+			serverGetRoot:           getRootCertificatesForServerCRL,
 			serverVerificationType:  CertVerification,
 			serverExpectError:       true,
 		},
@@ -829,14 +848,14 @@ func (s) TestClientServerHandshake(t *testing.T) {
 				t.Fatalf("Failed to listen: %v", err)
 			}
 			// Start a server using ServerOptions in another goroutine.
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates:                     test.serverCert,
 					GetIdentityCertificatesForServer: test.serverGetCert,
 					IdentityProvider:                 test.serverIdentityProvider,
 				},
 				RootOptions: RootCertificateOptions{
-					RootCACerts:         test.serverRoot,
+					RootCertificates:    test.serverRoot,
 					GetRootCertificates: test.serverGetRoot,
 					RootProvider:        test.serverRootProvider,
 				},
@@ -845,7 +864,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 				VerificationType:           test.serverVerificationType,
 				RevocationOptions:          test.serverRevocationOptions,
 			}
-			go func(done chan credentials.AuthInfo, lis net.Listener, serverOptions *ServerOptions) {
+			go func(done chan credentials.AuthInfo, lis net.Listener, serverOptions *Options) {
 				serverRawConn, err := lis.Accept()
 				if err != nil {
 					close(done)
@@ -873,7 +892,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 				t.Fatalf("Client failed to connect to %s. Error: %v", lisAddr, err)
 			}
 			defer conn.Close()
-			clientOptions := &ClientOptions{
+			clientOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					Certificates:                     test.clientCert,
 					GetIdentityCertificatesForClient: test.clientGetCert,
@@ -881,7 +900,7 @@ func (s) TestClientServerHandshake(t *testing.T) {
 				},
 				AdditionalPeerVerification: test.clientVerifyFunc,
 				RootOptions: RootCertificateOptions{
-					RootCACerts:         test.clientRoot,
+					RootCertificates:    test.clientRoot,
 					GetRootCertificates: test.clientGetRoot,
 					RootProvider:        test.clientRootProvider,
 				},
@@ -944,9 +963,9 @@ func (s) TestAdvancedTLSOverrideServerName(t *testing.T) {
 	if err := cs.LoadCerts(); err != nil {
 		t.Fatalf("cs.LoadCerts() failed, err: %v", err)
 	}
-	clientOptions := &ClientOptions{
+	clientOptions := &Options{
 		RootOptions: RootCertificateOptions{
-			RootCACerts: cs.ClientTrust1,
+			RootCertificates: cs.ClientTrust1,
 		},
 		serverNameOverride: expectedServerName,
 	}
@@ -988,16 +1007,16 @@ func (s) TestGetCertificatesSNI(t *testing.T) {
 	for _, test := range tests {
 		test := test
 		t.Run(test.desc, func(t *testing.T) {
-			serverOptions := &ServerOptions{
+			serverOptions := &Options{
 				IdentityOptions: IdentityCertificateOptions{
 					GetIdentityCertificatesForServer: func(info *tls.ClientHelloInfo) ([]*tls.Certificate, error) {
 						return []*tls.Certificate{&cs.ServerCert1, &cs.ServerCert2, &cs.ServerPeer3}, nil
 					},
 				},
 			}
-			serverConfig, err := serverOptions.config()
+			serverConfig, err := serverOptions.serverConfig()
 			if err != nil {
-				t.Fatalf("serverOptions.config() failed: %v", err)
+				t.Fatalf("serverOptions.serverConfig() failed: %v", err)
 			}
 			pointFormatUncompressed := uint8(0)
 			clientHello := &tls.ClientHelloInfo{
diff --git a/xds/internal/xdsclient/tests/resource_update_test.go b/xds/internal/xdsclient/tests/resource_update_test.go
index 783e9e41..89b5d338 100644
--- a/xds/internal/xdsclient/tests/resource_update_test.go
+++ b/xds/internal/xdsclient/tests/resource_update_test.go
@@ -669,9 +669,8 @@ func (s) TestHandleClusterResponseFromManagementServer(t *testing.T) {
 				Resources:   []*anypb.Any{testutils.MarshalAny(t, resource1)},
 			},
 			wantUpdate: xdsresource.ClusterUpdate{
-				ClusterName:     "resource-name-1",
-				EDSServiceName:  "eds-service-name",
-				LRSServerConfig: xdsresource.ClusterLRSServerSelf,
+				ClusterName:    "resource-name-1",
+				EDSServiceName: "eds-service-name",
 			},
 			wantUpdateMetadata: map[string]xdsresource.UpdateWithMD{
 				"resource-name-1": {
@@ -689,9 +688,8 @@ func (s) TestHandleClusterResponseFromManagementServer(t *testing.T) {
 				Resources:   []*anypb.Any{testutils.MarshalAny(t, resource1), testutils.MarshalAny(t, resource2)},
 			},
 			wantUpdate: xdsresource.ClusterUpdate{
-				ClusterName:     "resource-name-1",
-				EDSServiceName:  "eds-service-name",
-				LRSServerConfig: xdsresource.ClusterLRSServerSelf,
+				ClusterName:    "resource-name-1",
+				EDSServiceName: "eds-service-name",
 			},
 			wantUpdateMetadata: map[string]xdsresource.UpdateWithMD{
 				"resource-name-1": {
@@ -763,6 +761,16 @@ func (s) TestHandleClusterResponseFromManagementServer(t *testing.T) {
 			if gotErr != nil && !strings.Contains(gotErr.Error(), test.wantErr) {
 				t.Fatalf("Got error from handling update: %v, want %v", gotErr, test.wantErr)
 			}
+
+			// For tests expected to succeed, we expect an LRS server config in
+			// the update from the xDS client, because the LRS bit is turned on
+			// in the cluster resource. We *cannot* set the LRS server config in
+			// the test table because we do not have the address of the xDS
+			// server at that point, hence we do it here before verifying the
+			// received update.
+			if test.wantErr == "" {
+				test.wantUpdate.LRSServerConfig = xdstestutils.ServerConfigForAddress(t, mgmtServer.Address)
+			}
 			cmpOpts := []cmp.Option{
 				cmpopts.EquateEmpty(),
 				cmpopts.IgnoreFields(xdsresource.ClusterUpdate{}, "Raw", "LBPolicy"),
diff --git a/xds/internal/xdsclient/xdsresource/tests/unmarshal_cds_test.go b/xds/internal/xdsclient/xdsresource/tests/unmarshal_cds_test.go
index e373034e..fff78fe0 100644
--- a/xds/internal/xdsclient/xdsresource/tests/unmarshal_cds_test.go
+++ b/xds/internal/xdsclient/xdsresource/tests/unmarshal_cds_test.go
@@ -26,18 +26,20 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"google.golang.org/grpc/balancer/leastrequest"
-	_ "google.golang.org/grpc/balancer/roundrobin" // To register round_robin load balancer.
 	"google.golang.org/grpc/internal/balancer/stub"
 	"google.golang.org/grpc/internal/envconfig"
 	"google.golang.org/grpc/internal/grpctest"
 	iserviceconfig "google.golang.org/grpc/internal/serviceconfig"
 	"google.golang.org/grpc/internal/testutils"
 	"google.golang.org/grpc/internal/testutils/xds/e2e"
+	"google.golang.org/grpc/internal/xds/bootstrap"
 	"google.golang.org/grpc/serviceconfig"
-	_ "google.golang.org/grpc/xds" // Register the xDS LB Registry Converters.
 	"google.golang.org/grpc/xds/internal/balancer/ringhash"
 	"google.golang.org/grpc/xds/internal/balancer/wrrlocality"
 	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/anypb"
+	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
 
 	v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3"
@@ -48,9 +50,9 @@ import (
 	v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3"
 	v3roundrobinpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/round_robin/v3"
 	v3wrrlocalitypb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/wrr_locality/v3"
-	"google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/anypb"
-	"google.golang.org/protobuf/types/known/structpb"
+
+	_ "google.golang.org/grpc/balancer/roundrobin" // To register round_robin load balancer.
+	_ "google.golang.org/grpc/xds"                 // Register the xDS LB Registry Converters.
 )
 
 type s struct {
@@ -66,8 +68,6 @@ const (
 	serviceName = "service"
 )
 
-var emptyUpdate = xdsresource.ClusterUpdate{ClusterName: clusterName, LRSServerConfig: xdsresource.ClusterLRSOff}
-
 func wrrLocality(t *testing.T, m proto.Message) *v3wrrlocalitypb.WrrLocality {
 	return &v3wrrlocalitypb.WrrLocality{
 		EndpointPickingPolicy: &v3clusterpb.LoadBalancingPolicy{
@@ -105,6 +105,7 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 	tests := []struct {
 		name         string
 		cluster      *v3clusterpb.Cluster
+		serverCfg    *bootstrap.ServerConfig
 		wantUpdate   xdsresource.ClusterUpdate
 		wantLBConfig *iserviceconfig.BalancerConfig
 	}{
@@ -164,7 +165,8 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
 			},
 			wantUpdate: xdsresource.ClusterUpdate{
-				ClusterName: clusterName, LRSServerConfig: xdsresource.ClusterLRSOff, ClusterType: xdsresource.ClusterTypeAggregate,
+				ClusterName:             clusterName,
+				ClusterType:             xdsresource.ClusterTypeAggregate,
 				PrioritizedClusterNames: []string{"a", "b", "c"},
 			},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
@@ -179,7 +181,7 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 		{
 			name:       "happy-case-no-service-name-no-lrs",
 			cluster:    e2e.DefaultCluster(clusterName, "", e2e.SecurityLevelNone),
-			wantUpdate: emptyUpdate,
+			wantUpdate: xdsresource.ClusterUpdate{ClusterName: clusterName},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
 				Name: wrrlocality.Name,
 				Config: &wrrlocality.LBConfig{
@@ -206,16 +208,17 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 			},
 		},
 		{
-			name: "happiest-case",
+			name: "happiest-case-with-lrs",
 			cluster: e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
 				ClusterName: clusterName,
 				ServiceName: serviceName,
 				EnableLRS:   true,
 			}),
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 			wantUpdate: xdsresource.ClusterUpdate{
 				ClusterName:     clusterName,
 				EDSServiceName:  serviceName,
-				LRSServerConfig: xdsresource.ClusterLRSServerSelf,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 			},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
 				Name: wrrlocality.Name,
@@ -248,10 +251,11 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 				}
 				return c
 			}(),
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 			wantUpdate: xdsresource.ClusterUpdate{
 				ClusterName:     clusterName,
 				EDSServiceName:  serviceName,
-				LRSServerConfig: xdsresource.ClusterLRSServerSelf,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 				MaxRequests:     func() *uint32 { i := uint32(512); return &i }(),
 			},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
@@ -298,7 +302,8 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
 			},
 			wantUpdate: xdsresource.ClusterUpdate{
-				ClusterName: clusterName, EDSServiceName: serviceName,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 			},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
 				Name: "least_request_experimental",
@@ -353,7 +358,8 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 				},
 			},
 			wantUpdate: xdsresource.ClusterUpdate{
-				ClusterName: clusterName, EDSServiceName: serviceName,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 			},
 			wantLBConfig: &iserviceconfig.BalancerConfig{
 				Name: "least_request_experimental",
@@ -527,7 +533,7 @@ func (s) TestValidateCluster_Success(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			update, err := xdsresource.ValidateClusterAndConstructClusterUpdateForTesting(test.cluster)
+			update, err := xdsresource.ValidateClusterAndConstructClusterUpdateForTesting(test.cluster, test.serverCfg)
 			if err != nil {
 				t.Errorf("validateClusterAndConstructClusterUpdate(%+v) failed: %v", test.cluster, err)
 			}
diff --git a/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go b/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go
index a3454669..d008b251 100644
--- a/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go
+++ b/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"google.golang.org/grpc/internal/pretty"
 	"google.golang.org/grpc/internal/testutils"
+	"google.golang.org/grpc/internal/xds/bootstrap"
 	"google.golang.org/grpc/internal/xds/matcher"
 	"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
 
@@ -50,14 +51,11 @@ const (
 	serviceName = "service"
 )
 
-var emptyUpdate = ClusterUpdate{ClusterName: clusterName, LRSServerConfig: ClusterLRSOff}
-
 func (s) TestValidateCluster_Failure(t *testing.T) {
 	tests := []struct {
-		name       string
-		cluster    *v3clusterpb.Cluster
-		wantUpdate ClusterUpdate
-		wantErr    bool
+		name    string
+		cluster *v3clusterpb.Cluster
+		wantErr bool
 	}{
 		{
 			name: "non-supported-cluster-type-static",
@@ -72,8 +70,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				},
 				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "non-supported-cluster-type-original-dst",
@@ -88,8 +85,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				},
 				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "no-eds-config",
@@ -97,8 +93,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS},
 				LbPolicy:             v3clusterpb.Cluster_ROUND_ROBIN,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "no-ads-config-source",
@@ -107,8 +102,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				EdsClusterConfig:     &v3clusterpb.Cluster_EdsClusterConfig{},
 				LbPolicy:             v3clusterpb.Cluster_ROUND_ROBIN,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "non-round-robin-or-ring-hash-lb-policy",
@@ -123,8 +117,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				},
 				LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "logical-dns-multiple-localities",
@@ -140,8 +133,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "ring-hash-hash-function-not-xx-hash",
@@ -153,8 +145,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "least-request-choice-count-less-than-two",
@@ -166,8 +157,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "ring-hash-max-bound-greater-than-upper-bound",
@@ -179,8 +169,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "ring-hash-max-bound-greater-than-upper-bound-load-balancing-policy",
@@ -209,8 +198,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "least-request-unsupported-in-converter-since-env-var-unset",
@@ -235,8 +223,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 					},
 				},
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "aggregate-nil-clusters",
@@ -250,8 +237,7 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				},
 				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 		{
 			name: "aggregate-empty-clusters",
@@ -267,14 +253,13 @@ func (s) TestValidateCluster_Failure(t *testing.T) {
 				},
 				LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN,
 			},
-			wantUpdate: emptyUpdate,
-			wantErr:    true,
+			wantErr: true,
 		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			if update, err := validateClusterAndConstructClusterUpdate(test.cluster); err == nil {
+			if update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil); err == nil {
 				t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update)
 			}
 		})
@@ -882,9 +867,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName: rootPluginInstance,
 					RootCertName:     rootCertName,
@@ -924,9 +908,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName: rootPluginInstance,
 					RootCertName:     rootCertName,
@@ -968,9 +951,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName:     rootPluginInstance,
 					RootCertName:         rootCertName,
@@ -1016,9 +998,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName:     rootPluginInstance,
 					RootCertName:         rootCertName,
@@ -1076,9 +1057,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName:     rootPluginInstance,
 					RootCertName:         rootCertName,
@@ -1143,9 +1123,8 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 				},
 			},
 			wantUpdate: ClusterUpdate{
-				ClusterName:     clusterName,
-				EDSServiceName:  serviceName,
-				LRSServerConfig: ClusterLRSOff,
+				ClusterName:    clusterName,
+				EDSServiceName: serviceName,
 				SecurityCfg: &SecurityConfig{
 					RootInstanceName:     rootPluginInstance,
 					RootCertName:         rootCertName,
@@ -1165,7 +1144,7 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			update, err := validateClusterAndConstructClusterUpdate(test.cluster)
+			update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil)
 			if (err != nil) != test.wantErr {
 				t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr)
 			}
@@ -1287,6 +1266,7 @@ func (s) TestUnmarshalCluster(t *testing.T) {
 	tests := []struct {
 		name       string
 		resource   *anypb.Any
+		serverCfg  *bootstrap.ServerConfig
 		wantName   string
 		wantUpdate ClusterUpdate
 		wantErr    bool
@@ -1337,43 +1317,50 @@ func (s) TestUnmarshalCluster(t *testing.T) {
 			wantErr:  true,
 		},
 		{
-			name:     "v3 cluster",
-			resource: v3ClusterAny,
-			wantName: v3ClusterName,
+			name:      "v3 cluster",
+			resource:  v3ClusterAny,
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+			wantName:  v3ClusterName,
 			wantUpdate: ClusterUpdate{
-				ClusterName:    v3ClusterName,
-				EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf,
-				Raw: v3ClusterAny,
+				ClusterName:     v3ClusterName,
+				EDSServiceName:  v3Service,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+				Raw:             v3ClusterAny,
 			},
 		},
 		{
-			name:     "v3 cluster wrapped",
-			resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}),
-			wantName: v3ClusterName,
+			name:      "v3 cluster wrapped",
+			resource:  testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}),
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+			wantName:  v3ClusterName,
 			wantUpdate: ClusterUpdate{
-				ClusterName:    v3ClusterName,
-				EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf,
-				Raw: v3ClusterAny,
+				ClusterName:     v3ClusterName,
+				EDSServiceName:  v3Service,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+				Raw:             v3ClusterAny,
 			},
 		},
 		{
-			name:     "v3 cluster with EDS config source self",
-			resource: v3ClusterAnyWithEDSConfigSourceSelf,
-			wantName: v3ClusterName,
+			name:      "v3 cluster with EDS config source self",
+			resource:  v3ClusterAnyWithEDSConfigSourceSelf,
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+			wantName:  v3ClusterName,
 			wantUpdate: ClusterUpdate{
-				ClusterName:    v3ClusterName,
-				EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf,
-				Raw: v3ClusterAnyWithEDSConfigSourceSelf,
+				ClusterName:     v3ClusterName,
+				EDSServiceName:  v3Service,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+				Raw:             v3ClusterAnyWithEDSConfigSourceSelf,
 			},
 		},
 		{
-			name:     "v3 cluster with telemetry case",
-			resource: v3ClusterAnyWithTelemetryLabels,
-			wantName: v3ClusterName,
+			name:      "v3 cluster with telemetry case",
+			resource:  v3ClusterAnyWithTelemetryLabels,
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+			wantName:  v3ClusterName,
 			wantUpdate: ClusterUpdate{
 				ClusterName:     v3ClusterName,
 				EDSServiceName:  v3Service,
-				LRSServerConfig: ClusterLRSServerSelf,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 				Raw:             v3ClusterAnyWithTelemetryLabels,
 				TelemetryLabels: map[string]string{
 					"service_name":      "grpc-service",
@@ -1382,13 +1369,14 @@ func (s) TestUnmarshalCluster(t *testing.T) {
 			},
 		},
 		{
-			name:     "v3 metadata ignore other types not string and not com.google.csm.telemetry_labels",
-			resource: v3ClusterAnyWithTelemetryLabelsIgnoreSome,
-			wantName: v3ClusterName,
+			name:      "v3 metadata ignore other types not string and not com.google.csm.telemetry_labels",
+			resource:  v3ClusterAnyWithTelemetryLabelsIgnoreSome,
+			serverCfg: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
+			wantName:  v3ClusterName,
 			wantUpdate: ClusterUpdate{
 				ClusterName:     v3ClusterName,
 				EDSServiceName:  v3Service,
-				LRSServerConfig: ClusterLRSServerSelf,
+				LRSServerConfig: &bootstrap.ServerConfig{ServerURI: "test-server-uri"},
 				Raw:             v3ClusterAnyWithTelemetryLabelsIgnoreSome,
 				TelemetryLabels: map[string]string{
 					"service_name": "grpc-service",
@@ -1415,7 +1403,7 @@ func (s) TestUnmarshalCluster(t *testing.T) {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			name, update, err := unmarshalClusterResource(test.resource)
+			name, update, err := unmarshalClusterResource(test.resource, test.serverCfg)
 			if (err != nil) != test.wantErr {
 				t.Fatalf("unmarshalClusterResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr)
 			}
@@ -1584,7 +1572,7 @@ func (s) TestValidateClusterWithOutlierDetection(t *testing.T) {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			update, err := validateClusterAndConstructClusterUpdate(test.cluster)
+			update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil)
 			if (err != nil) != test.wantErr {
 				t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr)
 			}