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  }