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