google.golang.org/grpc@v1.72.2/internal/testutils/xds/e2e/clientresources.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 19 package e2e 20 21 import ( 22 "fmt" 23 "net" 24 "strconv" 25 26 "github.com/envoyproxy/go-control-plane/pkg/wellknown" 27 "google.golang.org/protobuf/proto" 28 "google.golang.org/protobuf/types/known/anypb" 29 "google.golang.org/protobuf/types/known/structpb" 30 "google.golang.org/protobuf/types/known/wrapperspb" 31 32 v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" 33 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 34 v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 35 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 36 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 37 v3aggregateclusterpb "github.com/envoyproxy/go-control-plane/envoy/extensions/clusters/aggregate/v3" 38 v3routerpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" 39 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 40 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 41 v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3" 42 ) 43 44 const ( 45 // ServerListenerResourceNameTemplate is the Listener resource name template 46 // used on the server side. 47 ServerListenerResourceNameTemplate = "grpc/server?xds.resource.listening_address=%s" 48 // ClientSideCertProviderInstance is the certificate provider instance name 49 // used in the Cluster resource on the client side. 50 ClientSideCertProviderInstance = "client-side-certificate-provider-instance" 51 // ServerSideCertProviderInstance is the certificate provider instance name 52 // used in the Listener resource on the server side. 53 ServerSideCertProviderInstance = "server-side-certificate-provider-instance" 54 ) 55 56 // SecurityLevel allows the test to control the security level to be used in the 57 // resource returned by this package. 58 type SecurityLevel int 59 60 const ( 61 // SecurityLevelNone is used when no security configuration is required. 62 SecurityLevelNone SecurityLevel = iota 63 // SecurityLevelTLS is used when security configuration corresponding to TLS 64 // is required. Only the server presents an identity certificate in this 65 // configuration. 66 SecurityLevelTLS 67 // SecurityLevelMTLS is used when security configuration corresponding to 68 // mTLS is required. Both client and server present identity certificates in 69 // this configuration. 70 SecurityLevelMTLS 71 // SecurityLevelTLSWithSystemRootCerts is used when security configuration 72 // corresponding to TLS is required. Only the server presents an identity 73 // certificate in this configuration and the client uses system root certs 74 // to validate the server certificate. 75 SecurityLevelTLSWithSystemRootCerts 76 ) 77 78 // ResourceParams wraps the arguments to be passed to DefaultClientResources. 79 type ResourceParams struct { 80 // DialTarget is the client's dial target. This is used as the name of the 81 // Listener resource. 82 DialTarget string 83 // NodeID is the id of the xdsClient to which this update is to be pushed. 84 NodeID string 85 // Host is the host of the default Endpoint resource. 86 Host string 87 // port is the port of the default Endpoint resource. 88 Port uint32 89 // SecLevel controls the security configuration in the Cluster resource. 90 SecLevel SecurityLevel 91 } 92 93 // DefaultClientResources returns a set of resources (LDS, RDS, CDS, EDS) for a 94 // client to generically connect to one server. 95 func DefaultClientResources(params ResourceParams) UpdateOptions { 96 routeConfigName := "route-" + params.DialTarget 97 clusterName := "cluster-" + params.DialTarget 98 endpointsName := "endpoints-" + params.DialTarget 99 return UpdateOptions{ 100 NodeID: params.NodeID, 101 Listeners: []*v3listenerpb.Listener{DefaultClientListener(params.DialTarget, routeConfigName)}, 102 Routes: []*v3routepb.RouteConfiguration{DefaultRouteConfig(routeConfigName, params.DialTarget, clusterName)}, 103 Clusters: []*v3clusterpb.Cluster{DefaultCluster(clusterName, endpointsName, params.SecLevel)}, 104 Endpoints: []*v3endpointpb.ClusterLoadAssignment{DefaultEndpoint(endpointsName, params.Host, []uint32{params.Port})}, 105 } 106 } 107 108 // RouterHTTPFilter is the HTTP Filter configuration for the Router filter. 109 var RouterHTTPFilter = HTTPFilter("router", &v3routerpb.Router{}) 110 111 // DefaultClientListener returns a basic xds Listener resource to be used on 112 // the client side. 113 func DefaultClientListener(target, routeName string) *v3listenerpb.Listener { 114 hcm := marshalAny(&v3httppb.HttpConnectionManager{ 115 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{Rds: &v3httppb.Rds{ 116 ConfigSource: &v3corepb.ConfigSource{ 117 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}}, 118 }, 119 RouteConfigName: routeName, 120 }}, 121 HttpFilters: []*v3httppb.HttpFilter{HTTPFilter("router", &v3routerpb.Router{})}, // router fields are unused by grpc 122 }) 123 return &v3listenerpb.Listener{ 124 Name: target, 125 ApiListener: &v3listenerpb.ApiListener{ApiListener: hcm}, 126 FilterChains: []*v3listenerpb.FilterChain{{ 127 Name: "filter-chain-name", 128 Filters: []*v3listenerpb.Filter{{ 129 Name: wellknown.HTTPConnectionManager, 130 ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: hcm}, 131 }}, 132 }}, 133 } 134 } 135 136 func marshalAny(m proto.Message) *anypb.Any { 137 a, err := anypb.New(m) 138 if err != nil { 139 panic(fmt.Sprintf("anypb.New(%+v) failed: %v", m, err)) 140 } 141 return a 142 } 143 144 // DefaultServerListener returns a basic xds Listener resource to be used on the 145 // server side. The returned Listener resource contains an inline route 146 // configuration with the name of routeName. 147 func DefaultServerListener(host string, port uint32, secLevel SecurityLevel, routeName string) *v3listenerpb.Listener { 148 return defaultServerListenerCommon(host, port, secLevel, routeName, true) 149 } 150 151 func defaultServerListenerCommon(host string, port uint32, secLevel SecurityLevel, routeName string, inlineRouteConfig bool) *v3listenerpb.Listener { 152 var hcm *v3httppb.HttpConnectionManager 153 if inlineRouteConfig { 154 hcm = &v3httppb.HttpConnectionManager{ 155 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 156 RouteConfig: &v3routepb.RouteConfiguration{ 157 Name: routeName, 158 VirtualHosts: []*v3routepb.VirtualHost{{ 159 // This "*" string matches on any incoming authority. This is to ensure any 160 // incoming RPC matches to Route_NonForwardingAction and will proceed as 161 // normal. 162 Domains: []string{"*"}, 163 Routes: []*v3routepb.Route{{ 164 Match: &v3routepb.RouteMatch{ 165 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 166 }, 167 Action: &v3routepb.Route_NonForwardingAction{}, 168 }}}}}, 169 }, 170 HttpFilters: []*v3httppb.HttpFilter{RouterHTTPFilter}, 171 } 172 } else { 173 hcm = &v3httppb.HttpConnectionManager{ 174 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 175 Rds: &v3httppb.Rds{ 176 ConfigSource: &v3corepb.ConfigSource{ 177 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}}, 178 }, 179 RouteConfigName: routeName, 180 }, 181 }, 182 HttpFilters: []*v3httppb.HttpFilter{RouterHTTPFilter}, 183 } 184 } 185 186 var tlsContext *v3tlspb.DownstreamTlsContext 187 switch secLevel { 188 case SecurityLevelNone: 189 case SecurityLevelTLS: 190 tlsContext = &v3tlspb.DownstreamTlsContext{ 191 CommonTlsContext: &v3tlspb.CommonTlsContext{ 192 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 193 InstanceName: ServerSideCertProviderInstance, 194 }, 195 }, 196 } 197 case SecurityLevelMTLS: 198 tlsContext = &v3tlspb.DownstreamTlsContext{ 199 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 200 CommonTlsContext: &v3tlspb.CommonTlsContext{ 201 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 202 InstanceName: ServerSideCertProviderInstance, 203 }, 204 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 205 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 206 InstanceName: ServerSideCertProviderInstance, 207 }, 208 }, 209 }, 210 } 211 } 212 213 var ts *v3corepb.TransportSocket 214 if tlsContext != nil { 215 ts = &v3corepb.TransportSocket{ 216 Name: "envoy.transport_sockets.tls", 217 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 218 TypedConfig: marshalAny(tlsContext), 219 }, 220 } 221 } 222 return &v3listenerpb.Listener{ 223 Name: fmt.Sprintf(ServerListenerResourceNameTemplate, net.JoinHostPort(host, strconv.Itoa(int(port)))), 224 Address: &v3corepb.Address{ 225 Address: &v3corepb.Address_SocketAddress{ 226 SocketAddress: &v3corepb.SocketAddress{ 227 Address: host, 228 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 229 PortValue: port, 230 }, 231 }, 232 }, 233 }, 234 FilterChains: []*v3listenerpb.FilterChain{ 235 { 236 Name: "v4-wildcard", 237 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 238 PrefixRanges: []*v3corepb.CidrRange{ 239 { 240 AddressPrefix: "0.0.0.0", 241 PrefixLen: &wrapperspb.UInt32Value{ 242 Value: uint32(0), 243 }, 244 }, 245 }, 246 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 247 SourcePrefixRanges: []*v3corepb.CidrRange{ 248 { 249 AddressPrefix: "0.0.0.0", 250 PrefixLen: &wrapperspb.UInt32Value{ 251 Value: uint32(0), 252 }, 253 }, 254 }, 255 }, 256 Filters: []*v3listenerpb.Filter{ 257 { 258 Name: "filter-1", 259 ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: marshalAny(hcm)}, 260 }, 261 }, 262 TransportSocket: ts, 263 }, 264 { 265 Name: "v6-wildcard", 266 FilterChainMatch: &v3listenerpb.FilterChainMatch{ 267 PrefixRanges: []*v3corepb.CidrRange{ 268 { 269 AddressPrefix: "::", 270 PrefixLen: &wrapperspb.UInt32Value{ 271 Value: uint32(0), 272 }, 273 }, 274 }, 275 SourceType: v3listenerpb.FilterChainMatch_SAME_IP_OR_LOOPBACK, 276 SourcePrefixRanges: []*v3corepb.CidrRange{ 277 { 278 AddressPrefix: "::", 279 PrefixLen: &wrapperspb.UInt32Value{ 280 Value: uint32(0), 281 }, 282 }, 283 }, 284 }, 285 Filters: []*v3listenerpb.Filter{ 286 { 287 Name: "filter-1", 288 ConfigType: &v3listenerpb.Filter_TypedConfig{TypedConfig: marshalAny(hcm)}, 289 }, 290 }, 291 TransportSocket: ts, 292 }, 293 }, 294 } 295 } 296 297 // HTTPFilter constructs an xds HttpFilter with the provided name and config. 298 func HTTPFilter(name string, config proto.Message) *v3httppb.HttpFilter { 299 return &v3httppb.HttpFilter{ 300 Name: name, 301 ConfigType: &v3httppb.HttpFilter_TypedConfig{ 302 TypedConfig: marshalAny(config), 303 }, 304 } 305 } 306 307 // DefaultRouteConfig returns a basic xds RouteConfig resource. 308 func DefaultRouteConfig(routeName, vhDomain, clusterName string) *v3routepb.RouteConfiguration { 309 return &v3routepb.RouteConfiguration{ 310 Name: routeName, 311 VirtualHosts: []*v3routepb.VirtualHost{{ 312 Domains: []string{vhDomain}, 313 Routes: []*v3routepb.Route{{ 314 Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}}, 315 Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ 316 ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{ 317 Clusters: []*v3routepb.WeightedCluster_ClusterWeight{ 318 { 319 Name: clusterName, 320 Weight: &wrapperspb.UInt32Value{Value: 100}, 321 }, 322 }, 323 }}, 324 }}, 325 }}, 326 }}, 327 } 328 } 329 330 // RouteConfigClusterSpecifierType determines the cluster specifier type for the 331 // route actions configured in the returned RouteConfiguration resource. 332 type RouteConfigClusterSpecifierType int 333 334 const ( 335 // RouteConfigClusterSpecifierTypeCluster results in the cluster specifier 336 // being set to a RouteAction_Cluster. 337 RouteConfigClusterSpecifierTypeCluster RouteConfigClusterSpecifierType = iota 338 // RouteConfigClusterSpecifierTypeWeightedCluster results in the cluster 339 // specifier being set to RouteAction_WeightedClusters. 340 RouteConfigClusterSpecifierTypeWeightedCluster 341 // RouteConfigClusterSpecifierTypeClusterSpecifierPlugin results in the 342 // cluster specifier being set to a RouteAction_ClusterSpecifierPlugin. 343 RouteConfigClusterSpecifierTypeClusterSpecifierPlugin 344 ) 345 346 // RouteConfigOptions contains options to configure a RouteConfiguration 347 // resource. 348 type RouteConfigOptions struct { 349 // RouteConfigName is the name of the RouteConfiguration resource. 350 RouteConfigName string 351 // ListenerName is the name of the Listener resource which uses this 352 // RouteConfiguration. 353 ListenerName string 354 // ClusterSpecifierType determines the cluster specifier type. 355 ClusterSpecifierType RouteConfigClusterSpecifierType 356 // ClusterName is name of the cluster resource used when the cluster 357 // specifier type is set to RouteConfigClusterSpecifierTypeCluster. 358 // 359 // Default value of "A" is used if left unspecified. 360 ClusterName string 361 // WeightedClusters is a map from cluster name to weights, and is used when 362 // the cluster specifier type is set to 363 // RouteConfigClusterSpecifierTypeWeightedCluster. 364 // 365 // Default value of {"A": 75, "B": 25} is used if left unspecified. 366 WeightedClusters map[string]int 367 // The below two fields specify the name of the cluster specifier plugin and 368 // its configuration, and are used when the cluster specifier type is set to 369 // RouteConfigClusterSpecifierTypeClusterSpecifierPlugin. Tests are expected 370 // to provide valid values for these fields when appropriate. 371 ClusterSpecifierPluginName string 372 ClusterSpecifierPluginConfig *anypb.Any 373 } 374 375 // RouteConfigResourceWithOptions returns a RouteConfiguration resource 376 // configured with the provided options. 377 func RouteConfigResourceWithOptions(opts RouteConfigOptions) *v3routepb.RouteConfiguration { 378 switch opts.ClusterSpecifierType { 379 case RouteConfigClusterSpecifierTypeCluster: 380 clusterName := opts.ClusterName 381 if clusterName == "" { 382 clusterName = "A" 383 } 384 return &v3routepb.RouteConfiguration{ 385 Name: opts.RouteConfigName, 386 VirtualHosts: []*v3routepb.VirtualHost{{ 387 Domains: []string{opts.ListenerName}, 388 Routes: []*v3routepb.Route{{ 389 Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}}, 390 Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ 391 ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName}, 392 }}, 393 }}, 394 }}, 395 } 396 case RouteConfigClusterSpecifierTypeWeightedCluster: 397 weightedClusters := opts.WeightedClusters 398 if weightedClusters == nil { 399 weightedClusters = map[string]int{"A": 75, "B": 25} 400 } 401 clusters := []*v3routepb.WeightedCluster_ClusterWeight{} 402 for name, weight := range weightedClusters { 403 clusters = append(clusters, &v3routepb.WeightedCluster_ClusterWeight{ 404 Name: name, 405 Weight: &wrapperspb.UInt32Value{Value: uint32(weight)}, 406 }) 407 } 408 return &v3routepb.RouteConfiguration{ 409 Name: opts.RouteConfigName, 410 VirtualHosts: []*v3routepb.VirtualHost{{ 411 Domains: []string{opts.ListenerName}, 412 Routes: []*v3routepb.Route{{ 413 Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}}, 414 Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ 415 ClusterSpecifier: &v3routepb.RouteAction_WeightedClusters{WeightedClusters: &v3routepb.WeightedCluster{Clusters: clusters}}, 416 }}, 417 }}, 418 }}, 419 } 420 case RouteConfigClusterSpecifierTypeClusterSpecifierPlugin: 421 return &v3routepb.RouteConfiguration{ 422 Name: opts.RouteConfigName, 423 ClusterSpecifierPlugins: []*v3routepb.ClusterSpecifierPlugin{{ 424 Extension: &v3corepb.TypedExtensionConfig{ 425 Name: opts.ClusterSpecifierPluginName, 426 TypedConfig: opts.ClusterSpecifierPluginConfig, 427 }}, 428 }, 429 VirtualHosts: []*v3routepb.VirtualHost{{ 430 Domains: []string{opts.ListenerName}, 431 Routes: []*v3routepb.Route{{ 432 Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}}, 433 Action: &v3routepb.Route_Route{Route: &v3routepb.RouteAction{ 434 ClusterSpecifier: &v3routepb.RouteAction_ClusterSpecifierPlugin{ClusterSpecifierPlugin: opts.ClusterSpecifierPluginName}, 435 }}, 436 }}, 437 }}, 438 } 439 default: 440 panic(fmt.Sprintf("unsupported cluster specifier plugin type: %v", opts.ClusterSpecifierType)) 441 } 442 } 443 444 // DefaultCluster returns a basic xds Cluster resource. 445 func DefaultCluster(clusterName, edsServiceName string, secLevel SecurityLevel) *v3clusterpb.Cluster { 446 return ClusterResourceWithOptions(ClusterOptions{ 447 ClusterName: clusterName, 448 ServiceName: edsServiceName, 449 Policy: LoadBalancingPolicyRoundRobin, 450 SecurityLevel: secLevel, 451 }) 452 } 453 454 // LoadBalancingPolicy determines the policy used for balancing load across 455 // endpoints in the Cluster. 456 type LoadBalancingPolicy int 457 458 const ( 459 // LoadBalancingPolicyRoundRobin results in the use of the weighted_target 460 // LB policy to balance load across localities and endpoints in the cluster. 461 LoadBalancingPolicyRoundRobin LoadBalancingPolicy = iota 462 // LoadBalancingPolicyRingHash results in the use of the ring_hash LB policy 463 // as the leaf policy. 464 LoadBalancingPolicyRingHash 465 ) 466 467 // ClusterType specifies the type of the Cluster resource. 468 type ClusterType int 469 470 const ( 471 // ClusterTypeEDS specifies a Cluster that uses EDS to resolve endpoints. 472 ClusterTypeEDS ClusterType = iota 473 // ClusterTypeLogicalDNS specifies a Cluster that uses DNS to resolve 474 // endpoints. 475 ClusterTypeLogicalDNS 476 // ClusterTypeAggregate specifies a Cluster that is made up of child 477 // clusters. 478 ClusterTypeAggregate 479 ) 480 481 // ClusterOptions contains options to configure a Cluster resource. 482 type ClusterOptions struct { 483 Type ClusterType 484 // ClusterName is the name of the Cluster resource. 485 ClusterName string 486 // ServiceName is the EDS service name of the Cluster. Applicable only when 487 // cluster type is EDS. 488 ServiceName string 489 // ChildNames is the list of child Cluster names. Applicable only when 490 // cluster type is Aggregate. 491 ChildNames []string 492 // DNSHostName is the dns host name of the Cluster. Applicable only when the 493 // cluster type is DNS. 494 DNSHostName string 495 // DNSPort is the port number of the Cluster. Applicable only when the 496 // cluster type is DNS. 497 DNSPort uint32 498 // Policy is the LB policy to be used. 499 Policy LoadBalancingPolicy 500 // SecurityLevel determines the security configuration for the Cluster. 501 SecurityLevel SecurityLevel 502 // EnableLRS adds a load reporting configuration with a config source 503 // pointing to self. 504 EnableLRS bool 505 } 506 507 // ClusterResourceWithOptions returns an xDS Cluster resource configured with 508 // the provided options. 509 func ClusterResourceWithOptions(opts ClusterOptions) *v3clusterpb.Cluster { 510 var tlsContext *v3tlspb.UpstreamTlsContext 511 switch opts.SecurityLevel { 512 case SecurityLevelNone: 513 case SecurityLevelTLS: 514 tlsContext = &v3tlspb.UpstreamTlsContext{ 515 CommonTlsContext: &v3tlspb.CommonTlsContext{ 516 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 517 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 518 InstanceName: ClientSideCertProviderInstance, 519 }, 520 }, 521 }, 522 } 523 case SecurityLevelMTLS: 524 tlsContext = &v3tlspb.UpstreamTlsContext{ 525 CommonTlsContext: &v3tlspb.CommonTlsContext{ 526 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 527 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 528 InstanceName: ClientSideCertProviderInstance, 529 }, 530 }, 531 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 532 InstanceName: ClientSideCertProviderInstance, 533 }, 534 }, 535 } 536 case SecurityLevelTLSWithSystemRootCerts: 537 tlsContext = &v3tlspb.UpstreamTlsContext{ 538 CommonTlsContext: &v3tlspb.CommonTlsContext{ 539 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 540 ValidationContext: &v3tlspb.CertificateValidationContext{ 541 SystemRootCerts: &v3tlspb.CertificateValidationContext_SystemRootCerts{}, 542 }, 543 }, 544 }, 545 } 546 } 547 548 var lbPolicy v3clusterpb.Cluster_LbPolicy 549 switch opts.Policy { 550 case LoadBalancingPolicyRoundRobin: 551 lbPolicy = v3clusterpb.Cluster_ROUND_ROBIN 552 case LoadBalancingPolicyRingHash: 553 lbPolicy = v3clusterpb.Cluster_RING_HASH 554 } 555 cluster := &v3clusterpb.Cluster{ 556 Name: opts.ClusterName, 557 LbPolicy: lbPolicy, 558 } 559 switch opts.Type { 560 case ClusterTypeEDS: 561 cluster.ClusterDiscoveryType = &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_EDS} 562 cluster.EdsClusterConfig = &v3clusterpb.Cluster_EdsClusterConfig{ 563 EdsConfig: &v3corepb.ConfigSource{ 564 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{ 565 Ads: &v3corepb.AggregatedConfigSource{}, 566 }, 567 }, 568 ServiceName: opts.ServiceName, 569 } 570 case ClusterTypeLogicalDNS: 571 cluster.ClusterDiscoveryType = &v3clusterpb.Cluster_Type{Type: v3clusterpb.Cluster_LOGICAL_DNS} 572 cluster.LoadAssignment = &v3endpointpb.ClusterLoadAssignment{ 573 Endpoints: []*v3endpointpb.LocalityLbEndpoints{{ 574 LbEndpoints: []*v3endpointpb.LbEndpoint{{ 575 HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{ 576 Endpoint: &v3endpointpb.Endpoint{ 577 Address: &v3corepb.Address{ 578 Address: &v3corepb.Address_SocketAddress{ 579 SocketAddress: &v3corepb.SocketAddress{ 580 Address: opts.DNSHostName, 581 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 582 PortValue: opts.DNSPort, 583 }, 584 }, 585 }, 586 }, 587 }, 588 }, 589 }}, 590 }}, 591 } 592 case ClusterTypeAggregate: 593 cluster.ClusterDiscoveryType = &v3clusterpb.Cluster_ClusterType{ 594 ClusterType: &v3clusterpb.Cluster_CustomClusterType{ 595 Name: "envoy.clusters.aggregate", 596 TypedConfig: marshalAny(&v3aggregateclusterpb.ClusterConfig{ 597 Clusters: opts.ChildNames, 598 }), 599 }, 600 } 601 } 602 if tlsContext != nil { 603 cluster.TransportSocket = &v3corepb.TransportSocket{ 604 Name: "envoy.transport_sockets.tls", 605 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 606 TypedConfig: marshalAny(tlsContext), 607 }, 608 } 609 } 610 if opts.EnableLRS { 611 cluster.LrsServer = &v3corepb.ConfigSource{ 612 ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{ 613 Self: &v3corepb.SelfConfigSource{}, 614 }, 615 } 616 } 617 return cluster 618 } 619 620 // LocalityID represents a locality identifier. 621 type LocalityID struct { 622 Region string 623 Zone string 624 SubZone string 625 } 626 627 // LocalityOptions contains options to configure a Locality. 628 type LocalityOptions struct { 629 // Name is the unique locality name. 630 Name string 631 // Weight is the weight of the locality, used for load balancing. 632 Weight uint32 633 // Backends is a set of backends belonging to this locality. 634 Backends []BackendOptions 635 // Priority is the priority of the locality. Defaults to 0. 636 Priority uint32 637 // Locality is the locality identifier. If not specified, a random 638 // identifier is generated. 639 Locality LocalityID 640 } 641 642 // BackendOptions contains options to configure individual backends in a 643 // locality. 644 type BackendOptions struct { 645 // Ports on which the backend is accepting connections. All backends 646 // are expected to run on localhost, hence host name is not stored here. 647 Ports []uint32 648 // Health status of the backend. Default is UNKNOWN which is treated the 649 // same as HEALTHY. 650 HealthStatus v3corepb.HealthStatus 651 // Weight sets the backend weight. Defaults to 1. 652 Weight uint32 653 // Metadata sets the LB endpoint metadata (envoy.lb FilterMetadata field). 654 // See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#envoy-v3-api-msg-config-core-v3-metadata 655 Metadata map[string]any 656 } 657 658 // EndpointOptions contains options to configure an Endpoint (or 659 // ClusterLoadAssignment) resource. 660 type EndpointOptions struct { 661 // ClusterName is the name of the Cluster resource (or EDS service name) 662 // containing the endpoints specified below. 663 ClusterName string 664 // Host is the hostname of the endpoints. In our e2e tests, hostname must 665 // always be "localhost". 666 Host string 667 // Localities is a set of localities belonging to this resource. 668 Localities []LocalityOptions 669 // DropPercents is a map from drop category to a drop percentage. If unset, 670 // no drops are configured. 671 DropPercents map[string]int 672 } 673 674 // DefaultEndpoint returns a basic xds Endpoint resource. 675 func DefaultEndpoint(clusterName string, host string, ports []uint32) *v3endpointpb.ClusterLoadAssignment { 676 var bOpts []BackendOptions 677 for _, p := range ports { 678 bOpts = append(bOpts, BackendOptions{Ports: []uint32{p}, Weight: 1}) 679 } 680 return EndpointResourceWithOptions(EndpointOptions{ 681 ClusterName: clusterName, 682 Host: host, 683 Localities: []LocalityOptions{ 684 { 685 Backends: bOpts, 686 Weight: 1, 687 }, 688 }, 689 }) 690 } 691 692 // EndpointResourceWithOptions returns an xds Endpoint resource configured with 693 // the provided options. 694 func EndpointResourceWithOptions(opts EndpointOptions) *v3endpointpb.ClusterLoadAssignment { 695 var endpoints []*v3endpointpb.LocalityLbEndpoints 696 for i, locality := range opts.Localities { 697 var lbEndpoints []*v3endpointpb.LbEndpoint 698 for _, b := range locality.Backends { 699 // Weight defaults to 1. 700 if b.Weight == 0 { 701 b.Weight = 1 702 } 703 additionalAddresses := make([]*v3endpointpb.Endpoint_AdditionalAddress, len(b.Ports)-1) 704 for i, p := range b.Ports[1:] { 705 additionalAddresses[i] = &v3endpointpb.Endpoint_AdditionalAddress{ 706 Address: &v3corepb.Address{Address: &v3corepb.Address_SocketAddress{ 707 SocketAddress: &v3corepb.SocketAddress{ 708 Protocol: v3corepb.SocketAddress_TCP, 709 Address: opts.Host, 710 PortSpecifier: &v3corepb.SocketAddress_PortValue{PortValue: p}, 711 }}, 712 }, 713 } 714 } 715 metadata, err := structpb.NewStruct(b.Metadata) 716 if err != nil { 717 panic(err) 718 } 719 lbEndpoints = append(lbEndpoints, &v3endpointpb.LbEndpoint{ 720 HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{Endpoint: &v3endpointpb.Endpoint{ 721 Address: &v3corepb.Address{Address: &v3corepb.Address_SocketAddress{ 722 SocketAddress: &v3corepb.SocketAddress{ 723 Protocol: v3corepb.SocketAddress_TCP, 724 Address: opts.Host, 725 PortSpecifier: &v3corepb.SocketAddress_PortValue{PortValue: b.Ports[0]}, 726 }, 727 }}, 728 AdditionalAddresses: additionalAddresses, 729 }}, 730 HealthStatus: b.HealthStatus, 731 LoadBalancingWeight: &wrapperspb.UInt32Value{Value: b.Weight}, 732 Metadata: &v3corepb.Metadata{ 733 FilterMetadata: map[string]*structpb.Struct{ 734 "envoy.lb": metadata, 735 }, 736 }, 737 }) 738 } 739 740 l := locality.Locality 741 if l == (LocalityID{}) { 742 l = LocalityID{ 743 Region: fmt.Sprintf("region-%d", i+1), 744 Zone: fmt.Sprintf("zone-%d", i+1), 745 SubZone: fmt.Sprintf("subzone-%d", i+1), 746 } 747 } 748 endpoints = append(endpoints, &v3endpointpb.LocalityLbEndpoints{ 749 Locality: &v3corepb.Locality{Region: l.Region, Zone: l.Zone, SubZone: l.SubZone}, 750 LbEndpoints: lbEndpoints, 751 LoadBalancingWeight: &wrapperspb.UInt32Value{Value: locality.Weight}, 752 Priority: locality.Priority, 753 }) 754 } 755 756 cla := &v3endpointpb.ClusterLoadAssignment{ 757 ClusterName: opts.ClusterName, 758 Endpoints: endpoints, 759 } 760 761 var drops []*v3endpointpb.ClusterLoadAssignment_Policy_DropOverload 762 for category, val := range opts.DropPercents { 763 drops = append(drops, &v3endpointpb.ClusterLoadAssignment_Policy_DropOverload{ 764 Category: category, 765 DropPercentage: &v3typepb.FractionalPercent{ 766 Numerator: uint32(val), 767 Denominator: v3typepb.FractionalPercent_HUNDRED, 768 }, 769 }) 770 } 771 if len(drops) != 0 { 772 cla.Policy = &v3endpointpb.ClusterLoadAssignment_Policy{ 773 DropOverloads: drops, 774 } 775 } 776 return cla 777 } 778 779 // DefaultServerListenerWithRouteConfigName returns a basic xds Listener 780 // resource to be used on the server side. The returned Listener resource 781 // contains a RouteConfiguration resource name that needs to be resolved. 782 func DefaultServerListenerWithRouteConfigName(host string, port uint32, secLevel SecurityLevel, routeName string) *v3listenerpb.Listener { 783 return defaultServerListenerCommon(host, port, secLevel, routeName, false) 784 } 785 786 // RouteConfigNoRouteMatch returns an xDS RouteConfig resource which a route 787 // with no route match. This will be NACKed by the xDS Client. 788 func RouteConfigNoRouteMatch(routeName string) *v3routepb.RouteConfiguration { 789 return &v3routepb.RouteConfiguration{ 790 Name: routeName, 791 VirtualHosts: []*v3routepb.VirtualHost{{ 792 // This "*" string matches on any incoming authority. This is to ensure any 793 // incoming RPC matches to Route_NonForwardingAction and will proceed as 794 // normal. 795 Domains: []string{"*"}, 796 Routes: []*v3routepb.Route{{ 797 Action: &v3routepb.Route_NonForwardingAction{}, 798 }}}}} 799 } 800 801 // RouteConfigNonForwardingAction returns an xDS RouteConfig resource which 802 // specifies to route to a route specifying non forwarding action. This is 803 // intended to be used on the server side for RDS requests, and corresponds to 804 // the inline route configuration in DefaultServerListener. 805 func RouteConfigNonForwardingAction(routeName string) *v3routepb.RouteConfiguration { 806 return &v3routepb.RouteConfiguration{ 807 Name: routeName, 808 VirtualHosts: []*v3routepb.VirtualHost{{ 809 // This "*" string matches on any incoming authority. This is to ensure any 810 // incoming RPC matches to Route_NonForwardingAction and will proceed as 811 // normal. 812 Domains: []string{"*"}, 813 Routes: []*v3routepb.Route{{ 814 Match: &v3routepb.RouteMatch{ 815 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 816 }, 817 Action: &v3routepb.Route_NonForwardingAction{}, 818 }}}}} 819 } 820 821 // RouteConfigFilterAction returns an xDS RouteConfig resource which specifies 822 // to route to a route specifying route filter action. Since this is not type 823 // non forwarding action, this should fail requests that match to this server 824 // side. 825 func RouteConfigFilterAction(routeName string) *v3routepb.RouteConfiguration { 826 return &v3routepb.RouteConfiguration{ 827 Name: routeName, 828 VirtualHosts: []*v3routepb.VirtualHost{{ 829 // This "*" string matches on any incoming authority. This is to 830 // ensure any incoming RPC matches to Route_Route and will fail with 831 // UNAVAILABLE. 832 Domains: []string{"*"}, 833 Routes: []*v3routepb.Route{{ 834 Match: &v3routepb.RouteMatch{ 835 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 836 }, 837 Action: &v3routepb.Route_FilterAction{}, 838 }}}}} 839 }