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