Skip to content

Commit d00dd8f

Browse files
authored
xds: env var protection for xds fallback (#7483)
1 parent ffaa81e commit d00dd8f

File tree

3 files changed

+121
-5
lines changed

3 files changed

+121
-5
lines changed

internal/envconfig/envconfig.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ var (
4646
// by setting the environment variable "GRPC_ENFORCE_ALPN_ENABLED" to "true"
4747
// or "false".
4848
EnforceALPNEnabled = boolFromEnv("GRPC_ENFORCE_ALPN_ENABLED", false)
49+
// XDSFallbackSupport is the env variable that controls whether support for
50+
// xDS fallback is turned on. If this is unset or is false, only the first
51+
// xDS server in the list of server configs will be used.
52+
XDSFallbackSupport = boolFromEnv("GRPC_EXPERIMENTAL_XDS_FALLBACK", false)
4953
)
5054

5155
func boolFromEnv(envVar string, def bool) bool {

internal/xds/bootstrap/bootstrap.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,39 @@ func (cc ChannelCreds) String() string {
8484
return cc.Type + "-" + string(b)
8585
}
8686

87+
// ServerConfigs represents a collection of server configurations.
88+
type ServerConfigs []*ServerConfig
89+
90+
// Equal returns true if scs equals other.
91+
func (scs *ServerConfigs) Equal(other *ServerConfigs) bool {
92+
if len(*scs) != len(*other) {
93+
return false
94+
}
95+
for i := range *scs {
96+
if !(*scs)[i].Equal((*other)[i]) {
97+
return false
98+
}
99+
}
100+
return true
101+
}
102+
103+
// UnmarshalJSON takes the json data (a list of server configurations) and
104+
// unmarshals it to the struct.
105+
func (scs *ServerConfigs) UnmarshalJSON(data []byte) error {
106+
servers := []*ServerConfig{}
107+
if err := json.Unmarshal(data, &servers); err != nil {
108+
return fmt.Errorf("xds: failed to JSON unmarshal server configurations during bootstrap: %v, config:\n%s", err, string(data))
109+
}
110+
// Only use the first server config if fallback support is disabled.
111+
if !envconfig.XDSFallbackSupport {
112+
if len(servers) > 1 {
113+
servers = servers[:1]
114+
}
115+
}
116+
*scs = servers
117+
return nil
118+
}
119+
87120
// Authority contains configuration for an xDS control plane authority.
88121
//
89122
// This type does not implement custom JSON marshal/unmarshal logic because it
@@ -104,7 +137,7 @@ type Authority struct {
104137
// "xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s".
105138
ClientListenerResourceNameTemplate string `json:"client_listener_resource_name_template,omitempty"`
106139
// XDSServers contains the list of server configurations for this authority.
107-
XDSServers []*ServerConfig `json:"xds_servers,omitempty"`
140+
XDSServers ServerConfigs `json:"xds_servers,omitempty"`
108141
}
109142

110143
// Equal returns true if a equals other.
@@ -116,7 +149,7 @@ func (a *Authority) Equal(other *Authority) bool {
116149
return false
117150
case a.ClientListenerResourceNameTemplate != other.ClientListenerResourceNameTemplate:
118151
return false
119-
case !slices.EqualFunc(a.XDSServers, other.XDSServers, func(a, b *ServerConfig) bool { return a.Equal(b) }):
152+
case !a.XDSServers.Equal(&other.XDSServers):
120153
return false
121154
}
122155
return true
@@ -315,7 +348,7 @@ func ServerConfigForTesting(opts ServerConfigTestingOptions) (*ServerConfig, err
315348
// Config is the internal representation of the bootstrap configuration provided
316349
// to the xDS client.
317350
type Config struct {
318-
xDSServers []*ServerConfig
351+
xDSServers ServerConfigs
319352
cpcs map[string]certproviderNameAndConfig
320353
serverListenerResourceNameTemplate string
321354
clientDefaultListenerResourceNameTemplate string
@@ -405,7 +438,7 @@ func (c *Config) Equal(other *Config) bool {
405438
return true
406439
case (c != nil) != (other != nil):
407440
return false
408-
case !slices.EqualFunc(c.xDSServers, other.xDSServers, func(a, b *ServerConfig) bool { return a.Equal(b) }):
441+
case !c.xDSServers.Equal(&other.xDSServers):
409442
return false
410443
case !maps.EqualFunc(c.certProviderConfigs, other.certProviderConfigs, func(a, b *certprovider.BuildableConfig) bool { return a.String() == b.String() }):
411444
return false
@@ -429,7 +462,7 @@ func (c *Config) String() string {
429462

430463
// The following fields correspond 1:1 with the JSON schema for Config.
431464
type configJSON struct {
432-
XDSServers []*ServerConfig `json:"xds_servers,omitempty"`
465+
XDSServers ServerConfigs `json:"xds_servers,omitempty"`
433466
CertificateProviders map[string]certproviderNameAndConfig `json:"certificate_providers,omitempty"`
434467
ServerListenerResourceNameTemplate string `json:"server_listener_resource_name_template,omitempty"`
435468
ClientDefaultListenerResourceNameTemplate string `json:"client_default_listener_resource_name_template,omitempty"`

internal/xds/bootstrap/bootstrap_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ func (s) TestGetConfiguration_Success(t *testing.T) {
429429

430430
for _, test := range tests {
431431
t.Run(test.name, func(t *testing.T) {
432+
origFallbackEnv := envconfig.XDSFallbackSupport
433+
envconfig.XDSFallbackSupport = true
434+
defer func() { envconfig.XDSFallbackSupport = origFallbackEnv }()
435+
432436
testGetConfigurationWithFileNameEnv(t, test.name, false, test.wantConfig)
433437
testGetConfigurationWithFileContentEnv(t, test.name, false, test.wantConfig)
434438
})
@@ -1194,3 +1198,78 @@ func (s) TestNode_ToProto(t *testing.T) {
11941198
})
11951199
}
11961200
}
1201+
1202+
// Tests the case where the xDS fallback env var is set to false, and verifies
1203+
// that only the first server from the list of server configurations is used.
1204+
func (s) TestGetConfiguration_FallbackDisabled(t *testing.T) {
1205+
// TODO(easwars): Default value of "GRPC_EXPERIMENTAL_XDS_FALLBACK"
1206+
// env var is currently false. When the default is changed to true,
1207+
// explicitly set it to false here.
1208+
1209+
cancel := setupBootstrapOverride(map[string]string{
1210+
"multipleXDSServers": `
1211+
{
1212+
"node": {
1213+
"id": "ENVOY_NODE_ID",
1214+
"metadata": {
1215+
"TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector"
1216+
}
1217+
},
1218+
"xds_servers" : [
1219+
{
1220+
"server_uri": "trafficdirector.googleapis.com:443",
1221+
"channel_creds": [{ "type": "google_default" }],
1222+
"server_features": ["xds_v3"]
1223+
},
1224+
{
1225+
"server_uri": "backup.never.use.com:1234",
1226+
"channel_creds": [{ "type": "google_default" }]
1227+
}
1228+
],
1229+
"authorities": {
1230+
"xds.td.com": {
1231+
"xds_servers": [
1232+
{
1233+
"server_uri": "td.com",
1234+
"channel_creds": [ { "type": "google_default" } ],
1235+
"server_features" : ["xds_v3"]
1236+
},
1237+
{
1238+
"server_uri": "backup.never.use.com:1234",
1239+
"channel_creds": [{ "type": "google_default" }]
1240+
}
1241+
]
1242+
}
1243+
}
1244+
}`,
1245+
})
1246+
defer cancel()
1247+
1248+
wantConfig := &Config{
1249+
xDSServers: []*ServerConfig{{
1250+
serverURI: "trafficdirector.googleapis.com:443",
1251+
channelCreds: []ChannelCreds{{Type: "google_default"}},
1252+
serverFeatures: []string{"xds_v3"},
1253+
selectedCreds: ChannelCreds{Type: "google_default"},
1254+
}},
1255+
node: v3Node,
1256+
clientDefaultListenerResourceNameTemplate: "%s",
1257+
authorities: map[string]*Authority{
1258+
"xds.td.com": {
1259+
ClientListenerResourceNameTemplate: "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s",
1260+
XDSServers: []*ServerConfig{{
1261+
serverURI: "td.com",
1262+
channelCreds: []ChannelCreds{{Type: "google_default"}},
1263+
serverFeatures: []string{"xds_v3"},
1264+
selectedCreds: ChannelCreds{Type: "google_default"},
1265+
}},
1266+
},
1267+
},
1268+
}
1269+
t.Run("bootstrap_file_name", func(t *testing.T) {
1270+
testGetConfigurationWithFileNameEnv(t, "multipleXDSServers", false, wantConfig)
1271+
})
1272+
t.Run("bootstrap_file_contents", func(t *testing.T) {
1273+
testGetConfigurationWithFileContentEnv(t, "multipleXDSServers", false, wantConfig)
1274+
})
1275+
}

0 commit comments

Comments
 (0)