google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/xdsresource/unmarshal_cds_test.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package xdsresource 19 20 import ( 21 "encoding/json" 22 "regexp" 23 "strings" 24 "testing" 25 26 "github.com/google/go-cmp/cmp" 27 "github.com/google/go-cmp/cmp/cmpopts" 28 "google.golang.org/grpc/internal/pretty" 29 "google.golang.org/grpc/internal/testutils" 30 "google.golang.org/grpc/internal/xds/matcher" 31 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version" 32 33 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 34 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 35 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 36 v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" 37 v3leastrequestpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/least_request/v3" 38 v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3" 39 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 40 v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 41 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 42 "google.golang.org/protobuf/types/known/anypb" 43 "google.golang.org/protobuf/types/known/durationpb" 44 "google.golang.org/protobuf/types/known/wrapperspb" 45 ) 46 47 const ( 48 clusterName = "clusterName" 49 serviceName = "service" 50 ) 51 52 var emptyUpdate = ClusterUpdate{ClusterName: clusterName, LRSServerConfig: ClusterLRSOff} 53 54 func (s) TestValidateCluster_Failure(t *testing.T) { 55 tests := []struct { 56 name string 57 cluster *v3clusterpb.Cluster 58 wantUpdate ClusterUpdate 59 wantErr bool 60 }{ 61 { 62 name: "non-supported-cluster-type-static", 63 cluster: &v3clusterpb.Cluster{ 64 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 65 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 66 EdsConfig: &v3corepb.ConfigSource{ 67 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 68 Ads: &v3corepb.AggregatedConfigSource{}, 69 }, 70 }, 71 }, 72 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 73 }, 74 wantUpdate: emptyUpdate, 75 wantErr: true, 76 }, 77 { 78 name: "non-supported-cluster-type-original-dst", 79 cluster: &v3clusterpb.Cluster{ 80 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_ORIGINAL_DST}, 81 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 82 EdsConfig: &v3corepb.ConfigSource{ 83 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 84 Ads: &v3corepb.AggregatedConfigSource{}, 85 }, 86 }, 87 }, 88 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 89 }, 90 wantUpdate: emptyUpdate, 91 wantErr: true, 92 }, 93 { 94 name: "no-eds-config", 95 cluster: &v3clusterpb.Cluster{ 96 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 97 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 98 }, 99 wantUpdate: emptyUpdate, 100 wantErr: true, 101 }, 102 { 103 name: "no-ads-config-source", 104 cluster: &v3clusterpb.Cluster{ 105 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 106 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{}, 107 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 108 }, 109 wantUpdate: emptyUpdate, 110 wantErr: true, 111 }, 112 { 113 name: "non-round-robin-or-ring-hash-lb-policy", 114 cluster: &v3clusterpb.Cluster{ 115 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 116 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 117 EdsConfig: &v3corepb.ConfigSource{ 118 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 119 Ads: &v3corepb.AggregatedConfigSource{}, 120 }, 121 }, 122 }, 123 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 124 }, 125 wantUpdate: emptyUpdate, 126 wantErr: true, 127 }, 128 { 129 name: "logical-dns-multiple-localities", 130 cluster: &v3clusterpb.Cluster{ 131 Name: clusterName, 132 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS}, 133 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 134 LoadAssignment: &v3endpointpb.ClusterLoadAssignment{ 135 Endpoints: []*v3endpointpb.LocalityLbEndpoints{ 136 // Invalid if there are more than one locality. 137 {LbEndpoints: nil}, 138 {LbEndpoints: nil}, 139 }, 140 }, 141 }, 142 wantUpdate: emptyUpdate, 143 wantErr: true, 144 }, 145 { 146 name: "ring-hash-hash-function-not-xx-hash", 147 cluster: &v3clusterpb.Cluster{ 148 LbPolicy: v3clusterpb.Cluster_RING_HASH, 149 LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ 150 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 151 HashFunction: v3clusterpb.Cluster_RingHashLbConfig_MURMUR_HASH_2, 152 }, 153 }, 154 }, 155 wantUpdate: emptyUpdate, 156 wantErr: true, 157 }, 158 { 159 name: "least-request-choice-count-less-than-two", 160 cluster: &v3clusterpb.Cluster{ 161 LbPolicy: v3clusterpb.Cluster_RING_HASH, 162 LbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig_{ 163 LeastRequestLbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig{ 164 ChoiceCount: wrapperspb.UInt32(1), 165 }, 166 }, 167 }, 168 wantUpdate: emptyUpdate, 169 wantErr: true, 170 }, 171 { 172 name: "ring-hash-max-bound-greater-than-upper-bound", 173 cluster: &v3clusterpb.Cluster{ 174 LbPolicy: v3clusterpb.Cluster_RING_HASH, 175 LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ 176 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 177 MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), 178 }, 179 }, 180 }, 181 wantUpdate: emptyUpdate, 182 wantErr: true, 183 }, 184 { 185 name: "ring-hash-max-bound-greater-than-upper-bound-load-balancing-policy", 186 cluster: &v3clusterpb.Cluster{ 187 Name: clusterName, 188 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 189 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 190 EdsConfig: &v3corepb.ConfigSource{ 191 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 192 Ads: &v3corepb.AggregatedConfigSource{}, 193 }, 194 }, 195 ServiceName: serviceName, 196 }, 197 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 198 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 199 { 200 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 201 TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{ 202 HashFunction: v3ringhashpb.RingHash_XX_HASH, 203 MinimumRingSize: wrapperspb.UInt64(10), 204 MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), 205 }), 206 }, 207 }, 208 }, 209 }, 210 }, 211 wantUpdate: emptyUpdate, 212 wantErr: true, 213 }, 214 { 215 name: "least-request-unsupported-in-converter-since-env-var-unset", 216 cluster: &v3clusterpb.Cluster{ 217 Name: clusterName, 218 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 219 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 220 EdsConfig: &v3corepb.ConfigSource{ 221 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 222 Ads: &v3corepb.AggregatedConfigSource{}, 223 }, 224 }, 225 ServiceName: serviceName, 226 }, 227 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 228 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 229 { 230 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 231 TypedConfig: testutils.MarshalAny(t, &v3leastrequestpb.LeastRequest{}), 232 }, 233 }, 234 }, 235 }, 236 }, 237 wantUpdate: emptyUpdate, 238 wantErr: true, 239 }, 240 { 241 name: "aggregate-nil-clusters", 242 cluster: &v3clusterpb.Cluster{ 243 Name: clusterName, 244 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 245 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 246 Name: "envoy.clusters.aggregate", 247 TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{}), 248 }, 249 }, 250 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 251 }, 252 wantUpdate: emptyUpdate, 253 wantErr: true, 254 }, 255 { 256 name: "aggregate-empty-clusters", 257 cluster: &v3clusterpb.Cluster{ 258 Name: clusterName, 259 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 260 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 261 Name: "envoy.clusters.aggregate", 262 TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{ 263 Clusters: []string{}, 264 }), 265 }, 266 }, 267 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 268 }, 269 wantUpdate: emptyUpdate, 270 wantErr: true, 271 }, 272 } 273 274 for _, test := range tests { 275 t.Run(test.name, func(t *testing.T) { 276 if update, err := validateClusterAndConstructClusterUpdate(test.cluster); err == nil { 277 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update) 278 } 279 }) 280 } 281 } 282 283 func (s) TestSecurityConfigFromCommonTLSContextUsingNewFields_ErrorCases(t *testing.T) { 284 tests := []struct { 285 name string 286 common *v3tlspb.CommonTlsContext 287 server bool 288 wantErr string 289 }{ 290 { 291 name: "unsupported-tls_certificates-field-for-identity-certs", 292 common: &v3tlspb.CommonTlsContext{ 293 TlsCertificates: []*v3tlspb.TlsCertificate{ 294 {CertificateChain: &v3corepb.DataSource{}}, 295 }, 296 }, 297 wantErr: "unsupported field tls_certificates is set in CommonTlsContext message", 298 }, 299 { 300 name: "unsupported-tls_certificates_sds_secret_configs-field-for-identity-certs", 301 common: &v3tlspb.CommonTlsContext{ 302 TlsCertificateSdsSecretConfigs: []*v3tlspb.SdsSecretConfig{ 303 {Name: "sds-secrets-config"}, 304 }, 305 }, 306 wantErr: "unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message", 307 }, 308 { 309 name: "unsupported-sds-validation-context", 310 common: &v3tlspb.CommonTlsContext{ 311 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 312 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 313 Name: "foo-sds-secret", 314 }, 315 }, 316 }, 317 wantErr: "validation context contains unexpected type", 318 }, 319 { 320 name: "missing-ca_certificate_provider_instance-in-validation-context", 321 common: &v3tlspb.CommonTlsContext{ 322 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 323 ValidationContext: &v3tlspb.CertificateValidationContext{}, 324 }, 325 }, 326 wantErr: "expected field ca_certificate_provider_instance is missing in CommonTlsContext message", 327 }, 328 { 329 name: "unsupported-field-verify_certificate_spki-in-validation-context", 330 common: &v3tlspb.CommonTlsContext{ 331 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 332 ValidationContext: &v3tlspb.CertificateValidationContext{ 333 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 334 InstanceName: "rootPluginInstance", 335 CertificateName: "rootCertName", 336 }, 337 VerifyCertificateSpki: []string{"spki"}, 338 }, 339 }, 340 }, 341 wantErr: "unsupported verify_certificate_spki field in CommonTlsContext message", 342 }, 343 { 344 name: "unsupported-field-verify_certificate_hash-in-validation-context", 345 common: &v3tlspb.CommonTlsContext{ 346 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 347 ValidationContext: &v3tlspb.CertificateValidationContext{ 348 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 349 InstanceName: "rootPluginInstance", 350 CertificateName: "rootCertName", 351 }, 352 VerifyCertificateHash: []string{"hash"}, 353 }, 354 }, 355 }, 356 wantErr: "unsupported verify_certificate_hash field in CommonTlsContext message", 357 }, 358 { 359 name: "unsupported-field-require_signed_certificate_timestamp-in-validation-context", 360 common: &v3tlspb.CommonTlsContext{ 361 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 362 ValidationContext: &v3tlspb.CertificateValidationContext{ 363 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 364 InstanceName: "rootPluginInstance", 365 CertificateName: "rootCertName", 366 }, 367 RequireSignedCertificateTimestamp: &wrapperspb.BoolValue{Value: true}, 368 }, 369 }, 370 }, 371 wantErr: "unsupported require_sugned_ceritificate_timestamp field in CommonTlsContext message", 372 }, 373 { 374 name: "unsupported-field-crl-in-validation-context", 375 common: &v3tlspb.CommonTlsContext{ 376 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 377 ValidationContext: &v3tlspb.CertificateValidationContext{ 378 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 379 InstanceName: "rootPluginInstance", 380 CertificateName: "rootCertName", 381 }, 382 Crl: &v3corepb.DataSource{}, 383 }, 384 }, 385 }, 386 wantErr: "unsupported crl field in CommonTlsContext message", 387 }, 388 { 389 name: "unsupported-field-custom_validator_config-in-validation-context", 390 common: &v3tlspb.CommonTlsContext{ 391 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 392 ValidationContext: &v3tlspb.CertificateValidationContext{ 393 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 394 InstanceName: "rootPluginInstance", 395 CertificateName: "rootCertName", 396 }, 397 CustomValidatorConfig: &v3corepb.TypedExtensionConfig{}, 398 }, 399 }, 400 }, 401 wantErr: "unsupported custom_validator_config field in CommonTlsContext message", 402 }, 403 { 404 name: "invalid-match_subject_alt_names-field-in-validation-context", 405 common: &v3tlspb.CommonTlsContext{ 406 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 407 ValidationContext: &v3tlspb.CertificateValidationContext{ 408 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 409 InstanceName: "rootPluginInstance", 410 CertificateName: "rootCertName", 411 }, 412 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 413 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, 414 }, 415 }, 416 }, 417 }, 418 wantErr: "empty prefix is not allowed in StringMatcher", 419 }, 420 { 421 name: "unsupported-field-matching-subject-alt-names-in-validation-context-of-server", 422 common: &v3tlspb.CommonTlsContext{ 423 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 424 ValidationContext: &v3tlspb.CertificateValidationContext{ 425 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 426 InstanceName: "rootPluginInstance", 427 CertificateName: "rootCertName", 428 }, 429 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 430 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "sanPrefix"}}, 431 }, 432 }, 433 }, 434 }, 435 server: true, 436 wantErr: "match_subject_alt_names field in validation context is not supported on the server", 437 }, 438 } 439 440 for _, test := range tests { 441 t.Run(test.name, func(t *testing.T) { 442 _, err := securityConfigFromCommonTLSContextUsingNewFields(test.common, test.server) 443 if err == nil { 444 t.Fatal("securityConfigFromCommonTLSContextUsingNewFields() succeeded when expected to fail") 445 } 446 if !strings.Contains(err.Error(), test.wantErr) { 447 t.Fatalf("securityConfigFromCommonTLSContextUsingNewFields() returned err: %v, wantErr: %v", err, test.wantErr) 448 } 449 }) 450 } 451 } 452 453 func (s) TestValidateClusterWithSecurityConfig(t *testing.T) { 454 const ( 455 identityPluginInstance = "identityPluginInstance" 456 identityCertName = "identityCert" 457 rootPluginInstance = "rootPluginInstance" 458 rootCertName = "rootCert" 459 clusterName = "cluster" 460 serviceName = "service" 461 sanExact = "san-exact" 462 sanPrefix = "san-prefix" 463 sanSuffix = "san-suffix" 464 sanRegexBad = "??" 465 sanRegexGood = "san?regex?" 466 sanContains = "san-contains" 467 ) 468 var sanRE = regexp.MustCompile(sanRegexGood) 469 470 tests := []struct { 471 name string 472 cluster *v3clusterpb.Cluster 473 wantUpdate ClusterUpdate 474 wantErr bool 475 }{ 476 { 477 name: "transport-socket-matches", 478 cluster: &v3clusterpb.Cluster{ 479 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 480 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 481 EdsConfig: &v3corepb.ConfigSource{ 482 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 483 Ads: &v3corepb.AggregatedConfigSource{}, 484 }, 485 }, 486 ServiceName: serviceName, 487 }, 488 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 489 TransportSocketMatches: []*v3clusterpb.Cluster_TransportSocketMatch{ 490 {Name: "transport-socket-match-1"}, 491 }, 492 }, 493 wantErr: true, 494 }, 495 { 496 name: "transport-socket-unsupported-name", 497 cluster: &v3clusterpb.Cluster{ 498 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 499 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 500 EdsConfig: &v3corepb.ConfigSource{ 501 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 502 Ads: &v3corepb.AggregatedConfigSource{}, 503 }, 504 }, 505 ServiceName: serviceName, 506 }, 507 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 508 TransportSocket: &v3corepb.TransportSocket{ 509 Name: "unsupported-foo", 510 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 511 TypedConfig: &anypb.Any{ 512 TypeUrl: version.V3UpstreamTLSContextURL, 513 }, 514 }, 515 }, 516 }, 517 wantErr: true, 518 }, 519 { 520 name: "transport-socket-unsupported-typeURL", 521 cluster: &v3clusterpb.Cluster{ 522 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 523 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 524 EdsConfig: &v3corepb.ConfigSource{ 525 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 526 Ads: &v3corepb.AggregatedConfigSource{}, 527 }, 528 }, 529 ServiceName: serviceName, 530 }, 531 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 532 TransportSocket: &v3corepb.TransportSocket{ 533 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 534 TypedConfig: &anypb.Any{ 535 TypeUrl: version.V3HTTPConnManagerURL, 536 }, 537 }, 538 }, 539 }, 540 wantErr: true, 541 }, 542 { 543 name: "transport-socket-unsupported-type", 544 cluster: &v3clusterpb.Cluster{ 545 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 546 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 547 EdsConfig: &v3corepb.ConfigSource{ 548 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 549 Ads: &v3corepb.AggregatedConfigSource{}, 550 }, 551 }, 552 ServiceName: serviceName, 553 }, 554 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 555 TransportSocket: &v3corepb.TransportSocket{ 556 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 557 TypedConfig: &anypb.Any{ 558 TypeUrl: version.V3UpstreamTLSContextURL, 559 Value: []byte{1, 2, 3, 4}, 560 }, 561 }, 562 }, 563 }, 564 wantErr: true, 565 }, 566 { 567 name: "transport-socket-unsupported-tls-params-field", 568 cluster: &v3clusterpb.Cluster{ 569 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 570 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 571 EdsConfig: &v3corepb.ConfigSource{ 572 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 573 Ads: &v3corepb.AggregatedConfigSource{}, 574 }, 575 }, 576 ServiceName: serviceName, 577 }, 578 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 579 TransportSocket: &v3corepb.TransportSocket{ 580 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 581 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 582 CommonTlsContext: &v3tlspb.CommonTlsContext{ 583 TlsParams: &v3tlspb.TlsParameters{}, 584 }, 585 }), 586 }, 587 }, 588 }, 589 wantErr: true, 590 }, 591 { 592 name: "transport-socket-unsupported-custom-handshaker-field", 593 cluster: &v3clusterpb.Cluster{ 594 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 595 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 596 EdsConfig: &v3corepb.ConfigSource{ 597 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 598 Ads: &v3corepb.AggregatedConfigSource{}, 599 }, 600 }, 601 ServiceName: serviceName, 602 }, 603 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 604 TransportSocket: &v3corepb.TransportSocket{ 605 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 606 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 607 CommonTlsContext: &v3tlspb.CommonTlsContext{ 608 CustomHandshaker: &v3corepb.TypedExtensionConfig{}, 609 }, 610 }), 611 }, 612 }, 613 }, 614 wantErr: true, 615 }, 616 { 617 name: "transport-socket-unsupported-validation-context", 618 cluster: &v3clusterpb.Cluster{ 619 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 620 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 621 EdsConfig: &v3corepb.ConfigSource{ 622 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 623 Ads: &v3corepb.AggregatedConfigSource{}, 624 }, 625 }, 626 ServiceName: serviceName, 627 }, 628 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 629 TransportSocket: &v3corepb.TransportSocket{ 630 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 631 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 632 CommonTlsContext: &v3tlspb.CommonTlsContext{ 633 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 634 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 635 Name: "foo-sds-secret", 636 }, 637 }, 638 }, 639 }), 640 }, 641 }, 642 }, 643 wantErr: true, 644 }, 645 { 646 name: "transport-socket-without-validation-context", 647 cluster: &v3clusterpb.Cluster{ 648 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 649 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 650 EdsConfig: &v3corepb.ConfigSource{ 651 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 652 Ads: &v3corepb.AggregatedConfigSource{}, 653 }, 654 }, 655 ServiceName: serviceName, 656 }, 657 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 658 TransportSocket: &v3corepb.TransportSocket{ 659 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 660 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 661 CommonTlsContext: &v3tlspb.CommonTlsContext{}, 662 }), 663 }, 664 }, 665 }, 666 wantErr: true, 667 }, 668 { 669 name: "empty-prefix-in-matching-SAN", 670 cluster: &v3clusterpb.Cluster{ 671 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 672 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 673 EdsConfig: &v3corepb.ConfigSource{ 674 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 675 Ads: &v3corepb.AggregatedConfigSource{}, 676 }, 677 }, 678 ServiceName: serviceName, 679 }, 680 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 681 TransportSocket: &v3corepb.TransportSocket{ 682 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 683 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 684 CommonTlsContext: &v3tlspb.CommonTlsContext{ 685 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 686 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 687 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 688 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 689 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, 690 }, 691 }, 692 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 693 InstanceName: rootPluginInstance, 694 CertificateName: rootCertName, 695 }, 696 }, 697 }, 698 }, 699 }), 700 }, 701 }, 702 }, 703 wantErr: true, 704 }, 705 { 706 name: "empty-suffix-in-matching-SAN", 707 cluster: &v3clusterpb.Cluster{ 708 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 709 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 710 EdsConfig: &v3corepb.ConfigSource{ 711 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 712 Ads: &v3corepb.AggregatedConfigSource{}, 713 }, 714 }, 715 ServiceName: serviceName, 716 }, 717 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 718 TransportSocket: &v3corepb.TransportSocket{ 719 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 720 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 721 CommonTlsContext: &v3tlspb.CommonTlsContext{ 722 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 723 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 724 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 725 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 726 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}}, 727 }, 728 }, 729 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 730 InstanceName: rootPluginInstance, 731 CertificateName: rootCertName, 732 }, 733 }, 734 }, 735 }, 736 }), 737 }, 738 }, 739 }, 740 wantErr: true, 741 }, 742 { 743 name: "empty-contains-in-matching-SAN", 744 cluster: &v3clusterpb.Cluster{ 745 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 746 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 747 EdsConfig: &v3corepb.ConfigSource{ 748 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 749 Ads: &v3corepb.AggregatedConfigSource{}, 750 }, 751 }, 752 ServiceName: serviceName, 753 }, 754 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 755 TransportSocket: &v3corepb.TransportSocket{ 756 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 757 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 758 CommonTlsContext: &v3tlspb.CommonTlsContext{ 759 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 760 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 761 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 762 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 763 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}}, 764 }, 765 }, 766 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 767 InstanceName: rootPluginInstance, 768 CertificateName: rootCertName, 769 }, 770 }, 771 }, 772 }, 773 }), 774 }, 775 }, 776 }, 777 wantErr: true, 778 }, 779 { 780 name: "invalid-regex-in-matching-SAN", 781 cluster: &v3clusterpb.Cluster{ 782 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 783 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 784 EdsConfig: &v3corepb.ConfigSource{ 785 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 786 Ads: &v3corepb.AggregatedConfigSource{}, 787 }, 788 }, 789 ServiceName: serviceName, 790 }, 791 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 792 TransportSocket: &v3corepb.TransportSocket{ 793 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 794 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 795 CommonTlsContext: &v3tlspb.CommonTlsContext{ 796 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 797 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 798 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 799 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 800 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, 801 }, 802 }, 803 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 804 InstanceName: rootPluginInstance, 805 CertificateName: rootCertName, 806 }, 807 }, 808 }, 809 }, 810 }), 811 }, 812 }, 813 }, 814 wantErr: true, 815 }, 816 { 817 name: "invalid-regex-in-matching-SAN-with-new-fields", 818 cluster: &v3clusterpb.Cluster{ 819 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 820 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 821 EdsConfig: &v3corepb.ConfigSource{ 822 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 823 Ads: &v3corepb.AggregatedConfigSource{}, 824 }, 825 }, 826 ServiceName: serviceName, 827 }, 828 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 829 TransportSocket: &v3corepb.TransportSocket{ 830 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 831 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 832 CommonTlsContext: &v3tlspb.CommonTlsContext{ 833 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 834 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 835 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 836 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 837 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, 838 }, 839 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 840 InstanceName: rootPluginInstance, 841 CertificateName: rootCertName, 842 }, 843 }, 844 }, 845 }, 846 }, 847 }), 848 }, 849 }, 850 }, 851 wantErr: true, 852 }, 853 { 854 name: "happy-case-with-no-identity-certs-using-deprecated-fields", 855 cluster: &v3clusterpb.Cluster{ 856 Name: clusterName, 857 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 858 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 859 EdsConfig: &v3corepb.ConfigSource{ 860 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 861 Ads: &v3corepb.AggregatedConfigSource{}, 862 }, 863 }, 864 ServiceName: serviceName, 865 }, 866 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 867 TransportSocket: &v3corepb.TransportSocket{ 868 Name: "envoy.transport_sockets.tls", 869 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 870 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 871 CommonTlsContext: &v3tlspb.CommonTlsContext{ 872 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 873 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 874 InstanceName: rootPluginInstance, 875 CertificateName: rootCertName, 876 }, 877 }, 878 }, 879 }), 880 }, 881 }, 882 }, 883 wantUpdate: ClusterUpdate{ 884 ClusterName: clusterName, 885 EDSServiceName: serviceName, 886 LRSServerConfig: ClusterLRSOff, 887 SecurityCfg: &SecurityConfig{ 888 RootInstanceName: rootPluginInstance, 889 RootCertName: rootCertName, 890 }, 891 }, 892 }, 893 { 894 name: "happy-case-with-no-identity-certs-using-new-fields", 895 cluster: &v3clusterpb.Cluster{ 896 Name: clusterName, 897 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 898 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 899 EdsConfig: &v3corepb.ConfigSource{ 900 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 901 Ads: &v3corepb.AggregatedConfigSource{}, 902 }, 903 }, 904 ServiceName: serviceName, 905 }, 906 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 907 TransportSocket: &v3corepb.TransportSocket{ 908 Name: "envoy.transport_sockets.tls", 909 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 910 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 911 CommonTlsContext: &v3tlspb.CommonTlsContext{ 912 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 913 ValidationContext: &v3tlspb.CertificateValidationContext{ 914 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 915 InstanceName: rootPluginInstance, 916 CertificateName: rootCertName, 917 }, 918 }, 919 }, 920 }, 921 }), 922 }, 923 }, 924 }, 925 wantUpdate: ClusterUpdate{ 926 ClusterName: clusterName, 927 EDSServiceName: serviceName, 928 LRSServerConfig: ClusterLRSOff, 929 SecurityCfg: &SecurityConfig{ 930 RootInstanceName: rootPluginInstance, 931 RootCertName: rootCertName, 932 }, 933 }, 934 }, 935 { 936 name: "happy-case-with-validation-context-provider-instance-using-deprecated-fields", 937 cluster: &v3clusterpb.Cluster{ 938 Name: clusterName, 939 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 940 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 941 EdsConfig: &v3corepb.ConfigSource{ 942 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 943 Ads: &v3corepb.AggregatedConfigSource{}, 944 }, 945 }, 946 ServiceName: serviceName, 947 }, 948 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 949 TransportSocket: &v3corepb.TransportSocket{ 950 Name: "envoy.transport_sockets.tls", 951 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 952 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 953 CommonTlsContext: &v3tlspb.CommonTlsContext{ 954 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 955 InstanceName: identityPluginInstance, 956 CertificateName: identityCertName, 957 }, 958 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 959 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 960 InstanceName: rootPluginInstance, 961 CertificateName: rootCertName, 962 }, 963 }, 964 }, 965 }), 966 }, 967 }, 968 }, 969 wantUpdate: ClusterUpdate{ 970 ClusterName: clusterName, 971 EDSServiceName: serviceName, 972 LRSServerConfig: ClusterLRSOff, 973 SecurityCfg: &SecurityConfig{ 974 RootInstanceName: rootPluginInstance, 975 RootCertName: rootCertName, 976 IdentityInstanceName: identityPluginInstance, 977 IdentityCertName: identityCertName, 978 }, 979 }, 980 }, 981 { 982 name: "happy-case-with-validation-context-provider-instance-using-new-fields", 983 cluster: &v3clusterpb.Cluster{ 984 Name: clusterName, 985 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 986 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 987 EdsConfig: &v3corepb.ConfigSource{ 988 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 989 Ads: &v3corepb.AggregatedConfigSource{}, 990 }, 991 }, 992 ServiceName: serviceName, 993 }, 994 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 995 TransportSocket: &v3corepb.TransportSocket{ 996 Name: "envoy.transport_sockets.tls", 997 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 998 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 999 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1000 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1001 InstanceName: identityPluginInstance, 1002 CertificateName: identityCertName, 1003 }, 1004 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 1005 ValidationContext: &v3tlspb.CertificateValidationContext{ 1006 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1007 InstanceName: rootPluginInstance, 1008 CertificateName: rootCertName, 1009 }, 1010 }, 1011 }, 1012 }, 1013 }), 1014 }, 1015 }, 1016 }, 1017 wantUpdate: ClusterUpdate{ 1018 ClusterName: clusterName, 1019 EDSServiceName: serviceName, 1020 LRSServerConfig: ClusterLRSOff, 1021 SecurityCfg: &SecurityConfig{ 1022 RootInstanceName: rootPluginInstance, 1023 RootCertName: rootCertName, 1024 IdentityInstanceName: identityPluginInstance, 1025 IdentityCertName: identityCertName, 1026 }, 1027 }, 1028 }, 1029 { 1030 name: "happy-case-with-combined-validation-context-using-deprecated-fields", 1031 cluster: &v3clusterpb.Cluster{ 1032 Name: clusterName, 1033 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1034 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1035 EdsConfig: &v3corepb.ConfigSource{ 1036 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1037 Ads: &v3corepb.AggregatedConfigSource{}, 1038 }, 1039 }, 1040 ServiceName: serviceName, 1041 }, 1042 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1043 TransportSocket: &v3corepb.TransportSocket{ 1044 Name: "envoy.transport_sockets.tls", 1045 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1046 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1047 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1048 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1049 InstanceName: identityPluginInstance, 1050 CertificateName: identityCertName, 1051 }, 1052 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 1053 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 1054 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 1055 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 1056 { 1057 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 1058 IgnoreCase: true, 1059 }, 1060 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 1061 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 1062 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 1063 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 1064 }, 1065 }, 1066 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1067 InstanceName: rootPluginInstance, 1068 CertificateName: rootCertName, 1069 }, 1070 }, 1071 }, 1072 }, 1073 }), 1074 }, 1075 }, 1076 }, 1077 wantUpdate: ClusterUpdate{ 1078 ClusterName: clusterName, 1079 EDSServiceName: serviceName, 1080 LRSServerConfig: ClusterLRSOff, 1081 SecurityCfg: &SecurityConfig{ 1082 RootInstanceName: rootPluginInstance, 1083 RootCertName: rootCertName, 1084 IdentityInstanceName: identityPluginInstance, 1085 IdentityCertName: identityCertName, 1086 SubjectAltNameMatchers: []matcher.StringMatcher{ 1087 matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 1088 matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 1089 matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 1090 matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 1091 matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 1092 }, 1093 }, 1094 }, 1095 }, 1096 { 1097 name: "happy-case-with-combined-validation-context-using-new-fields", 1098 cluster: &v3clusterpb.Cluster{ 1099 Name: clusterName, 1100 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1101 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1102 EdsConfig: &v3corepb.ConfigSource{ 1103 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1104 Ads: &v3corepb.AggregatedConfigSource{}, 1105 }, 1106 }, 1107 ServiceName: serviceName, 1108 }, 1109 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1110 TransportSocket: &v3corepb.TransportSocket{ 1111 Name: "envoy.transport_sockets.tls", 1112 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1113 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1114 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1115 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1116 InstanceName: identityPluginInstance, 1117 CertificateName: identityCertName, 1118 }, 1119 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 1120 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 1121 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 1122 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 1123 { 1124 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 1125 IgnoreCase: true, 1126 }, 1127 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 1128 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 1129 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 1130 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 1131 }, 1132 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1133 InstanceName: rootPluginInstance, 1134 CertificateName: rootCertName, 1135 }, 1136 }, 1137 }, 1138 }, 1139 }, 1140 }), 1141 }, 1142 }, 1143 }, 1144 wantUpdate: ClusterUpdate{ 1145 ClusterName: clusterName, 1146 EDSServiceName: serviceName, 1147 LRSServerConfig: ClusterLRSOff, 1148 SecurityCfg: &SecurityConfig{ 1149 RootInstanceName: rootPluginInstance, 1150 RootCertName: rootCertName, 1151 IdentityInstanceName: identityPluginInstance, 1152 IdentityCertName: identityCertName, 1153 SubjectAltNameMatchers: []matcher.StringMatcher{ 1154 matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 1155 matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 1156 matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 1157 matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 1158 matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 1159 }, 1160 }, 1161 }, 1162 }, 1163 } 1164 1165 for _, test := range tests { 1166 t.Run(test.name, func(t *testing.T) { 1167 update, err := validateClusterAndConstructClusterUpdate(test.cluster) 1168 if (err != nil) != test.wantErr { 1169 t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) 1170 } 1171 if diff := cmp.Diff(test.wantUpdate, update, cmpopts.EquateEmpty(), cmp.AllowUnexported(regexp.Regexp{}), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { 1172 t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, +got):\n%s", diff) 1173 } 1174 }) 1175 } 1176 } 1177 1178 func (s) TestUnmarshalCluster(t *testing.T) { 1179 const ( 1180 v3ClusterName = "v3clusterName" 1181 v3Service = "v3Service" 1182 ) 1183 var ( 1184 v3ClusterAny = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1185 Name: v3ClusterName, 1186 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1187 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1188 EdsConfig: &v3corepb.ConfigSource{ 1189 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1190 Ads: &v3corepb.AggregatedConfigSource{}, 1191 }, 1192 }, 1193 ServiceName: v3Service, 1194 }, 1195 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1196 LrsServer: &v3corepb.ConfigSource{ 1197 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1198 Self: &v3corepb.SelfConfigSource{}, 1199 }, 1200 }, 1201 }) 1202 1203 v3ClusterAnyWithEDSConfigSourceSelf = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1204 Name: v3ClusterName, 1205 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1206 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1207 EdsConfig: &v3corepb.ConfigSource{ 1208 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{}, 1209 }, 1210 ServiceName: v3Service, 1211 }, 1212 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1213 LrsServer: &v3corepb.ConfigSource{ 1214 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1215 Self: &v3corepb.SelfConfigSource{}, 1216 }, 1217 }, 1218 }) 1219 ) 1220 1221 tests := []struct { 1222 name string 1223 resource *anypb.Any 1224 wantName string 1225 wantUpdate ClusterUpdate 1226 wantErr bool 1227 }{ 1228 { 1229 name: "non-cluster resource type", 1230 resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL}, 1231 wantErr: true, 1232 }, 1233 { 1234 name: "badly marshaled cluster resource", 1235 resource: &anypb.Any{ 1236 TypeUrl: version.V3ClusterURL, 1237 Value: []byte{1, 2, 3, 4}, 1238 }, 1239 wantErr: true, 1240 }, 1241 { 1242 name: "bad cluster resource", 1243 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1244 Name: "test", 1245 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 1246 }), 1247 wantName: "test", 1248 wantErr: true, 1249 }, 1250 { 1251 name: "cluster resource with non-self lrs_server field", 1252 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1253 Name: "test", 1254 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1255 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1256 EdsConfig: &v3corepb.ConfigSource{ 1257 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1258 Ads: &v3corepb.AggregatedConfigSource{}, 1259 }, 1260 }, 1261 ServiceName: v3Service, 1262 }, 1263 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1264 LrsServer: &v3corepb.ConfigSource{ 1265 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1266 Ads: &v3corepb.AggregatedConfigSource{}, 1267 }, 1268 }, 1269 }), 1270 wantName: "test", 1271 wantErr: true, 1272 }, 1273 { 1274 name: "v3 cluster", 1275 resource: v3ClusterAny, 1276 wantName: v3ClusterName, 1277 wantUpdate: ClusterUpdate{ 1278 ClusterName: v3ClusterName, 1279 EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf, 1280 Raw: v3ClusterAny, 1281 }, 1282 }, 1283 { 1284 name: "v3 cluster wrapped", 1285 resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}), 1286 wantName: v3ClusterName, 1287 wantUpdate: ClusterUpdate{ 1288 ClusterName: v3ClusterName, 1289 EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf, 1290 Raw: v3ClusterAny, 1291 }, 1292 }, 1293 { 1294 name: "v3 cluster with EDS config source self", 1295 resource: v3ClusterAnyWithEDSConfigSourceSelf, 1296 wantName: v3ClusterName, 1297 wantUpdate: ClusterUpdate{ 1298 ClusterName: v3ClusterName, 1299 EDSServiceName: v3Service, LRSServerConfig: ClusterLRSServerSelf, 1300 Raw: v3ClusterAnyWithEDSConfigSourceSelf, 1301 }, 1302 }, 1303 { 1304 name: "xdstp cluster resource with unset EDS service name", 1305 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1306 Name: "xdstp:foo", 1307 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1308 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1309 EdsConfig: &v3corepb.ConfigSource{ 1310 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1311 Ads: &v3corepb.AggregatedConfigSource{}, 1312 }, 1313 }, 1314 ServiceName: "", 1315 }, 1316 }), 1317 wantName: "xdstp:foo", 1318 wantErr: true, 1319 }, 1320 } 1321 for _, test := range tests { 1322 t.Run(test.name, func(t *testing.T) { 1323 name, update, err := unmarshalClusterResource(test.resource) 1324 if (err != nil) != test.wantErr { 1325 t.Fatalf("unmarshalClusterResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) 1326 } 1327 if name != test.wantName { 1328 t.Errorf("unmarshalClusterResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName) 1329 } 1330 if diff := cmp.Diff(update, test.wantUpdate, cmpOpts, cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { 1331 t.Errorf("unmarshalClusterResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff) 1332 } 1333 }) 1334 } 1335 } 1336 1337 func (s) TestValidateClusterWithOutlierDetection(t *testing.T) { 1338 odToClusterProto := func(od *v3clusterpb.OutlierDetection) *v3clusterpb.Cluster { 1339 // Cluster parsing doesn't fail with respect to fields orthogonal to 1340 // outlier detection. 1341 return &v3clusterpb.Cluster{ 1342 Name: clusterName, 1343 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1344 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1345 EdsConfig: &v3corepb.ConfigSource{ 1346 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1347 Ads: &v3corepb.AggregatedConfigSource{}, 1348 }, 1349 }, 1350 }, 1351 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1352 OutlierDetection: od, 1353 } 1354 } 1355 1356 tests := []struct { 1357 name string 1358 cluster *v3clusterpb.Cluster 1359 wantODCfg string 1360 wantErr bool 1361 }{ 1362 { 1363 name: "success-and-failure-null", 1364 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{}), 1365 wantODCfg: `{"successRateEjection": {}}`, 1366 }, 1367 { 1368 name: "success-and-failure-zero", 1369 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1370 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 0}, // Thus doesn't create sre - to focus on fpe 1371 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 0}, 1372 }), 1373 wantODCfg: `{}`, 1374 }, 1375 { 1376 name: "some-fields-set", 1377 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1378 Interval: &durationpb.Duration{Seconds: 1}, 1379 MaxEjectionTime: &durationpb.Duration{Seconds: 3}, 1380 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, 1381 SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, 1382 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, 1383 FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, 1384 }), 1385 wantODCfg: `{ 1386 "interval": "1s", 1387 "maxEjectionTime": "3s", 1388 "successRateEjection": { 1389 "enforcementPercentage": 3, 1390 "requestVolume": 5 1391 }, 1392 "failurePercentageEjection": { 1393 "enforcementPercentage": 7, 1394 "requestVolume": 9 1395 } 1396 }`, 1397 }, 1398 { 1399 name: "every-field-set-non-zero", 1400 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1401 // all fields set (including ones that will be layered) should 1402 // pick up those too and explicitly all fields, including those 1403 // put in layers, in the JSON generated. 1404 Interval: &durationpb.Duration{Seconds: 1}, 1405 BaseEjectionTime: &durationpb.Duration{Seconds: 2}, 1406 MaxEjectionTime: &durationpb.Duration{Seconds: 3}, 1407 MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 1}, 1408 SuccessRateStdevFactor: &wrapperspb.UInt32Value{Value: 2}, 1409 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, 1410 SuccessRateMinimumHosts: &wrapperspb.UInt32Value{Value: 4}, 1411 SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, 1412 FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 6}, 1413 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, 1414 FailurePercentageMinimumHosts: &wrapperspb.UInt32Value{Value: 8}, 1415 FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, 1416 }), 1417 wantODCfg: `{ 1418 "interval": "1s", 1419 "baseEjectionTime": "2s", 1420 "maxEjectionTime": "3s", 1421 "maxEjectionPercent": 1, 1422 "successRateEjection": { 1423 "stdevFactor": 2, 1424 "enforcementPercentage": 3, 1425 "minimumHosts": 4, 1426 "requestVolume": 5 1427 }, 1428 "failurePercentageEjection": { 1429 "threshold": 6, 1430 "enforcementPercentage": 7, 1431 "minimumHosts": 8, 1432 "requestVolume": 9 1433 } 1434 }`, 1435 }, 1436 { 1437 name: "interval-is-negative", 1438 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: -10}}), 1439 wantErr: true, 1440 }, 1441 { 1442 name: "interval-overflows", 1443 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: 315576000001}}), 1444 wantErr: true, 1445 }, 1446 { 1447 name: "base-ejection-time-is-negative", 1448 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: -10}}), 1449 wantErr: true, 1450 }, 1451 { 1452 name: "base-ejection-time-overflows", 1453 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), 1454 wantErr: true, 1455 }, 1456 { 1457 name: "max-ejection-time-is-negative", 1458 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: -10}}), 1459 wantErr: true, 1460 }, 1461 { 1462 name: "max-ejection-time-overflows", 1463 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), 1464 wantErr: true, 1465 }, 1466 { 1467 name: "max-ejection-percent-is-greater-than-100", 1468 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 150}}), 1469 wantErr: true, 1470 }, 1471 { 1472 name: "enforcing-success-rate-is-greater-than-100", 1473 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 150}}), 1474 wantErr: true, 1475 }, 1476 { 1477 name: "failure-percentage-threshold-is-greater-than-100", 1478 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 150}}), 1479 wantErr: true, 1480 }, 1481 { 1482 name: "enforcing-failure-percentage-is-greater-than-100", 1483 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 150}}), 1484 wantErr: true, 1485 }, 1486 // A Outlier Detection proto not present should lead to a nil 1487 // OutlierDetection field in the ClusterUpdate, which is implicitly 1488 // tested in every other test in this file. 1489 } 1490 for _, test := range tests { 1491 t.Run(test.name, func(t *testing.T) { 1492 update, err := validateClusterAndConstructClusterUpdate(test.cluster) 1493 if (err != nil) != test.wantErr { 1494 t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) 1495 } 1496 if test.wantErr { 1497 return 1498 } 1499 // got and want must be unmarshalled since JSON strings shouldn't 1500 // generally be directly compared. 1501 var got map[string]any 1502 if err := json.Unmarshal(update.OutlierDetection, &got); err != nil { 1503 t.Fatalf("Error unmarshalling update.OutlierDetection (%q): %v", update.OutlierDetection, err) 1504 } 1505 var want map[string]any 1506 if err := json.Unmarshal(json.RawMessage(test.wantODCfg), &want); err != nil { 1507 t.Fatalf("Error unmarshalling wantODCfg (%q): %v", test.wantODCfg, err) 1508 } 1509 if diff := cmp.Diff(got, want); diff != "" { 1510 t.Fatalf("cluster.OutlierDetection got unexpected output, diff (-got, +want): %v", diff) 1511 } 1512 }) 1513 } 1514 }