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