gitee.com/zhaochuninhefei/gmgo@v0.0.31-0.20240209061119-069254a02979/grpc/xds/internal/xdsclient/bootstrap/bootstrap_test.go (about) 1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package bootstrap 20 21 import ( 22 "encoding/json" 23 "errors" 24 "fmt" 25 "os" 26 "testing" 27 28 "github.com/golang/protobuf/proto" 29 "github.com/google/go-cmp/cmp" 30 "github.com/google/go-cmp/cmp/cmpopts" 31 32 grpc "gitee.com/zhaochuninhefei/gmgo/grpc" 33 "gitee.com/zhaochuninhefei/gmgo/grpc/credentials/google" 34 "gitee.com/zhaochuninhefei/gmgo/grpc/credentials/insecure" 35 "gitee.com/zhaochuninhefei/gmgo/grpc/credentials/tls/certprovider" 36 "gitee.com/zhaochuninhefei/gmgo/grpc/internal" 37 "gitee.com/zhaochuninhefei/gmgo/grpc/internal/envconfig" 38 "gitee.com/zhaochuninhefei/gmgo/grpc/xds/internal/xdsclient/xdsresource/version" 39 40 v2corepb "gitee.com/zhaochuninhefei/gmgo/go-control-plane/envoy/api/v2/core" 41 v3corepb "gitee.com/zhaochuninhefei/gmgo/go-control-plane/envoy/config/core/v3" 42 structpb "github.com/golang/protobuf/ptypes/struct" 43 ) 44 45 var ( 46 v2BootstrapFileMap = map[string]string{ 47 "emptyNodeProto": ` 48 { 49 "xds_servers" : [{ 50 "server_uri": "trafficdirector.googleapis.com:443", 51 "channel_creds": [ 52 { "type": "insecure" } 53 ] 54 }] 55 }`, 56 "unknownTopLevelFieldInFile": ` 57 { 58 "node": { 59 "id": "ENVOY_NODE_ID", 60 "metadata": { 61 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 62 } 63 }, 64 "xds_servers" : [{ 65 "server_uri": "trafficdirector.googleapis.com:443", 66 "channel_creds": [ 67 { "type": "insecure" } 68 ] 69 }], 70 "unknownField": "foobar" 71 }`, 72 "unknownFieldInNodeProto": ` 73 { 74 "node": { 75 "id": "ENVOY_NODE_ID", 76 "unknownField": "foobar", 77 "metadata": { 78 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 79 } 80 }, 81 "xds_servers" : [{ 82 "server_uri": "trafficdirector.googleapis.com:443", 83 "channel_creds": [ 84 { "type": "insecure" } 85 ] 86 }] 87 }`, 88 "unknownFieldInXdsServer": ` 89 { 90 "node": { 91 "id": "ENVOY_NODE_ID", 92 "metadata": { 93 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 94 } 95 }, 96 "xds_servers" : [{ 97 "server_uri": "trafficdirector.googleapis.com:443", 98 "channel_creds": [ 99 { "type": "insecure" } 100 ], 101 "unknownField": "foobar" 102 }] 103 }`, 104 "multipleChannelCreds": ` 105 { 106 "node": { 107 "id": "ENVOY_NODE_ID", 108 "metadata": { 109 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 110 } 111 }, 112 "xds_servers" : [{ 113 "server_uri": "trafficdirector.googleapis.com:443", 114 "channel_creds": [ 115 { "type": "not-google-default" }, 116 { "type": "google_default" } 117 ] 118 }] 119 }`, 120 "goodBootstrap": ` 121 { 122 "node": { 123 "id": "ENVOY_NODE_ID", 124 "metadata": { 125 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 126 } 127 }, 128 "xds_servers" : [{ 129 "server_uri": "trafficdirector.googleapis.com:443", 130 "channel_creds": [ 131 { "type": "google_default" } 132 ] 133 }] 134 }`, 135 "multipleXDSServers": ` 136 { 137 "node": { 138 "id": "ENVOY_NODE_ID", 139 "metadata": { 140 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 141 } 142 }, 143 "xds_servers" : [ 144 { 145 "server_uri": "trafficdirector.googleapis.com:443", 146 "channel_creds": [{ "type": "google_default" }] 147 }, 148 { 149 "server_uri": "backup.never.use.com:1234", 150 "channel_creds": [{ "type": "not-google-default" }] 151 } 152 ] 153 }`, 154 } 155 v3BootstrapFileMap = map[string]string{ 156 "serverDoesNotSupportsV3": ` 157 { 158 "node": { 159 "id": "ENVOY_NODE_ID", 160 "metadata": { 161 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 162 } 163 }, 164 "xds_servers" : [{ 165 "server_uri": "trafficdirector.googleapis.com:443", 166 "channel_creds": [ 167 { "type": "google_default" } 168 ], 169 "server_features" : ["foo", "bar"] 170 }] 171 }`, 172 "serverSupportsV3": ` 173 { 174 "node": { 175 "id": "ENVOY_NODE_ID", 176 "metadata": { 177 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 178 } 179 }, 180 "xds_servers" : [{ 181 "server_uri": "trafficdirector.googleapis.com:443", 182 "channel_creds": [ 183 { "type": "google_default" } 184 ], 185 "server_features" : ["foo", "bar", "xds_v3"] 186 }] 187 }`, 188 } 189 metadata = &structpb.Struct{ 190 Fields: map[string]*structpb.Value{ 191 "TRAFFICDIRECTOR_GRPC_HOSTNAME": { 192 Kind: &structpb.Value_StringValue{StringValue: "trafficdirector"}, 193 }, 194 }, 195 } 196 v2NodeProto = &v2corepb.Node{ 197 Id: "ENVOY_NODE_ID", 198 Metadata: metadata, 199 BuildVersion: gRPCVersion, 200 UserAgentName: gRPCUserAgentName, 201 UserAgentVersionType: &v2corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, 202 ClientFeatures: []string{clientFeatureNoOverprovisioning}, 203 } 204 v3NodeProto = &v3corepb.Node{ 205 Id: "ENVOY_NODE_ID", 206 Metadata: metadata, 207 UserAgentName: gRPCUserAgentName, 208 UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, 209 ClientFeatures: []string{clientFeatureNoOverprovisioning}, 210 } 211 nilCredsConfigV2 = &Config{ 212 XDSServer: &ServerConfig{ 213 ServerURI: "trafficdirector.googleapis.com:443", 214 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 215 CredsType: "insecure", 216 NodeProto: v2NodeProto, 217 }, 218 ClientDefaultListenerResourceNameTemplate: "%s", 219 } 220 nonNilCredsConfigV2 = &Config{ 221 XDSServer: &ServerConfig{ 222 ServerURI: "trafficdirector.googleapis.com:443", 223 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 224 CredsType: "google_default", 225 NodeProto: v2NodeProto, 226 }, 227 ClientDefaultListenerResourceNameTemplate: "%s", 228 } 229 nonNilCredsConfigV3 = &Config{ 230 XDSServer: &ServerConfig{ 231 ServerURI: "trafficdirector.googleapis.com:443", 232 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 233 CredsType: "google_default", 234 TransportAPI: version.TransportV3, 235 NodeProto: v3NodeProto, 236 }, 237 ClientDefaultListenerResourceNameTemplate: "%s", 238 } 239 ) 240 241 func (c *Config) compare(want *Config) error { 242 if diff := cmp.Diff(c, want, 243 cmpopts.EquateEmpty(), 244 cmp.AllowUnexported(ServerConfig{}), 245 cmp.Comparer(proto.Equal), 246 cmp.Comparer(func(a, b grpc.DialOption) bool { return (a != nil) == (b != nil) }), 247 cmp.Transformer("certproviderconfigstring", func(a *certprovider.BuildableConfig) string { return a.String() }), 248 ); diff != "" { 249 return fmt.Errorf("diff: %v", diff) 250 } 251 return nil 252 } 253 254 func fileReadFromFileMap(bootstrapFileMap map[string]string, name string) ([]byte, error) { 255 if b, ok := bootstrapFileMap[name]; ok { 256 return []byte(b), nil 257 } 258 return nil, os.ErrNotExist 259 } 260 261 func setupBootstrapOverride(bootstrapFileMap map[string]string) func() { 262 oldFileReadFunc := bootstrapFileReadFunc 263 bootstrapFileReadFunc = func(filename string) ([]byte, error) { 264 return fileReadFromFileMap(bootstrapFileMap, filename) 265 } 266 return func() { bootstrapFileReadFunc = oldFileReadFunc } 267 } 268 269 // TODO: enable leak check for this package when 270 // https://github.com/googleapis/google-cloud-go/issues/2417 is fixed. 271 272 // This function overrides the bootstrap file NAME env variable, to test the 273 // code that reads file with the given fileName. 274 func testNewConfigWithFileNameEnv(t *testing.T, fileName string, wantError bool, wantConfig *Config) { 275 t.Helper() 276 origBootstrapFileName := envconfig.XDSBootstrapFileName 277 envconfig.XDSBootstrapFileName = fileName 278 defer func() { envconfig.XDSBootstrapFileName = origBootstrapFileName }() 279 280 c, err := NewConfig() 281 if (err != nil) != wantError { 282 t.Fatalf("NewConfig() returned error %v, wantError: %v", err, wantError) 283 } 284 if wantError { 285 return 286 } 287 if err := c.compare(wantConfig); err != nil { 288 t.Fatal(err) 289 } 290 } 291 292 // This function overrides the bootstrap file CONTENT env variable, to test the 293 // code that uses the content from env directly. 294 func testNewConfigWithFileContentEnv(t *testing.T, fileName string, wantError bool, wantConfig *Config) { 295 t.Helper() 296 b, err := bootstrapFileReadFunc(fileName) 297 if err != nil { 298 t.Skip(err) 299 } 300 origBootstrapContent := envconfig.XDSBootstrapFileContent 301 envconfig.XDSBootstrapFileContent = string(b) 302 defer func() { envconfig.XDSBootstrapFileContent = origBootstrapContent }() 303 304 c, err := NewConfig() 305 if (err != nil) != wantError { 306 t.Fatalf("NewConfig() returned error %v, wantError: %v", err, wantError) 307 } 308 if wantError { 309 return 310 } 311 if err := c.compare(wantConfig); err != nil { 312 t.Fatal(err) 313 } 314 } 315 316 // TestNewConfigV2ProtoFailure exercises the functionality in NewConfig with 317 // different bootstrap file contents which are expected to fail. 318 func TestNewConfigV2ProtoFailure(t *testing.T) { 319 bootstrapFileMap := map[string]string{ 320 "empty": "", 321 "badJSON": `["test": 123]`, 322 "noBalancerName": `{"node": {"id": "ENVOY_NODE_ID"}}`, 323 "emptyXdsServer": ` 324 { 325 "node": { 326 "id": "ENVOY_NODE_ID", 327 "metadata": { 328 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 329 } 330 } 331 }`, 332 "emptyChannelCreds": ` 333 { 334 "node": { 335 "id": "ENVOY_NODE_ID", 336 "metadata": { 337 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 338 } 339 }, 340 "xds_servers" : [{ 341 "server_uri": "trafficdirector.googleapis.com:443" 342 }] 343 }`, 344 "nonGoogleDefaultCreds": ` 345 { 346 "node": { 347 "id": "ENVOY_NODE_ID", 348 "metadata": { 349 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 350 } 351 }, 352 "xds_servers" : [{ 353 "server_uri": "trafficdirector.googleapis.com:443", 354 "channel_creds": [ 355 { "type": "not-google-default" } 356 ] 357 }] 358 }`, 359 } 360 cancel := setupBootstrapOverride(bootstrapFileMap) 361 defer cancel() 362 363 tests := []struct { 364 name string 365 wantError bool 366 }{ 367 {"nonExistentBootstrapFile", true}, 368 {"empty", true}, 369 {"badJSON", true}, 370 {"noBalancerName", true}, 371 {"emptyXdsServer", true}, 372 } 373 374 for _, test := range tests { 375 t.Run(test.name, func(t *testing.T) { 376 testNewConfigWithFileNameEnv(t, test.name, true, nil) 377 testNewConfigWithFileContentEnv(t, test.name, true, nil) 378 }) 379 } 380 } 381 382 // TestNewConfigV2ProtoSuccess exercises the functionality in NewConfig with 383 // different bootstrap file contents. It overrides the fileReadFunc by returning 384 // bootstrap file contents defined in this test, instead of reading from a file. 385 func TestNewConfigV2ProtoSuccess(t *testing.T) { 386 cancel := setupBootstrapOverride(v2BootstrapFileMap) 387 defer cancel() 388 389 tests := []struct { 390 name string 391 wantConfig *Config 392 }{ 393 { 394 "emptyNodeProto", &Config{ 395 XDSServer: &ServerConfig{ 396 ServerURI: "trafficdirector.googleapis.com:443", 397 Creds: grpc.WithTransportCredentials(insecure.NewCredentials()), 398 CredsType: "insecure", 399 NodeProto: &v2corepb.Node{ 400 BuildVersion: gRPCVersion, 401 UserAgentName: gRPCUserAgentName, 402 UserAgentVersionType: &v2corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version}, 403 ClientFeatures: []string{clientFeatureNoOverprovisioning}, 404 }, 405 }, 406 ClientDefaultListenerResourceNameTemplate: "%s", 407 }, 408 }, 409 {"unknownTopLevelFieldInFile", nilCredsConfigV2}, 410 {"unknownFieldInNodeProto", nilCredsConfigV2}, 411 {"unknownFieldInXdsServer", nilCredsConfigV2}, 412 {"multipleChannelCreds", nonNilCredsConfigV2}, 413 {"goodBootstrap", nonNilCredsConfigV2}, 414 {"multipleXDSServers", nonNilCredsConfigV2}, 415 } 416 417 for _, test := range tests { 418 t.Run(test.name, func(t *testing.T) { 419 testNewConfigWithFileNameEnv(t, test.name, false, test.wantConfig) 420 testNewConfigWithFileContentEnv(t, test.name, false, test.wantConfig) 421 }) 422 } 423 } 424 425 // TestNewConfigV3Support verifies bootstrap functionality involving support for 426 // the xDS v3 transport protocol. Here the client ends up using v2 or v3 based 427 // on what the server supports. 428 func TestNewConfigV3Support(t *testing.T) { 429 cancel := setupBootstrapOverride(v3BootstrapFileMap) 430 defer cancel() 431 432 tests := []struct { 433 name string 434 wantConfig *Config 435 }{ 436 {"serverDoesNotSupportsV3", nonNilCredsConfigV2}, 437 {"serverSupportsV3", nonNilCredsConfigV3}, 438 } 439 440 for _, test := range tests { 441 t.Run(test.name, func(t *testing.T) { 442 testNewConfigWithFileNameEnv(t, test.name, false, test.wantConfig) 443 testNewConfigWithFileContentEnv(t, test.name, false, test.wantConfig) 444 }) 445 } 446 } 447 448 // TestNewConfigBootstrapEnvPriority tests that the two env variables are read 449 // in correct priority. 450 // 451 // the case where the bootstrap file 452 // environment variable is not set. 453 func TestNewConfigBootstrapEnvPriority(t *testing.T) { 454 oldFileReadFunc := bootstrapFileReadFunc 455 bootstrapFileReadFunc = func(filename string) ([]byte, error) { 456 return fileReadFromFileMap(v2BootstrapFileMap, filename) 457 } 458 defer func() { bootstrapFileReadFunc = oldFileReadFunc }() 459 460 goodFileName1 := "goodBootstrap" 461 goodConfig1 := nonNilCredsConfigV2 462 463 goodFileName2 := "serverSupportsV3" 464 goodFileContent2 := v3BootstrapFileMap[goodFileName2] 465 goodConfig2 := nonNilCredsConfigV3 466 467 origBootstrapFileName := envconfig.XDSBootstrapFileName 468 envconfig.XDSBootstrapFileName = "" 469 defer func() { envconfig.XDSBootstrapFileName = origBootstrapFileName }() 470 471 origBootstrapContent := envconfig.XDSBootstrapFileContent 472 envconfig.XDSBootstrapFileContent = "" 473 defer func() { envconfig.XDSBootstrapFileContent = origBootstrapContent }() 474 475 // When both env variables are empty, NewConfig should fail. 476 if _, err := NewConfig(); err == nil { 477 t.Errorf("NewConfig() returned nil error, expected to fail") 478 } 479 480 // When one of them is set, it should be used. 481 envconfig.XDSBootstrapFileName = goodFileName1 482 envconfig.XDSBootstrapFileContent = "" 483 if c, err := NewConfig(); err != nil || c.compare(goodConfig1) != nil { 484 t.Errorf("NewConfig() = %v, %v, want: %v, %v", c, err, goodConfig1, nil) 485 } 486 487 envconfig.XDSBootstrapFileName = "" 488 envconfig.XDSBootstrapFileContent = goodFileContent2 489 if c, err := NewConfig(); err != nil || c.compare(goodConfig2) != nil { 490 t.Errorf("NewConfig() = %v, %v, want: %v, %v", c, err, goodConfig1, nil) 491 } 492 493 // Set both, file name should be read. 494 envconfig.XDSBootstrapFileName = goodFileName1 495 envconfig.XDSBootstrapFileContent = goodFileContent2 496 if c, err := NewConfig(); err != nil || c.compare(goodConfig1) != nil { 497 t.Errorf("NewConfig() = %v, %v, want: %v, %v", c, err, goodConfig1, nil) 498 } 499 } 500 501 func init() { 502 certprovider.Register(&fakeCertProviderBuilder{}) 503 } 504 505 const fakeCertProviderName = "fake-certificate-provider" 506 507 // fakeCertProviderBuilder builds new instances of fakeCertProvider and 508 // interprets the config provided to it as JSON with a single key and value. 509 type fakeCertProviderBuilder struct{} 510 511 // ParseConfig expects input in JSON format containing a map from string to 512 // string, with a single entry and mapKey being "configKey". 513 func (b *fakeCertProviderBuilder) ParseConfig(cfg interface{}) (*certprovider.BuildableConfig, error) { 514 config, ok := cfg.(json.RawMessage) 515 if !ok { 516 return nil, fmt.Errorf("fakeCertProviderBuilder received config of type %T, want []byte", config) 517 } 518 var cfgData map[string]string 519 if err := json.Unmarshal(config, &cfgData); err != nil { 520 return nil, fmt.Errorf("fakeCertProviderBuilder config parsing failed: %v", err) 521 } 522 if len(cfgData) != 1 || cfgData["configKey"] == "" { 523 return nil, errors.New("fakeCertProviderBuilder received invalid config") 524 } 525 fc := &fakeStableConfig{config: cfgData} 526 return certprovider.NewBuildableConfig(fakeCertProviderName, fc.canonical(), func(certprovider.BuildOptions) certprovider.Provider { 527 return &fakeCertProvider{} 528 }), nil 529 } 530 531 func (b *fakeCertProviderBuilder) Name() string { 532 return fakeCertProviderName 533 } 534 535 type fakeStableConfig struct { 536 config map[string]string 537 } 538 539 func (c *fakeStableConfig) canonical() []byte { 540 var cfg string 541 for k, v := range c.config { 542 cfg = fmt.Sprintf("%s:%s", k, v) 543 } 544 return []byte(cfg) 545 } 546 547 // fakeCertProvider is an empty implementation of the Provider interface. 548 type fakeCertProvider struct { 549 certprovider.Provider 550 } 551 552 func TestNewConfigWithCertificateProviders(t *testing.T) { 553 bootstrapFileMap := map[string]string{ 554 "badJSONCertProviderConfig": ` 555 { 556 "node": { 557 "id": "ENVOY_NODE_ID", 558 "metadata": { 559 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 560 } 561 }, 562 "xds_servers" : [{ 563 "server_uri": "trafficdirector.googleapis.com:443", 564 "channel_creds": [ 565 { "type": "google_default" } 566 ], 567 "server_features" : ["foo", "bar", "xds_v3"], 568 }], 569 "certificate_providers": "bad JSON" 570 }`, 571 "allUnknownCertProviders": ` 572 { 573 "node": { 574 "id": "ENVOY_NODE_ID", 575 "metadata": { 576 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 577 } 578 }, 579 "xds_servers" : [{ 580 "server_uri": "trafficdirector.googleapis.com:443", 581 "channel_creds": [ 582 { "type": "google_default" } 583 ], 584 "server_features" : ["foo", "bar", "xds_v3"] 585 }], 586 "certificate_providers": { 587 "unknownProviderInstance1": { 588 "plugin_name": "foo", 589 "config": {"foo": "bar"} 590 }, 591 "unknownProviderInstance2": { 592 "plugin_name": "bar", 593 "config": {"foo": "bar"} 594 } 595 } 596 }`, 597 "badCertProviderConfig": ` 598 { 599 "node": { 600 "id": "ENVOY_NODE_ID", 601 "metadata": { 602 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 603 } 604 }, 605 "xds_servers" : [{ 606 "server_uri": "trafficdirector.googleapis.com:443", 607 "channel_creds": [ 608 { "type": "google_default" } 609 ], 610 "server_features" : ["foo", "bar", "xds_v3"], 611 }], 612 "certificate_providers": { 613 "unknownProviderInstance": { 614 "plugin_name": "foo", 615 "config": {"foo": "bar"} 616 }, 617 "fakeProviderInstanceBad": { 618 "plugin_name": "fake-certificate-provider", 619 "config": {"configKey": 666} 620 } 621 } 622 }`, 623 "goodCertProviderConfig": ` 624 { 625 "node": { 626 "id": "ENVOY_NODE_ID", 627 "metadata": { 628 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 629 } 630 }, 631 "xds_servers" : [{ 632 "server_uri": "trafficdirector.googleapis.com:443", 633 "channel_creds": [ 634 { "type": "google_default" } 635 ], 636 "server_features" : ["foo", "bar", "xds_v3"] 637 }], 638 "certificate_providers": { 639 "unknownProviderInstance": { 640 "plugin_name": "foo", 641 "config": {"foo": "bar"} 642 }, 643 "fakeProviderInstance": { 644 "plugin_name": "fake-certificate-provider", 645 "config": {"configKey": "configValue"} 646 } 647 } 648 }`, 649 } 650 651 getBuilder := internal.GetCertificateProviderBuilder.(func(string) certprovider.Builder) 652 parser := getBuilder(fakeCertProviderName) 653 if parser == nil { 654 t.Fatalf("missing certprovider plugin %q", fakeCertProviderName) 655 } 656 wantCfg, err := parser.ParseConfig(json.RawMessage(`{"configKey": "configValue"}`)) 657 if err != nil { 658 t.Fatalf("config parsing for plugin %q failed: %v", fakeCertProviderName, err) 659 } 660 661 cancel := setupBootstrapOverride(bootstrapFileMap) 662 defer cancel() 663 664 goodConfig := &Config{ 665 XDSServer: &ServerConfig{ 666 ServerURI: "trafficdirector.googleapis.com:443", 667 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 668 CredsType: "google_default", 669 TransportAPI: version.TransportV3, 670 NodeProto: v3NodeProto, 671 }, 672 CertProviderConfigs: map[string]*certprovider.BuildableConfig{ 673 "fakeProviderInstance": wantCfg, 674 }, 675 ClientDefaultListenerResourceNameTemplate: "%s", 676 } 677 tests := []struct { 678 name string 679 wantConfig *Config 680 wantErr bool 681 }{ 682 { 683 name: "badJSONCertProviderConfig", 684 wantErr: true, 685 }, 686 { 687 688 name: "badCertProviderConfig", 689 wantErr: true, 690 }, 691 { 692 693 name: "allUnknownCertProviders", 694 wantConfig: nonNilCredsConfigV3, 695 }, 696 { 697 name: "goodCertProviderConfig", 698 wantConfig: goodConfig, 699 }, 700 } 701 702 for _, test := range tests { 703 t.Run(test.name, func(t *testing.T) { 704 testNewConfigWithFileNameEnv(t, test.name, test.wantErr, test.wantConfig) 705 testNewConfigWithFileContentEnv(t, test.name, test.wantErr, test.wantConfig) 706 }) 707 } 708 } 709 710 func TestNewConfigWithServerListenerResourceNameTemplate(t *testing.T) { 711 cancel := setupBootstrapOverride(map[string]string{ 712 "badServerListenerResourceNameTemplate:": ` 713 { 714 "node": { 715 "id": "ENVOY_NODE_ID", 716 "metadata": { 717 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 718 } 719 }, 720 "xds_servers" : [{ 721 "server_uri": "trafficdirector.googleapis.com:443", 722 "channel_creds": [ 723 { "type": "google_default" } 724 ] 725 }], 726 "server_listener_resource_name_template": 123456789 727 }`, 728 "goodServerListenerResourceNameTemplate": ` 729 { 730 "node": { 731 "id": "ENVOY_NODE_ID", 732 "metadata": { 733 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 734 } 735 }, 736 "xds_servers" : [{ 737 "server_uri": "trafficdirector.googleapis.com:443", 738 "channel_creds": [ 739 { "type": "google_default" } 740 ] 741 }], 742 "server_listener_resource_name_template": "grpc/server?xds.resource.listening_address=%s" 743 }`, 744 }) 745 defer cancel() 746 747 tests := []struct { 748 name string 749 wantConfig *Config 750 wantErr bool 751 }{ 752 { 753 name: "badServerListenerResourceNameTemplate", 754 wantErr: true, 755 }, 756 { 757 name: "goodServerListenerResourceNameTemplate", 758 wantConfig: &Config{ 759 XDSServer: &ServerConfig{ 760 ServerURI: "trafficdirector.googleapis.com:443", 761 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 762 CredsType: "google_default", 763 TransportAPI: version.TransportV2, 764 NodeProto: v2NodeProto, 765 }, 766 ServerListenerResourceNameTemplate: "grpc/server?xds.resource.listening_address=%s", 767 ClientDefaultListenerResourceNameTemplate: "%s", 768 }, 769 }, 770 } 771 772 for _, test := range tests { 773 t.Run(test.name, func(t *testing.T) { 774 testNewConfigWithFileNameEnv(t, test.name, test.wantErr, test.wantConfig) 775 testNewConfigWithFileContentEnv(t, test.name, test.wantErr, test.wantConfig) 776 }) 777 } 778 } 779 780 func TestNewConfigWithFederation(t *testing.T) { 781 cancel := setupBootstrapOverride(map[string]string{ 782 "badClientListenerResourceNameTemplate": ` 783 { 784 "node": { "id": "ENVOY_NODE_ID" }, 785 "xds_servers" : [{ 786 "server_uri": "trafficdirector.googleapis.com:443" 787 }], 788 "client_default_listener_resource_name_template": 123456789 789 }`, 790 "badClientListenerResourceNameTemplatePerAuthority": ` 791 { 792 "node": { "id": "ENVOY_NODE_ID" }, 793 "xds_servers" : [{ 794 "server_uri": "trafficdirector.googleapis.com:443", 795 "channel_creds": [ { "type": "google_default" } ] 796 }], 797 "authorities": { 798 "xds.td.com": { 799 "client_listener_resource_name_template": "some/template/%s", 800 "xds_servers": [{ 801 "server_uri": "td.com", 802 "channel_creds": [ { "type": "google_default" } ], 803 "server_features" : ["foo", "bar", "xds_v3"] 804 }] 805 } 806 } 807 }`, 808 "good": ` 809 { 810 "node": { 811 "id": "ENVOY_NODE_ID", 812 "metadata": { 813 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 814 } 815 }, 816 "xds_servers" : [{ 817 "server_uri": "trafficdirector.googleapis.com:443", 818 "channel_creds": [ { "type": "google_default" } ] 819 }], 820 "server_listener_resource_name_template": "xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server?listening_address=%s", 821 "client_default_listener_resource_name_template": "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 822 "authorities": { 823 "xds.td.com": { 824 "client_listener_resource_name_template": "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s", 825 "xds_servers": [{ 826 "server_uri": "td.com", 827 "channel_creds": [ { "type": "google_default" } ], 828 "server_features" : ["foo", "bar", "xds_v3"] 829 }] 830 } 831 } 832 }`, 833 // If client_default_listener_resource_name_template is not set, it 834 // defaults to "%s". 835 "goodWithDefaultDefaultClientListenerTemplate": ` 836 { 837 "node": { 838 "id": "ENVOY_NODE_ID", 839 "metadata": { 840 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 841 } 842 }, 843 "xds_servers" : [{ 844 "server_uri": "trafficdirector.googleapis.com:443", 845 "channel_creds": [ { "type": "google_default" } ] 846 }] 847 }`, 848 // If client_listener_resource_name_template in authority is not set, it 849 // defaults to 850 // "xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s". 851 "goodWithDefaultClientListenerTemplatePerAuthority": ` 852 { 853 "node": { 854 "id": "ENVOY_NODE_ID", 855 "metadata": { 856 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 857 } 858 }, 859 "xds_servers" : [{ 860 "server_uri": "trafficdirector.googleapis.com:443", 861 "channel_creds": [ { "type": "google_default" } ] 862 }], 863 "client_default_listener_resource_name_template": "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 864 "authorities": { 865 "xds.td.com": { } 866 } 867 }`, 868 // It's OK for an authority to not have servers. The top-level server 869 // will be used. 870 "goodWithNoServerPerAuthority": ` 871 { 872 "node": { 873 "id": "ENVOY_NODE_ID", 874 "metadata": { 875 "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector" 876 } 877 }, 878 "xds_servers" : [{ 879 "server_uri": "trafficdirector.googleapis.com:443", 880 "channel_creds": [ { "type": "google_default" } ] 881 }], 882 "client_default_listener_resource_name_template": "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 883 "authorities": { 884 "xds.td.com": { 885 "client_listener_resource_name_template": "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s" 886 } 887 } 888 }`, 889 }) 890 defer cancel() 891 892 tests := []struct { 893 name string 894 wantConfig *Config 895 wantErr bool 896 }{ 897 { 898 name: "badClientListenerResourceNameTemplate", 899 wantErr: true, 900 }, 901 { 902 name: "badClientListenerResourceNameTemplatePerAuthority", 903 wantErr: true, 904 }, 905 { 906 name: "good", 907 wantConfig: &Config{ 908 XDSServer: &ServerConfig{ 909 ServerURI: "trafficdirector.googleapis.com:443", 910 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 911 CredsType: "google_default", 912 TransportAPI: version.TransportV2, 913 NodeProto: v2NodeProto, 914 }, 915 ServerListenerResourceNameTemplate: "xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server?listening_address=%s", 916 ClientDefaultListenerResourceNameTemplate: "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 917 Authorities: map[string]*Authority{ 918 "xds.td.com": { 919 ClientListenerResourceNameTemplate: "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s", 920 XDSServer: &ServerConfig{ 921 ServerURI: "td.com", 922 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 923 CredsType: "google_default", 924 TransportAPI: version.TransportV3, 925 NodeProto: v3NodeProto, 926 }, 927 }, 928 }, 929 }, 930 }, 931 { 932 name: "goodWithDefaultDefaultClientListenerTemplate", 933 wantConfig: &Config{ 934 XDSServer: &ServerConfig{ 935 ServerURI: "trafficdirector.googleapis.com:443", 936 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 937 CredsType: "google_default", 938 TransportAPI: version.TransportV2, 939 NodeProto: v2NodeProto, 940 }, 941 ClientDefaultListenerResourceNameTemplate: "%s", 942 }, 943 }, 944 { 945 name: "goodWithDefaultClientListenerTemplatePerAuthority", 946 wantConfig: &Config{ 947 XDSServer: &ServerConfig{ 948 ServerURI: "trafficdirector.googleapis.com:443", 949 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 950 CredsType: "google_default", 951 TransportAPI: version.TransportV2, 952 NodeProto: v2NodeProto, 953 }, 954 ClientDefaultListenerResourceNameTemplate: "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 955 Authorities: map[string]*Authority{ 956 "xds.td.com": { 957 ClientListenerResourceNameTemplate: "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s", 958 }, 959 }, 960 }, 961 }, 962 { 963 name: "goodWithNoServerPerAuthority", 964 wantConfig: &Config{ 965 XDSServer: &ServerConfig{ 966 ServerURI: "trafficdirector.googleapis.com:443", 967 Creds: grpc.WithCredentialsBundle(google.NewComputeEngineCredentials()), 968 CredsType: "google_default", 969 TransportAPI: version.TransportV2, 970 NodeProto: v2NodeProto, 971 }, 972 ClientDefaultListenerResourceNameTemplate: "xdstp://xds.example.com/envoy.config.listener.v3.Listener/%s", 973 Authorities: map[string]*Authority{ 974 "xds.td.com": { 975 ClientListenerResourceNameTemplate: "xdstp://xds.td.com/envoy.config.listener.v3.Listener/%s", 976 }, 977 }, 978 }, 979 }, 980 } 981 982 oldFederationSupport := envconfig.XDSFederation 983 envconfig.XDSFederation = true 984 defer func() { envconfig.XDSFederation = oldFederationSupport }() 985 986 for _, test := range tests { 987 t.Run(test.name, func(t *testing.T) { 988 testNewConfigWithFileNameEnv(t, test.name, test.wantErr, test.wantConfig) 989 testNewConfigWithFileContentEnv(t, test.name, test.wantErr, test.wantConfig) 990 }) 991 } 992 }