1// +build go1.12 2 3/* 4 * 5 * Copyright 2020 gRPC authors. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21package client 22 23import ( 24 "regexp" 25 "testing" 26 27 v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 28 v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" 29 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 30 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 31 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 32 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 33 anypb "github.com/golang/protobuf/ptypes/any" 34 "github.com/google/go-cmp/cmp" 35 "github.com/google/go-cmp/cmp/cmpopts" 36 "google.golang.org/grpc/internal/testutils" 37 xdsinternal "google.golang.org/grpc/internal/xds" 38 "google.golang.org/grpc/internal/xds/env" 39 "google.golang.org/grpc/xds/internal/version" 40 "google.golang.org/protobuf/types/known/wrapperspb" 41) 42 43const ( 44 clusterName = "clusterName" 45 serviceName = "service" 46) 47 48var emptyUpdate = ClusterUpdate{ServiceName: "", EnableLRS: false} 49 50func (s) TestValidateCluster_Failure(t *testing.T) { 51 tests := []struct { 52 name string 53 cluster *v3clusterpb.Cluster 54 wantUpdate ClusterUpdate 55 wantErr bool 56 }{ 57 { 58 name: "non-supported-cluster-type-static", 59 cluster: &v3clusterpb.Cluster{ 60 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 61 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 62 EdsConfig: &v3corepb.ConfigSource{ 63 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 64 Ads: &v3corepb.AggregatedConfigSource{}, 65 }, 66 }, 67 }, 68 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 69 }, 70 wantUpdate: emptyUpdate, 71 wantErr: true, 72 }, 73 { 74 name: "non-supported-cluster-type-original-dst", 75 cluster: &v3clusterpb.Cluster{ 76 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_ORIGINAL_DST}, 77 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 78 EdsConfig: &v3corepb.ConfigSource{ 79 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 80 Ads: &v3corepb.AggregatedConfigSource{}, 81 }, 82 }, 83 }, 84 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 85 }, 86 wantUpdate: emptyUpdate, 87 wantErr: true, 88 }, 89 { 90 name: "no-eds-config", 91 cluster: &v3clusterpb.Cluster{ 92 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 93 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 94 }, 95 wantUpdate: emptyUpdate, 96 wantErr: true, 97 }, 98 { 99 name: "no-ads-config-source", 100 cluster: &v3clusterpb.Cluster{ 101 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 102 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{}, 103 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 104 }, 105 wantUpdate: emptyUpdate, 106 wantErr: true, 107 }, 108 { 109 name: "non-round-robin-lb-policy", 110 cluster: &v3clusterpb.Cluster{ 111 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 112 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 113 EdsConfig: &v3corepb.ConfigSource{ 114 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 115 Ads: &v3corepb.AggregatedConfigSource{}, 116 }, 117 }, 118 }, 119 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 120 }, 121 wantUpdate: emptyUpdate, 122 wantErr: true, 123 }, 124 } 125 126 oldAggregateAndDNSSupportEnv := env.AggregateAndDNSSupportEnv 127 env.AggregateAndDNSSupportEnv = true 128 defer func() { env.AggregateAndDNSSupportEnv = oldAggregateAndDNSSupportEnv }() 129 for _, test := range tests { 130 t.Run(test.name, func(t *testing.T) { 131 if update, err := validateClusterAndConstructClusterUpdate(test.cluster); err == nil { 132 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update) 133 } 134 }) 135 } 136} 137 138func (s) TestValidateCluster_Success(t *testing.T) { 139 tests := []struct { 140 name string 141 cluster *v3clusterpb.Cluster 142 wantUpdate ClusterUpdate 143 }{ 144 { 145 name: "happy-case-logical-dns", 146 cluster: &v3clusterpb.Cluster{ 147 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS}, 148 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 149 }, 150 wantUpdate: ClusterUpdate{ServiceName: "", EnableLRS: false, ClusterType: ClusterTypeLogicalDNS}, 151 }, 152 { 153 name: "happy-case-aggregate-v3", 154 cluster: &v3clusterpb.Cluster{ 155 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 156 ClusterType: &v3clusterpb.Cluster_CustomClusterType{Name: "envoy.clusters.aggregate"}, 157 }, 158 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 159 }, 160 wantUpdate: ClusterUpdate{ServiceName: "", EnableLRS: false, ClusterType: ClusterTypeAggregate}, 161 }, 162 { 163 name: "happy-case-no-service-name-no-lrs", 164 cluster: &v3clusterpb.Cluster{ 165 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 166 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 167 EdsConfig: &v3corepb.ConfigSource{ 168 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 169 Ads: &v3corepb.AggregatedConfigSource{}, 170 }, 171 }, 172 }, 173 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 174 }, 175 wantUpdate: emptyUpdate, 176 }, 177 { 178 name: "happy-case-no-lrs", 179 cluster: &v3clusterpb.Cluster{ 180 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 181 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 182 EdsConfig: &v3corepb.ConfigSource{ 183 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 184 Ads: &v3corepb.AggregatedConfigSource{}, 185 }, 186 }, 187 ServiceName: serviceName, 188 }, 189 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 190 }, 191 wantUpdate: ClusterUpdate{ServiceName: serviceName, EnableLRS: false}, 192 }, 193 { 194 name: "happiest-case", 195 cluster: &v3clusterpb.Cluster{ 196 Name: clusterName, 197 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 198 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 199 EdsConfig: &v3corepb.ConfigSource{ 200 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 201 Ads: &v3corepb.AggregatedConfigSource{}, 202 }, 203 }, 204 ServiceName: serviceName, 205 }, 206 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 207 LrsServer: &v3corepb.ConfigSource{ 208 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 209 Self: &v3corepb.SelfConfigSource{}, 210 }, 211 }, 212 }, 213 wantUpdate: ClusterUpdate{ServiceName: serviceName, EnableLRS: true}, 214 }, 215 { 216 name: "happiest-case-with-circuitbreakers", 217 cluster: &v3clusterpb.Cluster{ 218 Name: clusterName, 219 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 220 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 221 EdsConfig: &v3corepb.ConfigSource{ 222 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 223 Ads: &v3corepb.AggregatedConfigSource{}, 224 }, 225 }, 226 ServiceName: serviceName, 227 }, 228 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 229 CircuitBreakers: &v3clusterpb.CircuitBreakers{ 230 Thresholds: []*v3clusterpb.CircuitBreakers_Thresholds{ 231 { 232 Priority: v3corepb.RoutingPriority_DEFAULT, 233 MaxRequests: wrapperspb.UInt32(512), 234 }, 235 { 236 Priority: v3corepb.RoutingPriority_HIGH, 237 MaxRequests: nil, 238 }, 239 }, 240 }, 241 LrsServer: &v3corepb.ConfigSource{ 242 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 243 Self: &v3corepb.SelfConfigSource{}, 244 }, 245 }, 246 }, 247 wantUpdate: ClusterUpdate{ServiceName: serviceName, EnableLRS: true, MaxRequests: func() *uint32 { i := uint32(512); return &i }()}, 248 }, 249 } 250 251 origCircuitBreakingSupport := env.CircuitBreakingSupport 252 env.CircuitBreakingSupport = true 253 defer func() { env.CircuitBreakingSupport = origCircuitBreakingSupport }() 254 oldAggregateAndDNSSupportEnv := env.AggregateAndDNSSupportEnv 255 env.AggregateAndDNSSupportEnv = true 256 defer func() { env.AggregateAndDNSSupportEnv = oldAggregateAndDNSSupportEnv }() 257 for _, test := range tests { 258 t.Run(test.name, func(t *testing.T) { 259 update, err := validateClusterAndConstructClusterUpdate(test.cluster) 260 if err != nil { 261 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) failed: %v", test.cluster, err) 262 } 263 if !cmp.Equal(update, test.wantUpdate, cmpopts.EquateEmpty()) { 264 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, want: %v", test.cluster, update, test.wantUpdate) 265 } 266 }) 267 } 268} 269 270func (s) TestValidateClusterWithSecurityConfig_EnvVarOff(t *testing.T) { 271 // Turn off the env var protection for client-side security. 272 origClientSideSecurityEnvVar := env.ClientSideSecuritySupport 273 env.ClientSideSecuritySupport = false 274 defer func() { env.ClientSideSecuritySupport = origClientSideSecurityEnvVar }() 275 276 cluster := &v3clusterpb.Cluster{ 277 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 278 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 279 EdsConfig: &v3corepb.ConfigSource{ 280 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 281 Ads: &v3corepb.AggregatedConfigSource{}, 282 }, 283 }, 284 ServiceName: serviceName, 285 }, 286 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 287 TransportSocket: &v3corepb.TransportSocket{ 288 Name: "envoy.transport_sockets.tls", 289 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 290 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 291 CommonTlsContext: &v3tlspb.CommonTlsContext{ 292 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 293 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 294 InstanceName: "rootInstance", 295 CertificateName: "rootCert", 296 }, 297 }, 298 }, 299 }), 300 }, 301 }, 302 } 303 wantUpdate := ClusterUpdate{ 304 ServiceName: serviceName, 305 EnableLRS: false, 306 } 307 gotUpdate, err := validateClusterAndConstructClusterUpdate(cluster) 308 if err != nil { 309 t.Errorf("validateClusterAndConstructClusterUpdate() failed: %v", err) 310 } 311 if diff := cmp.Diff(wantUpdate, gotUpdate); diff != "" { 312 t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, got):\n%s", diff) 313 } 314} 315 316func (s) TestValidateClusterWithSecurityConfig(t *testing.T) { 317 // Turn on the env var protection for client-side security. 318 origClientSideSecurityEnvVar := env.ClientSideSecuritySupport 319 env.ClientSideSecuritySupport = true 320 defer func() { env.ClientSideSecuritySupport = origClientSideSecurityEnvVar }() 321 322 const ( 323 identityPluginInstance = "identityPluginInstance" 324 identityCertName = "identityCert" 325 rootPluginInstance = "rootPluginInstance" 326 rootCertName = "rootCert" 327 serviceName = "service" 328 sanExact = "san-exact" 329 sanPrefix = "san-prefix" 330 sanSuffix = "san-suffix" 331 sanRegexBad = "??" 332 sanRegexGood = "san?regex?" 333 sanContains = "san-contains" 334 ) 335 var sanRE = regexp.MustCompile(sanRegexGood) 336 337 tests := []struct { 338 name string 339 cluster *v3clusterpb.Cluster 340 wantUpdate ClusterUpdate 341 wantErr bool 342 }{ 343 { 344 name: "transport-socket-unsupported-name", 345 cluster: &v3clusterpb.Cluster{ 346 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 347 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 348 EdsConfig: &v3corepb.ConfigSource{ 349 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 350 Ads: &v3corepb.AggregatedConfigSource{}, 351 }, 352 }, 353 ServiceName: serviceName, 354 }, 355 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 356 TransportSocket: &v3corepb.TransportSocket{ 357 Name: "unsupported-foo", 358 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 359 TypedConfig: &anypb.Any{ 360 TypeUrl: version.V3UpstreamTLSContextURL, 361 }, 362 }, 363 }, 364 }, 365 wantErr: true, 366 }, 367 { 368 name: "transport-socket-unsupported-typeURL", 369 cluster: &v3clusterpb.Cluster{ 370 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 371 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 372 EdsConfig: &v3corepb.ConfigSource{ 373 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 374 Ads: &v3corepb.AggregatedConfigSource{}, 375 }, 376 }, 377 ServiceName: serviceName, 378 }, 379 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 380 TransportSocket: &v3corepb.TransportSocket{ 381 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 382 TypedConfig: &anypb.Any{ 383 TypeUrl: version.V3HTTPConnManagerURL, 384 }, 385 }, 386 }, 387 }, 388 wantErr: true, 389 }, 390 { 391 name: "transport-socket-unsupported-type", 392 cluster: &v3clusterpb.Cluster{ 393 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 394 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 395 EdsConfig: &v3corepb.ConfigSource{ 396 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 397 Ads: &v3corepb.AggregatedConfigSource{}, 398 }, 399 }, 400 ServiceName: serviceName, 401 }, 402 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 403 TransportSocket: &v3corepb.TransportSocket{ 404 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 405 TypedConfig: &anypb.Any{ 406 TypeUrl: version.V3UpstreamTLSContextURL, 407 Value: []byte{1, 2, 3, 4}, 408 }, 409 }, 410 }, 411 }, 412 wantErr: true, 413 }, 414 { 415 name: "transport-socket-unsupported-validation-context", 416 cluster: &v3clusterpb.Cluster{ 417 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 418 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 419 EdsConfig: &v3corepb.ConfigSource{ 420 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 421 Ads: &v3corepb.AggregatedConfigSource{}, 422 }, 423 }, 424 ServiceName: serviceName, 425 }, 426 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 427 TransportSocket: &v3corepb.TransportSocket{ 428 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 429 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 430 CommonTlsContext: &v3tlspb.CommonTlsContext{ 431 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 432 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 433 Name: "foo-sds-secret", 434 }, 435 }, 436 }, 437 }), 438 }, 439 }, 440 }, 441 wantErr: true, 442 }, 443 { 444 name: "transport-socket-without-validation-context", 445 cluster: &v3clusterpb.Cluster{ 446 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 447 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 448 EdsConfig: &v3corepb.ConfigSource{ 449 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 450 Ads: &v3corepb.AggregatedConfigSource{}, 451 }, 452 }, 453 ServiceName: serviceName, 454 }, 455 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 456 TransportSocket: &v3corepb.TransportSocket{ 457 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 458 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 459 CommonTlsContext: &v3tlspb.CommonTlsContext{}, 460 }), 461 }, 462 }, 463 }, 464 wantErr: true, 465 }, 466 { 467 name: "empty-prefix-in-matching-SAN", 468 cluster: &v3clusterpb.Cluster{ 469 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 470 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 471 EdsConfig: &v3corepb.ConfigSource{ 472 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 473 Ads: &v3corepb.AggregatedConfigSource{}, 474 }, 475 }, 476 ServiceName: serviceName, 477 }, 478 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 479 TransportSocket: &v3corepb.TransportSocket{ 480 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 481 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 482 CommonTlsContext: &v3tlspb.CommonTlsContext{ 483 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 484 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 485 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 486 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 487 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, 488 }, 489 }, 490 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 491 InstanceName: rootPluginInstance, 492 CertificateName: rootCertName, 493 }, 494 }, 495 }, 496 }, 497 }), 498 }, 499 }, 500 }, 501 wantErr: true, 502 }, 503 { 504 name: "empty-suffix-in-matching-SAN", 505 cluster: &v3clusterpb.Cluster{ 506 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 507 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 508 EdsConfig: &v3corepb.ConfigSource{ 509 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 510 Ads: &v3corepb.AggregatedConfigSource{}, 511 }, 512 }, 513 ServiceName: serviceName, 514 }, 515 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 516 TransportSocket: &v3corepb.TransportSocket{ 517 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 518 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 519 CommonTlsContext: &v3tlspb.CommonTlsContext{ 520 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 521 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 522 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 523 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 524 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}}, 525 }, 526 }, 527 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 528 InstanceName: rootPluginInstance, 529 CertificateName: rootCertName, 530 }, 531 }, 532 }, 533 }, 534 }), 535 }, 536 }, 537 }, 538 wantErr: true, 539 }, 540 { 541 name: "empty-contains-in-matching-SAN", 542 cluster: &v3clusterpb.Cluster{ 543 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 544 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 545 EdsConfig: &v3corepb.ConfigSource{ 546 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 547 Ads: &v3corepb.AggregatedConfigSource{}, 548 }, 549 }, 550 ServiceName: serviceName, 551 }, 552 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 553 TransportSocket: &v3corepb.TransportSocket{ 554 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 555 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 556 CommonTlsContext: &v3tlspb.CommonTlsContext{ 557 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 558 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 559 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 560 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 561 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}}, 562 }, 563 }, 564 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 565 InstanceName: rootPluginInstance, 566 CertificateName: rootCertName, 567 }, 568 }, 569 }, 570 }, 571 }), 572 }, 573 }, 574 }, 575 wantErr: true, 576 }, 577 { 578 name: "invalid-regex-in-matching-SAN", 579 cluster: &v3clusterpb.Cluster{ 580 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 581 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 582 EdsConfig: &v3corepb.ConfigSource{ 583 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 584 Ads: &v3corepb.AggregatedConfigSource{}, 585 }, 586 }, 587 ServiceName: serviceName, 588 }, 589 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 590 TransportSocket: &v3corepb.TransportSocket{ 591 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 592 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 593 CommonTlsContext: &v3tlspb.CommonTlsContext{ 594 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 595 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 596 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 597 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 598 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, 599 }, 600 }, 601 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 602 InstanceName: rootPluginInstance, 603 CertificateName: rootCertName, 604 }, 605 }, 606 }, 607 }, 608 }), 609 }, 610 }, 611 }, 612 wantErr: true, 613 }, 614 { 615 name: "happy-case-with-no-identity-certs", 616 cluster: &v3clusterpb.Cluster{ 617 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 618 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 619 EdsConfig: &v3corepb.ConfigSource{ 620 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 621 Ads: &v3corepb.AggregatedConfigSource{}, 622 }, 623 }, 624 ServiceName: serviceName, 625 }, 626 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 627 TransportSocket: &v3corepb.TransportSocket{ 628 Name: "envoy.transport_sockets.tls", 629 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 630 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 631 CommonTlsContext: &v3tlspb.CommonTlsContext{ 632 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 633 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 634 InstanceName: rootPluginInstance, 635 CertificateName: rootCertName, 636 }, 637 }, 638 }, 639 }), 640 }, 641 }, 642 }, 643 wantUpdate: ClusterUpdate{ 644 ServiceName: serviceName, 645 EnableLRS: false, 646 SecurityCfg: &SecurityConfig{ 647 RootInstanceName: rootPluginInstance, 648 RootCertName: rootCertName, 649 }, 650 }, 651 }, 652 { 653 name: "happy-case-with-validation-context-provider-instance", 654 cluster: &v3clusterpb.Cluster{ 655 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 656 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 657 EdsConfig: &v3corepb.ConfigSource{ 658 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 659 Ads: &v3corepb.AggregatedConfigSource{}, 660 }, 661 }, 662 ServiceName: serviceName, 663 }, 664 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 665 TransportSocket: &v3corepb.TransportSocket{ 666 Name: "envoy.transport_sockets.tls", 667 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 668 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 669 CommonTlsContext: &v3tlspb.CommonTlsContext{ 670 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 671 InstanceName: identityPluginInstance, 672 CertificateName: identityCertName, 673 }, 674 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 675 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 676 InstanceName: rootPluginInstance, 677 CertificateName: rootCertName, 678 }, 679 }, 680 }, 681 }), 682 }, 683 }, 684 }, 685 wantUpdate: ClusterUpdate{ 686 ServiceName: serviceName, 687 EnableLRS: false, 688 SecurityCfg: &SecurityConfig{ 689 RootInstanceName: rootPluginInstance, 690 RootCertName: rootCertName, 691 IdentityInstanceName: identityPluginInstance, 692 IdentityCertName: identityCertName, 693 }, 694 }, 695 }, 696 { 697 name: "happy-case-with-combined-validation-context", 698 cluster: &v3clusterpb.Cluster{ 699 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 700 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 701 EdsConfig: &v3corepb.ConfigSource{ 702 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 703 Ads: &v3corepb.AggregatedConfigSource{}, 704 }, 705 }, 706 ServiceName: serviceName, 707 }, 708 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 709 TransportSocket: &v3corepb.TransportSocket{ 710 Name: "envoy.transport_sockets.tls", 711 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 712 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{ 713 CommonTlsContext: &v3tlspb.CommonTlsContext{ 714 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 715 InstanceName: identityPluginInstance, 716 CertificateName: identityCertName, 717 }, 718 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 719 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 720 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 721 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 722 { 723 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 724 IgnoreCase: true, 725 }, 726 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 727 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 728 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 729 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 730 }, 731 }, 732 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 733 InstanceName: rootPluginInstance, 734 CertificateName: rootCertName, 735 }, 736 }, 737 }, 738 }, 739 }), 740 }, 741 }, 742 }, 743 wantUpdate: ClusterUpdate{ 744 ServiceName: serviceName, 745 EnableLRS: false, 746 SecurityCfg: &SecurityConfig{ 747 RootInstanceName: rootPluginInstance, 748 RootCertName: rootCertName, 749 IdentityInstanceName: identityPluginInstance, 750 IdentityCertName: identityCertName, 751 SubjectAltNameMatchers: []xdsinternal.StringMatcher{ 752 xdsinternal.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 753 xdsinternal.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 754 xdsinternal.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 755 xdsinternal.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 756 xdsinternal.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 757 }, 758 }, 759 }, 760 }, 761 } 762 763 for _, test := range tests { 764 t.Run(test.name, func(t *testing.T) { 765 update, err := validateClusterAndConstructClusterUpdate(test.cluster) 766 if (err != nil) != test.wantErr { 767 t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) 768 } 769 if diff := cmp.Diff(test.wantUpdate, update, cmpopts.EquateEmpty(), cmp.AllowUnexported(regexp.Regexp{})); diff != "" { 770 t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, +got):\n%s", diff) 771 } 772 }) 773 } 774} 775 776func (s) TestUnmarshalCluster(t *testing.T) { 777 const ( 778 v2ClusterName = "v2clusterName" 779 v3ClusterName = "v3clusterName" 780 v2Service = "v2Service" 781 v3Service = "v2Service" 782 ) 783 var ( 784 v2ClusterAny = testutils.MarshalAny(&v2xdspb.Cluster{ 785 Name: v2ClusterName, 786 ClusterDiscoveryType: &v2xdspb.Cluster_Type{Type: v2xdspb.Cluster_EDS}, 787 EdsClusterConfig: &v2xdspb.Cluster_EdsClusterConfig{ 788 EdsConfig: &v2corepb.ConfigSource{ 789 ConfigSourceSpecifier: &v2corepb.ConfigSource_Ads{ 790 Ads: &v2corepb.AggregatedConfigSource{}, 791 }, 792 }, 793 ServiceName: v2Service, 794 }, 795 LbPolicy: v2xdspb.Cluster_ROUND_ROBIN, 796 LrsServer: &v2corepb.ConfigSource{ 797 ConfigSourceSpecifier: &v2corepb.ConfigSource_Self{ 798 Self: &v2corepb.SelfConfigSource{}, 799 }, 800 }, 801 }) 802 803 v3ClusterAny = testutils.MarshalAny(&v3clusterpb.Cluster{ 804 Name: v3ClusterName, 805 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 806 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 807 EdsConfig: &v3corepb.ConfigSource{ 808 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 809 Ads: &v3corepb.AggregatedConfigSource{}, 810 }, 811 }, 812 ServiceName: v3Service, 813 }, 814 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 815 LrsServer: &v3corepb.ConfigSource{ 816 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 817 Self: &v3corepb.SelfConfigSource{}, 818 }, 819 }, 820 }) 821 ) 822 const testVersion = "test-version-cds" 823 824 tests := []struct { 825 name string 826 resources []*anypb.Any 827 wantUpdate map[string]ClusterUpdate 828 wantMD UpdateMetadata 829 wantErr bool 830 }{ 831 { 832 name: "non-cluster resource type", 833 resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}}, 834 wantMD: UpdateMetadata{ 835 Status: ServiceStatusNACKed, 836 Version: testVersion, 837 ErrState: &UpdateErrorMetadata{ 838 Version: testVersion, 839 Err: errPlaceHolder, 840 }, 841 }, 842 wantErr: true, 843 }, 844 { 845 name: "badly marshaled cluster resource", 846 resources: []*anypb.Any{ 847 { 848 TypeUrl: version.V3ClusterURL, 849 Value: []byte{1, 2, 3, 4}, 850 }, 851 }, 852 wantMD: UpdateMetadata{ 853 Status: ServiceStatusNACKed, 854 Version: testVersion, 855 ErrState: &UpdateErrorMetadata{ 856 Version: testVersion, 857 Err: errPlaceHolder, 858 }, 859 }, 860 wantErr: true, 861 }, 862 { 863 name: "bad cluster resource", 864 resources: []*anypb.Any{ 865 testutils.MarshalAny(&v3clusterpb.Cluster{ 866 Name: "test", 867 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 868 }), 869 }, 870 wantUpdate: map[string]ClusterUpdate{"test": {}}, 871 wantMD: UpdateMetadata{ 872 Status: ServiceStatusNACKed, 873 Version: testVersion, 874 ErrState: &UpdateErrorMetadata{ 875 Version: testVersion, 876 Err: errPlaceHolder, 877 }, 878 }, 879 wantErr: true, 880 }, 881 { 882 name: "v2 cluster", 883 resources: []*anypb.Any{v2ClusterAny}, 884 wantUpdate: map[string]ClusterUpdate{ 885 v2ClusterName: { 886 ServiceName: v2Service, EnableLRS: true, 887 Raw: v2ClusterAny, 888 }, 889 }, 890 wantMD: UpdateMetadata{ 891 Status: ServiceStatusACKed, 892 Version: testVersion, 893 }, 894 }, 895 { 896 name: "v3 cluster", 897 resources: []*anypb.Any{v3ClusterAny}, 898 wantUpdate: map[string]ClusterUpdate{ 899 v3ClusterName: { 900 ServiceName: v3Service, EnableLRS: true, 901 Raw: v3ClusterAny, 902 }, 903 }, 904 wantMD: UpdateMetadata{ 905 Status: ServiceStatusACKed, 906 Version: testVersion, 907 }, 908 }, 909 { 910 name: "multiple clusters", 911 resources: []*anypb.Any{v2ClusterAny, v3ClusterAny}, 912 wantUpdate: map[string]ClusterUpdate{ 913 v2ClusterName: { 914 ServiceName: v2Service, EnableLRS: true, 915 Raw: v2ClusterAny, 916 }, 917 v3ClusterName: { 918 ServiceName: v3Service, EnableLRS: true, 919 Raw: v3ClusterAny, 920 }, 921 }, 922 wantMD: UpdateMetadata{ 923 Status: ServiceStatusACKed, 924 Version: testVersion, 925 }, 926 }, 927 { 928 // To test that unmarshal keeps processing on errors. 929 name: "good and bad clusters", 930 resources: []*anypb.Any{ 931 v2ClusterAny, 932 // bad cluster resource 933 testutils.MarshalAny(&v3clusterpb.Cluster{ 934 Name: "bad", 935 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 936 }), 937 v3ClusterAny, 938 }, 939 wantUpdate: map[string]ClusterUpdate{ 940 v2ClusterName: { 941 ServiceName: v2Service, EnableLRS: true, 942 Raw: v2ClusterAny, 943 }, 944 v3ClusterName: { 945 ServiceName: v3Service, EnableLRS: true, 946 Raw: v3ClusterAny, 947 }, 948 "bad": {}, 949 }, 950 wantMD: UpdateMetadata{ 951 Status: ServiceStatusNACKed, 952 Version: testVersion, 953 ErrState: &UpdateErrorMetadata{ 954 Version: testVersion, 955 Err: errPlaceHolder, 956 }, 957 }, 958 wantErr: true, 959 }, 960 } 961 for _, test := range tests { 962 t.Run(test.name, func(t *testing.T) { 963 update, md, err := UnmarshalCluster(testVersion, test.resources, nil) 964 if (err != nil) != test.wantErr { 965 t.Fatalf("UnmarshalCluster(), got err: %v, wantErr: %v", err, test.wantErr) 966 } 967 if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" { 968 t.Errorf("got unexpected update, diff (-got +want): %v", diff) 969 } 970 if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" { 971 t.Errorf("got unexpected metadata, diff (-got +want): %v", diff) 972 } 973 }) 974 } 975} 976