google.golang.org/grpc@v1.72.2/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/envconfig" 29 "google.golang.org/grpc/internal/pretty" 30 "google.golang.org/grpc/internal/testutils" 31 "google.golang.org/grpc/internal/xds/bootstrap" 32 "google.golang.org/grpc/internal/xds/matcher" 33 "google.golang.org/grpc/xds/internal" 34 "google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version" 35 36 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 37 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 38 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 39 v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" 40 v3leastrequestpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/least_request/v3" 41 v3ringhashpb "github.com/envoyproxy/go-control-plane/envoy/extensions/load_balancing_policies/ring_hash/v3" 42 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 43 v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 44 v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" 45 "google.golang.org/protobuf/types/known/anypb" 46 "google.golang.org/protobuf/types/known/durationpb" 47 "google.golang.org/protobuf/types/known/structpb" 48 "google.golang.org/protobuf/types/known/wrapperspb" 49 ) 50 51 const ( 52 clusterName = "clusterName" 53 serviceName = "service" 54 ) 55 56 func (s) TestValidateCluster_Failure(t *testing.T) { 57 tests := []struct { 58 name string 59 cluster *v3clusterpb.Cluster 60 wantErr bool 61 }{ 62 { 63 name: "non-supported-cluster-type-static", 64 cluster: &v3clusterpb.Cluster{ 65 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 66 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 67 EdsConfig: &v3corepb.ConfigSource{ 68 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 69 Ads: &v3corepb.AggregatedConfigSource{}, 70 }, 71 }, 72 }, 73 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 74 }, 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 wantErr: true, 91 }, 92 { 93 name: "no-eds-config", 94 cluster: &v3clusterpb.Cluster{ 95 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 96 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 97 }, 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 wantErr: true, 108 }, 109 { 110 name: "non-round-robin-or-ring-hash-lb-policy", 111 cluster: &v3clusterpb.Cluster{ 112 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 113 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 114 EdsConfig: &v3corepb.ConfigSource{ 115 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 116 Ads: &v3corepb.AggregatedConfigSource{}, 117 }, 118 }, 119 }, 120 LbPolicy: v3clusterpb.Cluster_LEAST_REQUEST, 121 }, 122 wantErr: true, 123 }, 124 { 125 name: "logical-dns-multiple-localities", 126 cluster: &v3clusterpb.Cluster{ 127 Name: clusterName, 128 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS}, 129 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 130 LoadAssignment: &v3endpointpb.ClusterLoadAssignment{ 131 Endpoints: []*v3endpointpb.LocalityLbEndpoints{ 132 // Invalid if there are more than one locality. 133 {LbEndpoints: nil}, 134 {LbEndpoints: nil}, 135 }, 136 }, 137 }, 138 wantErr: true, 139 }, 140 { 141 name: "ring-hash-hash-function-not-xx-hash", 142 cluster: &v3clusterpb.Cluster{ 143 LbPolicy: v3clusterpb.Cluster_RING_HASH, 144 LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ 145 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 146 HashFunction: v3clusterpb.Cluster_RingHashLbConfig_MURMUR_HASH_2, 147 }, 148 }, 149 }, 150 wantErr: true, 151 }, 152 { 153 name: "least-request-choice-count-less-than-two", 154 cluster: &v3clusterpb.Cluster{ 155 LbPolicy: v3clusterpb.Cluster_RING_HASH, 156 LbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig_{ 157 LeastRequestLbConfig: &v3clusterpb.Cluster_LeastRequestLbConfig{ 158 ChoiceCount: wrapperspb.UInt32(1), 159 }, 160 }, 161 }, 162 wantErr: true, 163 }, 164 { 165 name: "ring-hash-max-bound-greater-than-upper-bound", 166 cluster: &v3clusterpb.Cluster{ 167 LbPolicy: v3clusterpb.Cluster_RING_HASH, 168 LbConfig: &v3clusterpb.Cluster_RingHashLbConfig_{ 169 RingHashLbConfig: &v3clusterpb.Cluster_RingHashLbConfig{ 170 MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), 171 }, 172 }, 173 }, 174 wantErr: true, 175 }, 176 { 177 name: "ring-hash-max-bound-greater-than-upper-bound-load-balancing-policy", 178 cluster: &v3clusterpb.Cluster{ 179 Name: clusterName, 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 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 190 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 191 { 192 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 193 TypedConfig: testutils.MarshalAny(t, &v3ringhashpb.RingHash{ 194 HashFunction: v3ringhashpb.RingHash_XX_HASH, 195 MinimumRingSize: wrapperspb.UInt64(10), 196 MaximumRingSize: wrapperspb.UInt64(ringHashSizeUpperBound + 1), 197 }), 198 }, 199 }, 200 }, 201 }, 202 }, 203 wantErr: true, 204 }, 205 { 206 name: "least-request-unsupported-in-converter-since-env-var-unset", 207 cluster: &v3clusterpb.Cluster{ 208 Name: clusterName, 209 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 210 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 211 EdsConfig: &v3corepb.ConfigSource{ 212 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 213 Ads: &v3corepb.AggregatedConfigSource{}, 214 }, 215 }, 216 ServiceName: serviceName, 217 }, 218 LoadBalancingPolicy: &v3clusterpb.LoadBalancingPolicy{ 219 Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{ 220 { 221 TypedExtensionConfig: &v3corepb.TypedExtensionConfig{ 222 TypedConfig: testutils.MarshalAny(t, &v3leastrequestpb.LeastRequest{}), 223 }, 224 }, 225 }, 226 }, 227 }, 228 wantErr: true, 229 }, 230 { 231 name: "aggregate-nil-clusters", 232 cluster: &v3clusterpb.Cluster{ 233 Name: clusterName, 234 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 235 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 236 Name: "envoy.clusters.aggregate", 237 TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{}), 238 }, 239 }, 240 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 241 }, 242 wantErr: true, 243 }, 244 { 245 name: "aggregate-empty-clusters", 246 cluster: &v3clusterpb.Cluster{ 247 Name: clusterName, 248 ClusterDiscoveryType: &v3clusterpb.Cluster_ClusterType{ 249 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 250 Name: "envoy.clusters.aggregate", 251 TypedConfig: testutils.MarshalAny(t, &v3aggregateclusterpb.ClusterConfig{ 252 Clusters: []string{}, 253 }), 254 }, 255 }, 256 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 257 }, 258 wantErr: true, 259 }, 260 } 261 262 for _, test := range tests { 263 t.Run(test.name, func(t *testing.T) { 264 if update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil); err == nil { 265 t.Errorf("validateClusterAndConstructClusterUpdate(%+v) = %v, wanted error", test.cluster, update) 266 } 267 }) 268 } 269 } 270 271 func (s) TestSecurityConfigFromCommonTLSContextUsingNewFields_ErrorCases(t *testing.T) { 272 tests := []struct { 273 name string 274 common *v3tlspb.CommonTlsContext 275 server bool 276 wantErr string 277 enableSystemRootCertsFlag bool 278 }{ 279 { 280 name: "unsupported-tls_certificates-field-for-identity-certs", 281 common: &v3tlspb.CommonTlsContext{ 282 TlsCertificates: []*v3tlspb.TlsCertificate{ 283 {CertificateChain: &v3corepb.DataSource{}}, 284 }, 285 }, 286 wantErr: "unsupported field tls_certificates is set in CommonTlsContext message", 287 }, 288 { 289 name: "unsupported-tls_certificates_sds_secret_configs-field-for-identity-certs", 290 common: &v3tlspb.CommonTlsContext{ 291 TlsCertificateSdsSecretConfigs: []*v3tlspb.SdsSecretConfig{ 292 {Name: "sds-secrets-config"}, 293 }, 294 }, 295 wantErr: "unsupported field tls_certificate_sds_secret_configs is set in CommonTlsContext message", 296 }, 297 { 298 name: "unsupported-sds-validation-context", 299 common: &v3tlspb.CommonTlsContext{ 300 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 301 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 302 Name: "foo-sds-secret", 303 }, 304 }, 305 }, 306 wantErr: "validation context contains unexpected type", 307 }, 308 { 309 name: "missing-ca_certificate_provider_instance-in-validation-context", 310 common: &v3tlspb.CommonTlsContext{ 311 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 312 ValidationContext: &v3tlspb.CertificateValidationContext{}, 313 }, 314 }, 315 wantErr: "expected field ca_certificate_provider_instance is missing in CommonTlsContext message", 316 }, 317 { 318 name: "unsupported-field-verify_certificate_spki-in-validation-context", 319 common: &v3tlspb.CommonTlsContext{ 320 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 321 ValidationContext: &v3tlspb.CertificateValidationContext{ 322 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 323 InstanceName: "rootPluginInstance", 324 CertificateName: "rootCertName", 325 }, 326 VerifyCertificateSpki: []string{"spki"}, 327 }, 328 }, 329 }, 330 wantErr: "unsupported verify_certificate_spki field in CommonTlsContext message", 331 }, 332 { 333 name: "unsupported-field-verify_certificate_hash-in-validation-context", 334 common: &v3tlspb.CommonTlsContext{ 335 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 336 ValidationContext: &v3tlspb.CertificateValidationContext{ 337 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 338 InstanceName: "rootPluginInstance", 339 CertificateName: "rootCertName", 340 }, 341 VerifyCertificateHash: []string{"hash"}, 342 }, 343 }, 344 }, 345 wantErr: "unsupported verify_certificate_hash field in CommonTlsContext message", 346 }, 347 { 348 name: "unsupported-field-require_signed_certificate_timestamp-in-validation-context", 349 common: &v3tlspb.CommonTlsContext{ 350 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 351 ValidationContext: &v3tlspb.CertificateValidationContext{ 352 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 353 InstanceName: "rootPluginInstance", 354 CertificateName: "rootCertName", 355 }, 356 RequireSignedCertificateTimestamp: &wrapperspb.BoolValue{Value: true}, 357 }, 358 }, 359 }, 360 wantErr: "unsupported require_signed_certificate_timestamp field in CommonTlsContext message", 361 }, 362 { 363 name: "unsupported-field-crl-in-validation-context", 364 common: &v3tlspb.CommonTlsContext{ 365 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 366 ValidationContext: &v3tlspb.CertificateValidationContext{ 367 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 368 InstanceName: "rootPluginInstance", 369 CertificateName: "rootCertName", 370 }, 371 Crl: &v3corepb.DataSource{}, 372 }, 373 }, 374 }, 375 wantErr: "unsupported crl field in CommonTlsContext message", 376 }, 377 { 378 name: "unsupported-field-custom_validator_config-in-validation-context", 379 common: &v3tlspb.CommonTlsContext{ 380 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 381 ValidationContext: &v3tlspb.CertificateValidationContext{ 382 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 383 InstanceName: "rootPluginInstance", 384 CertificateName: "rootCertName", 385 }, 386 CustomValidatorConfig: &v3corepb.TypedExtensionConfig{}, 387 }, 388 }, 389 }, 390 wantErr: "unsupported custom_validator_config field in CommonTlsContext message", 391 }, 392 { 393 name: "invalid-match_subject_alt_names-field-in-validation-context", 394 common: &v3tlspb.CommonTlsContext{ 395 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 396 ValidationContext: &v3tlspb.CertificateValidationContext{ 397 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 398 InstanceName: "rootPluginInstance", 399 CertificateName: "rootCertName", 400 }, 401 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 402 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, 403 }, 404 }, 405 }, 406 }, 407 wantErr: "empty prefix is not allowed in StringMatcher", 408 }, 409 { 410 name: "unsupported-field-matching-subject-alt-names-in-validation-context-of-server", 411 common: &v3tlspb.CommonTlsContext{ 412 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 413 ValidationContext: &v3tlspb.CertificateValidationContext{ 414 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 415 InstanceName: "rootPluginInstance", 416 CertificateName: "rootCertName", 417 }, 418 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 419 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "sanPrefix"}}, 420 }, 421 }, 422 }, 423 }, 424 server: true, 425 wantErr: "match_subject_alt_names field in validation context is not supported on the server", 426 }, 427 { 428 name: "client-missing-root-cert-provider-and-use-system-certs-fields", 429 enableSystemRootCertsFlag: true, 430 common: &v3tlspb.CommonTlsContext{ 431 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 432 ValidationContext: &v3tlspb.CertificateValidationContext{}, 433 }, 434 }, 435 wantErr: "expected fields ca_certificate_provider_instance and system_root_certs are missing", 436 }, 437 { 438 name: "server-missing-root-cert-provider-and-use-system-certs-fields", 439 enableSystemRootCertsFlag: true, 440 common: &v3tlspb.CommonTlsContext{ 441 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 442 ValidationContext: &v3tlspb.CertificateValidationContext{}, 443 }, 444 }, 445 server: true, 446 wantErr: "expected field ca_certificate_provider_instance is missing", 447 }, 448 { 449 name: "client-missing-root-cert-provider-and-use-system-certs-fields-env-var-unset", 450 common: &v3tlspb.CommonTlsContext{ 451 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 452 ValidationContext: &v3tlspb.CertificateValidationContext{}, 453 }, 454 }, 455 wantErr: "expected field ca_certificate_provider_instance is missing", 456 }, 457 { 458 name: "server-missing-root-cert-provider-and-use-system-certs-fields-env-var-unset", 459 common: &v3tlspb.CommonTlsContext{ 460 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 461 ValidationContext: &v3tlspb.CertificateValidationContext{}, 462 }, 463 }, 464 server: true, 465 wantErr: "expected field ca_certificate_provider_instance is missing", 466 }, 467 { 468 name: "server-missing-root-cert-provider-and-set-use-system-certs-fields", 469 enableSystemRootCertsFlag: true, 470 common: &v3tlspb.CommonTlsContext{ 471 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 472 ValidationContext: &v3tlspb.CertificateValidationContext{ 473 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 474 }, 475 }, 476 }, 477 server: true, 478 wantErr: "expected field ca_certificate_provider_instance is missing and unexpected field system_root_certs is set", 479 }, 480 } 481 482 for _, test := range tests { 483 t.Run(test.name, func(t *testing.T) { 484 origFlag := envconfig.XDSSystemRootCertsEnabled 485 defer func() { 486 envconfig.XDSSystemRootCertsEnabled = origFlag 487 }() 488 envconfig.XDSSystemRootCertsEnabled = test.enableSystemRootCertsFlag 489 _, err := securityConfigFromCommonTLSContextUsingNewFields(test.common, test.server) 490 if err == nil { 491 t.Fatal("securityConfigFromCommonTLSContextUsingNewFields() succeeded when expected to fail") 492 } 493 if !strings.Contains(err.Error(), test.wantErr) { 494 t.Fatalf("securityConfigFromCommonTLSContextUsingNewFields() returned err: %v, wantErr: %v", err, test.wantErr) 495 } 496 }) 497 } 498 } 499 500 func (s) TestValidateClusterWithSecurityConfig(t *testing.T) { 501 const ( 502 identityPluginInstance = "identityPluginInstance" 503 identityCertName = "identityCert" 504 rootPluginInstance = "rootPluginInstance" 505 rootCertName = "rootCert" 506 clusterName = "cluster" 507 serviceName = "service" 508 sanExact = "san-exact" 509 sanPrefix = "san-prefix" 510 sanSuffix = "san-suffix" 511 sanRegexBad = "??" 512 sanRegexGood = "san?regex?" 513 sanContains = "san-contains" 514 ) 515 var sanRE = regexp.MustCompile(sanRegexGood) 516 517 tests := []struct { 518 name string 519 cluster *v3clusterpb.Cluster 520 wantUpdate ClusterUpdate 521 wantErr bool 522 enableSystemRootCertsFlag bool 523 }{ 524 { 525 name: "transport-socket-matches", 526 cluster: &v3clusterpb.Cluster{ 527 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 528 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 529 EdsConfig: &v3corepb.ConfigSource{ 530 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 531 Ads: &v3corepb.AggregatedConfigSource{}, 532 }, 533 }, 534 ServiceName: serviceName, 535 }, 536 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 537 TransportSocketMatches: []*v3clusterpb.Cluster_TransportSocketMatch{ 538 {Name: "transport-socket-match-1"}, 539 }, 540 }, 541 wantErr: true, 542 }, 543 { 544 name: "transport-socket-unsupported-name", 545 cluster: &v3clusterpb.Cluster{ 546 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 547 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 548 EdsConfig: &v3corepb.ConfigSource{ 549 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 550 Ads: &v3corepb.AggregatedConfigSource{}, 551 }, 552 }, 553 ServiceName: serviceName, 554 }, 555 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 556 TransportSocket: &v3corepb.TransportSocket{ 557 Name: "unsupported-foo", 558 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 559 TypedConfig: &anypb.Any{ 560 TypeUrl: version.V3UpstreamTLSContextURL, 561 }, 562 }, 563 }, 564 }, 565 wantErr: true, 566 }, 567 { 568 name: "transport-socket-unsupported-typeURL", 569 cluster: &v3clusterpb.Cluster{ 570 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 571 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 572 EdsConfig: &v3corepb.ConfigSource{ 573 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 574 Ads: &v3corepb.AggregatedConfigSource{}, 575 }, 576 }, 577 ServiceName: serviceName, 578 }, 579 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 580 TransportSocket: &v3corepb.TransportSocket{ 581 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 582 TypedConfig: &anypb.Any{ 583 TypeUrl: version.V3HTTPConnManagerURL, 584 }, 585 }, 586 }, 587 }, 588 wantErr: true, 589 }, 590 { 591 name: "transport-socket-unsupported-type", 592 cluster: &v3clusterpb.Cluster{ 593 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 594 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 595 EdsConfig: &v3corepb.ConfigSource{ 596 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 597 Ads: &v3corepb.AggregatedConfigSource{}, 598 }, 599 }, 600 ServiceName: serviceName, 601 }, 602 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 603 TransportSocket: &v3corepb.TransportSocket{ 604 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 605 TypedConfig: &anypb.Any{ 606 TypeUrl: version.V3UpstreamTLSContextURL, 607 Value: []byte{1, 2, 3, 4}, 608 }, 609 }, 610 }, 611 }, 612 wantErr: true, 613 }, 614 { 615 name: "transport-socket-unsupported-tls-params-field", 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 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 629 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 630 CommonTlsContext: &v3tlspb.CommonTlsContext{ 631 TlsParams: &v3tlspb.TlsParameters{}, 632 }, 633 }), 634 }, 635 }, 636 }, 637 wantErr: true, 638 }, 639 { 640 name: "transport-socket-unsupported-custom-handshaker-field", 641 cluster: &v3clusterpb.Cluster{ 642 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 643 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 644 EdsConfig: &v3corepb.ConfigSource{ 645 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 646 Ads: &v3corepb.AggregatedConfigSource{}, 647 }, 648 }, 649 ServiceName: serviceName, 650 }, 651 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 652 TransportSocket: &v3corepb.TransportSocket{ 653 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 654 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 655 CommonTlsContext: &v3tlspb.CommonTlsContext{ 656 CustomHandshaker: &v3corepb.TypedExtensionConfig{}, 657 }, 658 }), 659 }, 660 }, 661 }, 662 wantErr: true, 663 }, 664 { 665 name: "transport-socket-unsupported-validation-context", 666 cluster: &v3clusterpb.Cluster{ 667 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 668 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 669 EdsConfig: &v3corepb.ConfigSource{ 670 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 671 Ads: &v3corepb.AggregatedConfigSource{}, 672 }, 673 }, 674 ServiceName: serviceName, 675 }, 676 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 677 TransportSocket: &v3corepb.TransportSocket{ 678 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 679 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 680 CommonTlsContext: &v3tlspb.CommonTlsContext{ 681 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 682 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 683 Name: "foo-sds-secret", 684 }, 685 }, 686 }, 687 }), 688 }, 689 }, 690 }, 691 wantErr: true, 692 }, 693 { 694 name: "transport-socket-without-validation-context", 695 cluster: &v3clusterpb.Cluster{ 696 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 697 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 698 EdsConfig: &v3corepb.ConfigSource{ 699 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 700 Ads: &v3corepb.AggregatedConfigSource{}, 701 }, 702 }, 703 ServiceName: serviceName, 704 }, 705 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 706 TransportSocket: &v3corepb.TransportSocket{ 707 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 708 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 709 CommonTlsContext: &v3tlspb.CommonTlsContext{}, 710 }), 711 }, 712 }, 713 }, 714 wantErr: true, 715 }, 716 { 717 name: "empty-prefix-in-matching-SAN", 718 cluster: &v3clusterpb.Cluster{ 719 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 720 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 721 EdsConfig: &v3corepb.ConfigSource{ 722 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 723 Ads: &v3corepb.AggregatedConfigSource{}, 724 }, 725 }, 726 ServiceName: serviceName, 727 }, 728 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 729 TransportSocket: &v3corepb.TransportSocket{ 730 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 731 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 732 CommonTlsContext: &v3tlspb.CommonTlsContext{ 733 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 734 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 735 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 736 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 737 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: ""}}, 738 }, 739 }, 740 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 741 InstanceName: rootPluginInstance, 742 CertificateName: rootCertName, 743 }, 744 }, 745 }, 746 }, 747 }), 748 }, 749 }, 750 }, 751 wantErr: true, 752 }, 753 { 754 name: "empty-suffix-in-matching-SAN", 755 cluster: &v3clusterpb.Cluster{ 756 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 757 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 758 EdsConfig: &v3corepb.ConfigSource{ 759 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 760 Ads: &v3corepb.AggregatedConfigSource{}, 761 }, 762 }, 763 ServiceName: serviceName, 764 }, 765 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 766 TransportSocket: &v3corepb.TransportSocket{ 767 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 768 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 769 CommonTlsContext: &v3tlspb.CommonTlsContext{ 770 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 771 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 772 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 773 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 774 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""}}, 775 }, 776 }, 777 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 778 InstanceName: rootPluginInstance, 779 CertificateName: rootCertName, 780 }, 781 }, 782 }, 783 }, 784 }), 785 }, 786 }, 787 }, 788 wantErr: true, 789 }, 790 { 791 name: "empty-contains-in-matching-SAN", 792 cluster: &v3clusterpb.Cluster{ 793 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 794 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 795 EdsConfig: &v3corepb.ConfigSource{ 796 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 797 Ads: &v3corepb.AggregatedConfigSource{}, 798 }, 799 }, 800 ServiceName: serviceName, 801 }, 802 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 803 TransportSocket: &v3corepb.TransportSocket{ 804 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 805 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 806 CommonTlsContext: &v3tlspb.CommonTlsContext{ 807 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 808 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 809 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 810 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 811 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: ""}}, 812 }, 813 }, 814 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 815 InstanceName: rootPluginInstance, 816 CertificateName: rootCertName, 817 }, 818 }, 819 }, 820 }, 821 }), 822 }, 823 }, 824 }, 825 wantErr: true, 826 }, 827 { 828 name: "invalid-regex-in-matching-SAN", 829 cluster: &v3clusterpb.Cluster{ 830 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 831 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 832 EdsConfig: &v3corepb.ConfigSource{ 833 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 834 Ads: &v3corepb.AggregatedConfigSource{}, 835 }, 836 }, 837 ServiceName: serviceName, 838 }, 839 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 840 TransportSocket: &v3corepb.TransportSocket{ 841 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 842 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 843 CommonTlsContext: &v3tlspb.CommonTlsContext{ 844 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 845 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 846 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 847 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 848 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, 849 }, 850 }, 851 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 852 InstanceName: rootPluginInstance, 853 CertificateName: rootCertName, 854 }, 855 }, 856 }, 857 }, 858 }), 859 }, 860 }, 861 }, 862 wantErr: true, 863 }, 864 { 865 name: "invalid-regex-in-matching-SAN-with-new-fields", 866 cluster: &v3clusterpb.Cluster{ 867 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 868 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 869 EdsConfig: &v3corepb.ConfigSource{ 870 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 871 Ads: &v3corepb.AggregatedConfigSource{}, 872 }, 873 }, 874 ServiceName: serviceName, 875 }, 876 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 877 TransportSocket: &v3corepb.TransportSocket{ 878 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 879 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 880 CommonTlsContext: &v3tlspb.CommonTlsContext{ 881 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 882 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 883 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 884 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 885 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexBad}}}, 886 }, 887 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 888 InstanceName: rootPluginInstance, 889 CertificateName: rootCertName, 890 }, 891 }, 892 }, 893 }, 894 }, 895 }), 896 }, 897 }, 898 }, 899 wantErr: true, 900 }, 901 { 902 name: "happy-case-with-no-identity-certs-using-deprecated-fields", 903 cluster: &v3clusterpb.Cluster{ 904 Name: clusterName, 905 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 906 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 907 EdsConfig: &v3corepb.ConfigSource{ 908 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 909 Ads: &v3corepb.AggregatedConfigSource{}, 910 }, 911 }, 912 ServiceName: serviceName, 913 }, 914 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 915 TransportSocket: &v3corepb.TransportSocket{ 916 Name: "envoy.transport_sockets.tls", 917 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 918 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 919 CommonTlsContext: &v3tlspb.CommonTlsContext{ 920 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 921 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 922 InstanceName: rootPluginInstance, 923 CertificateName: rootCertName, 924 }, 925 }, 926 }, 927 }), 928 }, 929 }, 930 }, 931 wantUpdate: ClusterUpdate{ 932 ClusterName: clusterName, 933 EDSServiceName: serviceName, 934 SecurityCfg: &SecurityConfig{ 935 RootInstanceName: rootPluginInstance, 936 RootCertName: rootCertName, 937 }, 938 TelemetryLabels: internal.UnknownCSMLabels, 939 }, 940 }, 941 { 942 name: "happy-case-with-no-identity-certs-using-new-fields", 943 cluster: &v3clusterpb.Cluster{ 944 Name: clusterName, 945 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 946 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 947 EdsConfig: &v3corepb.ConfigSource{ 948 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 949 Ads: &v3corepb.AggregatedConfigSource{}, 950 }, 951 }, 952 ServiceName: serviceName, 953 }, 954 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 955 TransportSocket: &v3corepb.TransportSocket{ 956 Name: "envoy.transport_sockets.tls", 957 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 958 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 959 CommonTlsContext: &v3tlspb.CommonTlsContext{ 960 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 961 ValidationContext: &v3tlspb.CertificateValidationContext{ 962 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 963 InstanceName: rootPluginInstance, 964 CertificateName: rootCertName, 965 }, 966 }, 967 }, 968 }, 969 }), 970 }, 971 }, 972 }, 973 wantUpdate: ClusterUpdate{ 974 ClusterName: clusterName, 975 EDSServiceName: serviceName, 976 SecurityCfg: &SecurityConfig{ 977 RootInstanceName: rootPluginInstance, 978 RootCertName: rootCertName, 979 }, 980 TelemetryLabels: internal.UnknownCSMLabels, 981 }, 982 }, 983 { 984 name: "happy-case-with-validation-context-provider-instance-using-deprecated-fields", 985 cluster: &v3clusterpb.Cluster{ 986 Name: clusterName, 987 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 988 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 989 EdsConfig: &v3corepb.ConfigSource{ 990 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 991 Ads: &v3corepb.AggregatedConfigSource{}, 992 }, 993 }, 994 ServiceName: serviceName, 995 }, 996 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 997 TransportSocket: &v3corepb.TransportSocket{ 998 Name: "envoy.transport_sockets.tls", 999 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1000 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1001 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1002 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1003 InstanceName: identityPluginInstance, 1004 CertificateName: identityCertName, 1005 }, 1006 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 1007 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1008 InstanceName: rootPluginInstance, 1009 CertificateName: rootCertName, 1010 }, 1011 }, 1012 }, 1013 }), 1014 }, 1015 }, 1016 }, 1017 wantUpdate: ClusterUpdate{ 1018 ClusterName: clusterName, 1019 EDSServiceName: serviceName, 1020 SecurityCfg: &SecurityConfig{ 1021 RootInstanceName: rootPluginInstance, 1022 RootCertName: rootCertName, 1023 IdentityInstanceName: identityPluginInstance, 1024 IdentityCertName: identityCertName, 1025 }, 1026 TelemetryLabels: internal.UnknownCSMLabels, 1027 }, 1028 }, 1029 { 1030 name: "happy-case-with-validation-context-provider-instance-using-new-fields", 1031 enableSystemRootCertsFlag: true, 1032 cluster: &v3clusterpb.Cluster{ 1033 Name: clusterName, 1034 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1035 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1036 EdsConfig: &v3corepb.ConfigSource{ 1037 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1038 Ads: &v3corepb.AggregatedConfigSource{}, 1039 }, 1040 }, 1041 ServiceName: serviceName, 1042 }, 1043 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1044 TransportSocket: &v3corepb.TransportSocket{ 1045 Name: "envoy.transport_sockets.tls", 1046 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1047 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1048 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1049 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1050 InstanceName: identityPluginInstance, 1051 CertificateName: identityCertName, 1052 }, 1053 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 1054 ValidationContext: &v3tlspb.CertificateValidationContext{ 1055 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1056 InstanceName: rootPluginInstance, 1057 CertificateName: rootCertName, 1058 }, 1059 // SystemRootCerts will be ignored due 1060 // to the presence of 1061 // CaCertificateProviderInstance. 1062 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 1063 }, 1064 }, 1065 }, 1066 }), 1067 }, 1068 }, 1069 }, 1070 wantUpdate: ClusterUpdate{ 1071 ClusterName: clusterName, 1072 EDSServiceName: serviceName, 1073 SecurityCfg: &SecurityConfig{ 1074 RootInstanceName: rootPluginInstance, 1075 RootCertName: rootCertName, 1076 IdentityInstanceName: identityPluginInstance, 1077 IdentityCertName: identityCertName, 1078 }, 1079 TelemetryLabels: internal.UnknownCSMLabels, 1080 }, 1081 }, 1082 { 1083 name: "happy-case-with-validation-context-provider-instance-using-new-fields-and-system-root-certs", 1084 enableSystemRootCertsFlag: true, 1085 cluster: &v3clusterpb.Cluster{ 1086 Name: clusterName, 1087 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1088 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1089 EdsConfig: &v3corepb.ConfigSource{ 1090 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1091 Ads: &v3corepb.AggregatedConfigSource{}, 1092 }, 1093 }, 1094 ServiceName: serviceName, 1095 }, 1096 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1097 TransportSocket: &v3corepb.TransportSocket{ 1098 Name: "envoy.transport_sockets.tls", 1099 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1100 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1101 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1102 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1103 InstanceName: identityPluginInstance, 1104 CertificateName: identityCertName, 1105 }, 1106 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 1107 ValidationContext: &v3tlspb.CertificateValidationContext{ 1108 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 1109 }, 1110 }, 1111 }, 1112 }), 1113 }, 1114 }, 1115 }, 1116 wantUpdate: ClusterUpdate{ 1117 ClusterName: clusterName, 1118 EDSServiceName: serviceName, 1119 SecurityCfg: &SecurityConfig{ 1120 UseSystemRootCerts: true, 1121 IdentityInstanceName: identityPluginInstance, 1122 IdentityCertName: identityCertName, 1123 }, 1124 TelemetryLabels: internal.UnknownCSMLabels, 1125 }, 1126 }, 1127 { 1128 name: "failure-case-with-validation-context-provider-instance-using-new-fields-and-system-root-certs-env-flag-disabled", 1129 cluster: &v3clusterpb.Cluster{ 1130 Name: clusterName, 1131 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1132 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1133 EdsConfig: &v3corepb.ConfigSource{ 1134 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1135 Ads: &v3corepb.AggregatedConfigSource{}, 1136 }, 1137 }, 1138 ServiceName: serviceName, 1139 }, 1140 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1141 TransportSocket: &v3corepb.TransportSocket{ 1142 Name: "envoy.transport_sockets.tls", 1143 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1144 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1145 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1146 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1147 InstanceName: identityPluginInstance, 1148 CertificateName: identityCertName, 1149 }, 1150 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 1151 ValidationContext: &v3tlspb.CertificateValidationContext{ 1152 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 1153 }, 1154 }, 1155 }, 1156 }), 1157 }, 1158 }, 1159 }, 1160 wantErr: true, 1161 }, 1162 { 1163 name: "happy-case-with-combined-validation-context-using-deprecated-fields", 1164 cluster: &v3clusterpb.Cluster{ 1165 Name: clusterName, 1166 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1167 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1168 EdsConfig: &v3corepb.ConfigSource{ 1169 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1170 Ads: &v3corepb.AggregatedConfigSource{}, 1171 }, 1172 }, 1173 ServiceName: serviceName, 1174 }, 1175 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1176 TransportSocket: &v3corepb.TransportSocket{ 1177 Name: "envoy.transport_sockets.tls", 1178 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1179 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1180 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1181 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1182 InstanceName: identityPluginInstance, 1183 CertificateName: identityCertName, 1184 }, 1185 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 1186 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 1187 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 1188 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 1189 { 1190 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 1191 IgnoreCase: true, 1192 }, 1193 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 1194 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 1195 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 1196 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 1197 }, 1198 }, 1199 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1200 InstanceName: rootPluginInstance, 1201 CertificateName: rootCertName, 1202 }, 1203 }, 1204 }, 1205 }, 1206 }), 1207 }, 1208 }, 1209 }, 1210 wantUpdate: ClusterUpdate{ 1211 ClusterName: clusterName, 1212 EDSServiceName: serviceName, 1213 SecurityCfg: &SecurityConfig{ 1214 RootInstanceName: rootPluginInstance, 1215 RootCertName: rootCertName, 1216 IdentityInstanceName: identityPluginInstance, 1217 IdentityCertName: identityCertName, 1218 SubjectAltNameMatchers: []matcher.StringMatcher{ 1219 matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 1220 matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 1221 matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 1222 matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 1223 matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 1224 }, 1225 }, 1226 TelemetryLabels: internal.UnknownCSMLabels, 1227 }, 1228 }, 1229 { 1230 name: "happy-case-with-combined-validation-context-using-new-fields", 1231 cluster: &v3clusterpb.Cluster{ 1232 Name: clusterName, 1233 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1234 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1235 EdsConfig: &v3corepb.ConfigSource{ 1236 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1237 Ads: &v3corepb.AggregatedConfigSource{}, 1238 }, 1239 }, 1240 ServiceName: serviceName, 1241 }, 1242 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1243 TransportSocket: &v3corepb.TransportSocket{ 1244 Name: "envoy.transport_sockets.tls", 1245 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1246 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1247 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1248 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1249 InstanceName: identityPluginInstance, 1250 CertificateName: identityCertName, 1251 }, 1252 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 1253 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 1254 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 1255 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 1256 { 1257 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 1258 IgnoreCase: true, 1259 }, 1260 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 1261 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 1262 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 1263 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 1264 }, 1265 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1266 InstanceName: rootPluginInstance, 1267 CertificateName: rootCertName, 1268 }, 1269 }, 1270 }, 1271 }, 1272 }, 1273 }), 1274 }, 1275 }, 1276 }, 1277 wantUpdate: ClusterUpdate{ 1278 ClusterName: clusterName, 1279 EDSServiceName: serviceName, 1280 SecurityCfg: &SecurityConfig{ 1281 RootInstanceName: rootPluginInstance, 1282 RootCertName: rootCertName, 1283 IdentityInstanceName: identityPluginInstance, 1284 IdentityCertName: identityCertName, 1285 SubjectAltNameMatchers: []matcher.StringMatcher{ 1286 matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 1287 matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 1288 matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 1289 matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 1290 matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 1291 }, 1292 }, 1293 TelemetryLabels: internal.UnknownCSMLabels, 1294 }, 1295 }, 1296 { 1297 name: "happy-case-with-combined-validation-context-using-new-fields-and-system-root-certs", 1298 enableSystemRootCertsFlag: true, 1299 cluster: &v3clusterpb.Cluster{ 1300 Name: clusterName, 1301 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1302 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1303 EdsConfig: &v3corepb.ConfigSource{ 1304 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1305 Ads: &v3corepb.AggregatedConfigSource{}, 1306 }, 1307 }, 1308 ServiceName: serviceName, 1309 }, 1310 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1311 TransportSocket: &v3corepb.TransportSocket{ 1312 Name: "envoy.transport_sockets.tls", 1313 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1314 TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{ 1315 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1316 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1317 InstanceName: identityPluginInstance, 1318 CertificateName: identityCertName, 1319 }, 1320 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 1321 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 1322 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 1323 MatchSubjectAltNames: []*v3matcherpb.StringMatcher{ 1324 { 1325 MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: sanExact}, 1326 IgnoreCase: true, 1327 }, 1328 {MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: sanPrefix}}, 1329 {MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: sanSuffix}}, 1330 {MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: sanRegexGood}}}, 1331 {MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: sanContains}}, 1332 }, 1333 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 1334 }, 1335 }, 1336 }, 1337 }, 1338 }), 1339 }, 1340 }, 1341 }, 1342 wantUpdate: ClusterUpdate{ 1343 ClusterName: clusterName, 1344 EDSServiceName: serviceName, 1345 SecurityCfg: &SecurityConfig{ 1346 UseSystemRootCerts: true, 1347 IdentityInstanceName: identityPluginInstance, 1348 IdentityCertName: identityCertName, 1349 SubjectAltNameMatchers: []matcher.StringMatcher{ 1350 matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true), 1351 matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false), 1352 matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false), 1353 matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false), 1354 matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false), 1355 }, 1356 }, 1357 TelemetryLabels: internal.UnknownCSMLabels, 1358 }, 1359 }, 1360 } 1361 1362 for _, test := range tests { 1363 t.Run(test.name, func(t *testing.T) { 1364 origFlag := envconfig.XDSSystemRootCertsEnabled 1365 defer func() { 1366 envconfig.XDSSystemRootCertsEnabled = origFlag 1367 }() 1368 envconfig.XDSSystemRootCertsEnabled = test.enableSystemRootCertsFlag 1369 update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil) 1370 if (err != nil) != test.wantErr { 1371 t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) 1372 } 1373 if diff := cmp.Diff(test.wantUpdate, update, cmpopts.EquateEmpty(), cmp.AllowUnexported(regexp.Regexp{}), cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { 1374 t.Errorf("validateClusterAndConstructClusterUpdate() returned unexpected diff (-want, +got):\n%s", diff) 1375 } 1376 }) 1377 } 1378 } 1379 1380 func (s) TestUnmarshalCluster(t *testing.T) { 1381 const ( 1382 v3ClusterName = "v3clusterName" 1383 v3Service = "v3Service" 1384 ) 1385 var ( 1386 v3ClusterAny = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1387 Name: v3ClusterName, 1388 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1389 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1390 EdsConfig: &v3corepb.ConfigSource{ 1391 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1392 Ads: &v3corepb.AggregatedConfigSource{}, 1393 }, 1394 }, 1395 ServiceName: v3Service, 1396 }, 1397 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1398 LrsServer: &v3corepb.ConfigSource{ 1399 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1400 Self: &v3corepb.SelfConfigSource{}, 1401 }, 1402 }, 1403 }) 1404 1405 v3ClusterAnyWithEDSConfigSourceSelf = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1406 Name: v3ClusterName, 1407 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1408 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1409 EdsConfig: &v3corepb.ConfigSource{ 1410 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{}, 1411 }, 1412 ServiceName: v3Service, 1413 }, 1414 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1415 LrsServer: &v3corepb.ConfigSource{ 1416 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1417 Self: &v3corepb.SelfConfigSource{}, 1418 }, 1419 }, 1420 }) 1421 1422 v3ClusterAnyWithTelemetryLabels = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1423 Name: v3ClusterName, 1424 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1425 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1426 EdsConfig: &v3corepb.ConfigSource{ 1427 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1428 Ads: &v3corepb.AggregatedConfigSource{}, 1429 }, 1430 }, 1431 ServiceName: v3Service, 1432 }, 1433 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1434 LrsServer: &v3corepb.ConfigSource{ 1435 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1436 Self: &v3corepb.SelfConfigSource{}, 1437 }, 1438 }, 1439 Metadata: &v3corepb.Metadata{ 1440 FilterMetadata: map[string]*structpb.Struct{ 1441 "com.google.csm.telemetry_labels": { 1442 Fields: map[string]*structpb.Value{ 1443 "service_name": structpb.NewStringValue("grpc-service"), 1444 "service_namespace": structpb.NewStringValue("grpc-service-namespace"), 1445 }, 1446 }, 1447 }, 1448 }, 1449 }) 1450 v3ClusterAnyWithTelemetryLabelsIgnoreSome = testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1451 Name: v3ClusterName, 1452 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1453 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1454 EdsConfig: &v3corepb.ConfigSource{ 1455 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1456 Ads: &v3corepb.AggregatedConfigSource{}, 1457 }, 1458 }, 1459 ServiceName: v3Service, 1460 }, 1461 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1462 LrsServer: &v3corepb.ConfigSource{ 1463 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 1464 Self: &v3corepb.SelfConfigSource{}, 1465 }, 1466 }, 1467 Metadata: &v3corepb.Metadata{ 1468 FilterMetadata: map[string]*structpb.Struct{ 1469 "com.google.csm.telemetry_labels": { 1470 Fields: map[string]*structpb.Value{ 1471 "string-value-should-ignore": structpb.NewStringValue("string-val"), 1472 "float-value-ignore": structpb.NewNumberValue(3), 1473 "bool-value-ignore": structpb.NewBoolValue(false), 1474 "service_name": structpb.NewStringValue("grpc-service"), // shouldn't ignore 1475 "service_namespace": structpb.NewNullValue(), // should ignore - wrong type 1476 }, 1477 }, 1478 "ignore-this-metadata": { // should ignore this filter_metadata 1479 Fields: map[string]*structpb.Value{ 1480 "service_namespace": structpb.NewStringValue("string-val-should-ignore"), 1481 }, 1482 }, 1483 }, 1484 }, 1485 }) 1486 ) 1487 1488 serverCfg, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{URI: "test-server"}) 1489 if err != nil { 1490 t.Fatalf("Failed to create server config for testing: %v", err) 1491 } 1492 1493 tests := []struct { 1494 name string 1495 resource *anypb.Any 1496 serverCfg *bootstrap.ServerConfig 1497 wantName string 1498 wantUpdate ClusterUpdate 1499 wantErr bool 1500 }{ 1501 { 1502 name: "non-cluster resource type", 1503 resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL}, 1504 wantErr: true, 1505 }, 1506 { 1507 name: "badly marshaled cluster resource", 1508 resource: &anypb.Any{ 1509 TypeUrl: version.V3ClusterURL, 1510 Value: []byte{1, 2, 3, 4}, 1511 }, 1512 wantErr: true, 1513 }, 1514 { 1515 name: "bad cluster resource", 1516 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1517 Name: "test", 1518 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_STATIC}, 1519 }), 1520 wantName: "test", 1521 wantErr: true, 1522 }, 1523 { 1524 name: "cluster resource with non-self lrs_server field", 1525 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1526 Name: "test", 1527 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1528 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1529 EdsConfig: &v3corepb.ConfigSource{ 1530 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1531 Ads: &v3corepb.AggregatedConfigSource{}, 1532 }, 1533 }, 1534 ServiceName: v3Service, 1535 }, 1536 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1537 LrsServer: &v3corepb.ConfigSource{ 1538 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1539 Ads: &v3corepb.AggregatedConfigSource{}, 1540 }, 1541 }, 1542 }), 1543 wantName: "test", 1544 wantErr: true, 1545 }, 1546 { 1547 name: "v3 cluster", 1548 resource: v3ClusterAny, 1549 serverCfg: serverCfg, 1550 wantName: v3ClusterName, 1551 wantUpdate: ClusterUpdate{ 1552 ClusterName: v3ClusterName, 1553 EDSServiceName: v3Service, 1554 LRSServerConfig: serverCfg, 1555 Raw: v3ClusterAny, 1556 TelemetryLabels: internal.UnknownCSMLabels, 1557 }, 1558 }, 1559 { 1560 name: "v3 cluster wrapped", 1561 resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3ClusterAny}), 1562 serverCfg: serverCfg, 1563 wantName: v3ClusterName, 1564 wantUpdate: ClusterUpdate{ 1565 ClusterName: v3ClusterName, 1566 EDSServiceName: v3Service, 1567 LRSServerConfig: serverCfg, 1568 Raw: v3ClusterAny, 1569 TelemetryLabels: internal.UnknownCSMLabels, 1570 }, 1571 }, 1572 { 1573 name: "v3 cluster with EDS config source self", 1574 resource: v3ClusterAnyWithEDSConfigSourceSelf, 1575 serverCfg: serverCfg, 1576 wantName: v3ClusterName, 1577 wantUpdate: ClusterUpdate{ 1578 ClusterName: v3ClusterName, 1579 EDSServiceName: v3Service, 1580 LRSServerConfig: serverCfg, 1581 Raw: v3ClusterAnyWithEDSConfigSourceSelf, 1582 TelemetryLabels: internal.UnknownCSMLabels, 1583 }, 1584 }, 1585 { 1586 name: "v3 cluster with telemetry case", 1587 resource: v3ClusterAnyWithTelemetryLabels, 1588 serverCfg: serverCfg, 1589 wantName: v3ClusterName, 1590 wantUpdate: ClusterUpdate{ 1591 ClusterName: v3ClusterName, 1592 EDSServiceName: v3Service, 1593 LRSServerConfig: serverCfg, 1594 Raw: v3ClusterAnyWithTelemetryLabels, 1595 TelemetryLabels: map[string]string{ 1596 "csm.service_name": "grpc-service", 1597 "csm.service_namespace_name": "grpc-service-namespace", 1598 }, 1599 }, 1600 }, 1601 { 1602 name: "v3 metadata ignore other types not string and not com.google.csm.telemetry_labels", 1603 resource: v3ClusterAnyWithTelemetryLabelsIgnoreSome, 1604 serverCfg: serverCfg, 1605 wantName: v3ClusterName, 1606 wantUpdate: ClusterUpdate{ 1607 ClusterName: v3ClusterName, 1608 EDSServiceName: v3Service, 1609 LRSServerConfig: serverCfg, 1610 Raw: v3ClusterAnyWithTelemetryLabelsIgnoreSome, 1611 TelemetryLabels: map[string]string{ 1612 "csm.service_name": "grpc-service", 1613 "csm.service_namespace_name": "unknown", 1614 }, 1615 }, 1616 }, 1617 { 1618 name: "xdstp cluster resource with unset EDS service name", 1619 resource: testutils.MarshalAny(t, &v3clusterpb.Cluster{ 1620 Name: "xdstp:foo", 1621 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1622 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1623 EdsConfig: &v3corepb.ConfigSource{ 1624 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1625 Ads: &v3corepb.AggregatedConfigSource{}, 1626 }, 1627 }, 1628 ServiceName: "", 1629 }, 1630 }), 1631 wantName: "xdstp:foo", 1632 wantErr: true, 1633 }, 1634 } 1635 for _, test := range tests { 1636 t.Run(test.name, func(t *testing.T) { 1637 name, update, err := unmarshalClusterResource(test.resource, test.serverCfg) 1638 if (err != nil) != test.wantErr { 1639 t.Fatalf("unmarshalClusterResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) 1640 } 1641 if name != test.wantName { 1642 t.Errorf("unmarshalClusterResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName) 1643 } 1644 if diff := cmp.Diff(update, test.wantUpdate, cmpOpts, cmpopts.IgnoreFields(ClusterUpdate{}, "LBPolicy")); diff != "" { 1645 t.Errorf("unmarshalClusterResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff) 1646 } 1647 }) 1648 } 1649 } 1650 1651 func (s) TestValidateClusterWithOutlierDetection(t *testing.T) { 1652 odToClusterProto := func(od *v3clusterpb.OutlierDetection) *v3clusterpb.Cluster { 1653 // Cluster parsing doesn't fail with respect to fields orthogonal to 1654 // outlier detection. 1655 return &v3clusterpb.Cluster{ 1656 Name: clusterName, 1657 ClusterDiscoveryType: &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS}, 1658 EdsClusterConfig: &v3clusterpb.Cluster_EdsClusterConfig{ 1659 EdsConfig: &v3corepb.ConfigSource{ 1660 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 1661 Ads: &v3corepb.AggregatedConfigSource{}, 1662 }, 1663 }, 1664 }, 1665 LbPolicy: v3clusterpb.Cluster_ROUND_ROBIN, 1666 OutlierDetection: od, 1667 } 1668 } 1669 1670 tests := []struct { 1671 name string 1672 cluster *v3clusterpb.Cluster 1673 wantODCfg string 1674 wantErr bool 1675 }{ 1676 { 1677 name: "success-and-failure-null", 1678 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{}), 1679 wantODCfg: `{"successRateEjection": {}}`, 1680 }, 1681 { 1682 name: "success-and-failure-zero", 1683 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1684 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 0}, // Thus doesn't create sre - to focus on fpe 1685 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 0}, 1686 }), 1687 wantODCfg: `{}`, 1688 }, 1689 { 1690 name: "some-fields-set", 1691 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1692 Interval: &durationpb.Duration{Seconds: 1}, 1693 MaxEjectionTime: &durationpb.Duration{Seconds: 3}, 1694 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, 1695 SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, 1696 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, 1697 FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, 1698 }), 1699 wantODCfg: `{ 1700 "interval": "1s", 1701 "maxEjectionTime": "3s", 1702 "successRateEjection": { 1703 "enforcementPercentage": 3, 1704 "requestVolume": 5 1705 }, 1706 "failurePercentageEjection": { 1707 "enforcementPercentage": 7, 1708 "requestVolume": 9 1709 } 1710 }`, 1711 }, 1712 { 1713 name: "every-field-set-non-zero", 1714 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{ 1715 // all fields set (including ones that will be layered) should 1716 // pick up those too and explicitly all fields, including those 1717 // put in layers, in the JSON generated. 1718 Interval: &durationpb.Duration{Seconds: 1}, 1719 BaseEjectionTime: &durationpb.Duration{Seconds: 2}, 1720 MaxEjectionTime: &durationpb.Duration{Seconds: 3}, 1721 MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 1}, 1722 SuccessRateStdevFactor: &wrapperspb.UInt32Value{Value: 2}, 1723 EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 3}, 1724 SuccessRateMinimumHosts: &wrapperspb.UInt32Value{Value: 4}, 1725 SuccessRateRequestVolume: &wrapperspb.UInt32Value{Value: 5}, 1726 FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 6}, 1727 EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 7}, 1728 FailurePercentageMinimumHosts: &wrapperspb.UInt32Value{Value: 8}, 1729 FailurePercentageRequestVolume: &wrapperspb.UInt32Value{Value: 9}, 1730 }), 1731 wantODCfg: `{ 1732 "interval": "1s", 1733 "baseEjectionTime": "2s", 1734 "maxEjectionTime": "3s", 1735 "maxEjectionPercent": 1, 1736 "successRateEjection": { 1737 "stdevFactor": 2, 1738 "enforcementPercentage": 3, 1739 "minimumHosts": 4, 1740 "requestVolume": 5 1741 }, 1742 "failurePercentageEjection": { 1743 "threshold": 6, 1744 "enforcementPercentage": 7, 1745 "minimumHosts": 8, 1746 "requestVolume": 9 1747 } 1748 }`, 1749 }, 1750 { 1751 name: "interval-is-negative", 1752 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: -10}}), 1753 wantErr: true, 1754 }, 1755 { 1756 name: "interval-overflows", 1757 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{Interval: &durationpb.Duration{Seconds: 315576000001}}), 1758 wantErr: true, 1759 }, 1760 { 1761 name: "base-ejection-time-is-negative", 1762 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: -10}}), 1763 wantErr: true, 1764 }, 1765 { 1766 name: "base-ejection-time-overflows", 1767 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{BaseEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), 1768 wantErr: true, 1769 }, 1770 { 1771 name: "max-ejection-time-is-negative", 1772 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: -10}}), 1773 wantErr: true, 1774 }, 1775 { 1776 name: "max-ejection-time-overflows", 1777 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionTime: &durationpb.Duration{Seconds: 315576000001}}), 1778 wantErr: true, 1779 }, 1780 { 1781 name: "max-ejection-percent-is-greater-than-100", 1782 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{MaxEjectionPercent: &wrapperspb.UInt32Value{Value: 150}}), 1783 wantErr: true, 1784 }, 1785 { 1786 name: "enforcing-success-rate-is-greater-than-100", 1787 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingSuccessRate: &wrapperspb.UInt32Value{Value: 150}}), 1788 wantErr: true, 1789 }, 1790 { 1791 name: "failure-percentage-threshold-is-greater-than-100", 1792 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{FailurePercentageThreshold: &wrapperspb.UInt32Value{Value: 150}}), 1793 wantErr: true, 1794 }, 1795 { 1796 name: "enforcing-failure-percentage-is-greater-than-100", 1797 cluster: odToClusterProto(&v3clusterpb.OutlierDetection{EnforcingFailurePercentage: &wrapperspb.UInt32Value{Value: 150}}), 1798 wantErr: true, 1799 }, 1800 // A Outlier Detection proto not present should lead to a nil 1801 // OutlierDetection field in the ClusterUpdate, which is implicitly 1802 // tested in every other test in this file. 1803 } 1804 for _, test := range tests { 1805 t.Run(test.name, func(t *testing.T) { 1806 update, err := validateClusterAndConstructClusterUpdate(test.cluster, nil) 1807 if (err != nil) != test.wantErr { 1808 t.Errorf("validateClusterAndConstructClusterUpdate() returned err %v wantErr %v)", err, test.wantErr) 1809 } 1810 if test.wantErr { 1811 return 1812 } 1813 // got and want must be unmarshalled since JSON strings shouldn't 1814 // generally be directly compared. 1815 var got map[string]any 1816 if err := json.Unmarshal(update.OutlierDetection, &got); err != nil { 1817 t.Fatalf("Error unmarshalling update.OutlierDetection (%q): %v", update.OutlierDetection, err) 1818 } 1819 var want map[string]any 1820 if err := json.Unmarshal(json.RawMessage(test.wantODCfg), &want); err != nil { 1821 t.Fatalf("Error unmarshalling wantODCfg (%q): %v", test.wantODCfg, err) 1822 } 1823 if diff := cmp.Diff(got, want); diff != "" { 1824 t.Fatalf("cluster.OutlierDetection got unexpected output, diff (-got, +want): %v", diff) 1825 } 1826 }) 1827 } 1828 }