github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/go-control-plane/pkg/test/resource/v3/resource.go (about)

     1  // Copyright 2018 Envoyproxy Authors
     2  //
     3  //   Licensed under the Apache License, Version 2.0 (the "License");
     4  //   you may not use this file except in compliance with the License.
     5  //   You may obtain a copy of the License at
     6  //
     7  //       http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //   Unless required by applicable law or agreed to in writing, software
    10  //   distributed under the License is distributed on an "AS IS" BASIS,
    11  //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //   See the License for the specific language governing permissions and
    13  //   limitations under the License.
    14  
    15  // Package resource creates test xDS resources
    16  package resource
    17  
    18  import (
    19  	"fmt"
    20  	"time"
    21  
    22  	pstruct "github.com/golang/protobuf/ptypes/struct"
    23  
    24  	"github.com/golang/protobuf/ptypes"
    25  
    26  	alf "github.com/hxx258456/ccgo/go-control-plane/envoy/config/accesslog/v3"
    27  	cluster "github.com/hxx258456/ccgo/go-control-plane/envoy/config/cluster/v3"
    28  	core "github.com/hxx258456/ccgo/go-control-plane/envoy/config/core/v3"
    29  	endpoint "github.com/hxx258456/ccgo/go-control-plane/envoy/config/endpoint/v3"
    30  	listener "github.com/hxx258456/ccgo/go-control-plane/envoy/config/listener/v3"
    31  	route "github.com/hxx258456/ccgo/go-control-plane/envoy/config/route/v3"
    32  	als "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/access_loggers/grpc/v3"
    33  	hcm "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
    34  	tcp "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
    35  	auth "github.com/hxx258456/ccgo/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
    36  	runtime "github.com/hxx258456/ccgo/go-control-plane/envoy/service/runtime/v3"
    37  	"github.com/hxx258456/ccgo/go-control-plane/pkg/cache/types"
    38  	"github.com/hxx258456/ccgo/go-control-plane/pkg/cache/v3"
    39  	"github.com/hxx258456/ccgo/go-control-plane/pkg/resource/v3"
    40  	"github.com/hxx258456/ccgo/go-control-plane/pkg/wellknown"
    41  )
    42  
    43  const (
    44  	localhost = "127.0.0.1"
    45  
    46  	// XdsCluster is the cluster name for the control server (used by non-ADS set-up)
    47  	XdsCluster = "xds_cluster"
    48  
    49  	// Ads mode for resources: one aggregated xDS service
    50  	Ads = "ads"
    51  
    52  	// Xds mode for resources: individual xDS services
    53  	Xds = "xds"
    54  
    55  	// Rest mode for resources: polling using Fetch
    56  	Rest = "rest"
    57  
    58  	// Delta mode for resources: individual delta xDS services
    59  	Delta = "delta"
    60  
    61  	// Delta Ads mode for resource: one aggregated delta xDS service
    62  	DeltaAds = "delta-ads"
    63  )
    64  
    65  var (
    66  	// RefreshDelay for the polling config source
    67  	RefreshDelay = 500 * time.Millisecond
    68  )
    69  
    70  // MakeEndpoint creates a localhost endpoint on a given port.
    71  func MakeEndpoint(clusterName string, port uint32) *endpoint.ClusterLoadAssignment {
    72  	return &endpoint.ClusterLoadAssignment{
    73  		ClusterName: clusterName,
    74  		Endpoints: []*endpoint.LocalityLbEndpoints{{
    75  			LbEndpoints: []*endpoint.LbEndpoint{{
    76  				HostIdentifier: &endpoint.LbEndpoint_Endpoint{
    77  					Endpoint: &endpoint.Endpoint{
    78  						Address: &core.Address{
    79  							Address: &core.Address_SocketAddress{
    80  								SocketAddress: &core.SocketAddress{
    81  									Protocol: core.SocketAddress_TCP,
    82  									Address:  localhost,
    83  									PortSpecifier: &core.SocketAddress_PortValue{
    84  										PortValue: port,
    85  									},
    86  								},
    87  							},
    88  						},
    89  					},
    90  				},
    91  			}},
    92  		}},
    93  	}
    94  }
    95  
    96  // MakeCluster creates a cluster using either ADS or EDS.
    97  func MakeCluster(mode string, clusterName string) *cluster.Cluster {
    98  	edsSource := configSource(mode)
    99  
   100  	connectTimeout := 5 * time.Second
   101  	return &cluster.Cluster{
   102  		Name:                 clusterName,
   103  		ConnectTimeout:       ptypes.DurationProto(connectTimeout),
   104  		ClusterDiscoveryType: &cluster.Cluster_Type{Type: cluster.Cluster_EDS},
   105  		EdsClusterConfig: &cluster.Cluster_EdsClusterConfig{
   106  			EdsConfig: edsSource,
   107  		},
   108  	}
   109  }
   110  
   111  // MakeRoute creates an HTTP route that routes to a given cluster.
   112  func MakeRoute(routeName, clusterName string) *route.RouteConfiguration {
   113  	return &route.RouteConfiguration{
   114  		Name: routeName,
   115  		VirtualHosts: []*route.VirtualHost{{
   116  			Name:    routeName,
   117  			Domains: []string{"*"},
   118  			Routes: []*route.Route{{
   119  				Match: &route.RouteMatch{
   120  					PathSpecifier: &route.RouteMatch_Prefix{
   121  						Prefix: "/",
   122  					},
   123  				},
   124  				Action: &route.Route_Route{
   125  					Route: &route.RouteAction{
   126  						ClusterSpecifier: &route.RouteAction_Cluster{
   127  							Cluster: clusterName,
   128  						},
   129  					},
   130  				},
   131  			}},
   132  		}},
   133  	}
   134  }
   135  
   136  // data source configuration
   137  func configSource(mode string) *core.ConfigSource {
   138  	source := &core.ConfigSource{}
   139  	source.ResourceApiVersion = resource.DefaultAPIVersion
   140  	switch mode {
   141  	case Ads:
   142  		source.ConfigSourceSpecifier = &core.ConfigSource_Ads{
   143  			Ads: &core.AggregatedConfigSource{},
   144  		}
   145  	case DeltaAds:
   146  		source.ConfigSourceSpecifier = &core.ConfigSource_Ads{
   147  			Ads: &core.AggregatedConfigSource{},
   148  		}
   149  	case Xds:
   150  		source.ConfigSourceSpecifier = &core.ConfigSource_ApiConfigSource{
   151  			ApiConfigSource: &core.ApiConfigSource{
   152  				TransportApiVersion:       resource.DefaultAPIVersion,
   153  				ApiType:                   core.ApiConfigSource_GRPC,
   154  				SetNodeOnFirstMessageOnly: true,
   155  				GrpcServices: []*core.GrpcService{{
   156  					TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
   157  						EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: XdsCluster},
   158  					},
   159  				}},
   160  			},
   161  		}
   162  	case Rest:
   163  		source.ConfigSourceSpecifier = &core.ConfigSource_ApiConfigSource{
   164  			ApiConfigSource: &core.ApiConfigSource{
   165  				ApiType:             core.ApiConfigSource_REST,
   166  				TransportApiVersion: resource.DefaultAPIVersion,
   167  				ClusterNames:        []string{XdsCluster},
   168  				RefreshDelay:        ptypes.DurationProto(RefreshDelay),
   169  			},
   170  		}
   171  	case Delta:
   172  		source.ConfigSourceSpecifier = &core.ConfigSource_ApiConfigSource{
   173  			ApiConfigSource: &core.ApiConfigSource{
   174  				TransportApiVersion:       resource.DefaultAPIVersion,
   175  				ApiType:                   core.ApiConfigSource_DELTA_GRPC,
   176  				SetNodeOnFirstMessageOnly: true,
   177  				GrpcServices: []*core.GrpcService{{
   178  					TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
   179  						EnvoyGrpc: &core.GrpcService_EnvoyGrpc{ClusterName: XdsCluster},
   180  					},
   181  				}},
   182  			},
   183  		}
   184  	}
   185  	return source
   186  }
   187  
   188  // MakeHTTPListener creates a listener using either ADS or RDS for the route.
   189  func MakeHTTPListener(mode string, listenerName string, port uint32, route string) *listener.Listener {
   190  	rdsSource := configSource(mode)
   191  
   192  	// access log service configuration
   193  	alsConfig := &als.HttpGrpcAccessLogConfig{
   194  		CommonConfig: &als.CommonGrpcAccessLogConfig{
   195  			LogName:             "echo",
   196  			TransportApiVersion: resource.DefaultAPIVersion,
   197  			GrpcService: &core.GrpcService{
   198  				TargetSpecifier: &core.GrpcService_EnvoyGrpc_{
   199  					EnvoyGrpc: &core.GrpcService_EnvoyGrpc{
   200  						ClusterName: XdsCluster,
   201  					},
   202  				},
   203  			},
   204  		},
   205  	}
   206  	alsConfigPbst, err := ptypes.MarshalAny(alsConfig)
   207  	if err != nil {
   208  		panic(err)
   209  	}
   210  
   211  	// HTTP filter configuration
   212  	manager := &hcm.HttpConnectionManager{
   213  		CodecType:  hcm.HttpConnectionManager_AUTO,
   214  		StatPrefix: "http",
   215  		RouteSpecifier: &hcm.HttpConnectionManager_Rds{
   216  			Rds: &hcm.Rds{
   217  				ConfigSource:    rdsSource,
   218  				RouteConfigName: route,
   219  			},
   220  		},
   221  		HttpFilters: []*hcm.HttpFilter{{
   222  			Name: wellknown.Router,
   223  		}},
   224  		AccessLog: []*alf.AccessLog{{
   225  			Name: wellknown.HTTPGRPCAccessLog,
   226  			ConfigType: &alf.AccessLog_TypedConfig{
   227  				TypedConfig: alsConfigPbst,
   228  			},
   229  		}},
   230  	}
   231  	pbst, err := ptypes.MarshalAny(manager)
   232  	if err != nil {
   233  		panic(err)
   234  	}
   235  
   236  	return &listener.Listener{
   237  		Name: listenerName,
   238  		Address: &core.Address{
   239  			Address: &core.Address_SocketAddress{
   240  				SocketAddress: &core.SocketAddress{
   241  					Protocol: core.SocketAddress_TCP,
   242  					Address:  localhost,
   243  					PortSpecifier: &core.SocketAddress_PortValue{
   244  						PortValue: port,
   245  					},
   246  				},
   247  			},
   248  		},
   249  		FilterChains: []*listener.FilterChain{{
   250  			Filters: []*listener.Filter{{
   251  				Name: wellknown.HTTPConnectionManager,
   252  				ConfigType: &listener.Filter_TypedConfig{
   253  					TypedConfig: pbst,
   254  				},
   255  			}},
   256  		}},
   257  	}
   258  }
   259  
   260  // MakeTCPListener creates a TCP listener for a cluster.
   261  func MakeTCPListener(listenerName string, port uint32, clusterName string) *listener.Listener {
   262  	// TCP filter configuration
   263  	config := &tcp.TcpProxy{
   264  		StatPrefix: "tcp",
   265  		ClusterSpecifier: &tcp.TcpProxy_Cluster{
   266  			Cluster: clusterName,
   267  		},
   268  	}
   269  	pbst, err := ptypes.MarshalAny(config)
   270  	if err != nil {
   271  		panic(err)
   272  	}
   273  	return &listener.Listener{
   274  		Name: listenerName,
   275  		Address: &core.Address{
   276  			Address: &core.Address_SocketAddress{
   277  				SocketAddress: &core.SocketAddress{
   278  					Protocol: core.SocketAddress_TCP,
   279  					Address:  localhost,
   280  					PortSpecifier: &core.SocketAddress_PortValue{
   281  						PortValue: port,
   282  					},
   283  				},
   284  			},
   285  		},
   286  		FilterChains: []*listener.FilterChain{{
   287  			Filters: []*listener.Filter{{
   288  				Name: wellknown.TCPProxy,
   289  				ConfigType: &listener.Filter_TypedConfig{
   290  					TypedConfig: pbst,
   291  				},
   292  			}},
   293  		}},
   294  	}
   295  }
   296  
   297  // MakeRuntime creates an RTDS layer with some fields.
   298  func MakeRuntime(runtimeName string) *runtime.Runtime {
   299  	return &runtime.Runtime{
   300  		Name: runtimeName,
   301  		Layer: &pstruct.Struct{
   302  			Fields: map[string]*pstruct.Value{
   303  				"field-0": {
   304  					Kind: &pstruct.Value_NumberValue{NumberValue: 100},
   305  				},
   306  				"field-1": {
   307  					Kind: &pstruct.Value_StringValue{StringValue: "foobar"},
   308  				},
   309  			},
   310  		},
   311  	}
   312  }
   313  
   314  // MakeExtensionConfig creates a extension config for a cluster.
   315  func MakeExtensionConfig(mode string, extensionConfigName string, route string) *core.TypedExtensionConfig {
   316  	rdsSource := configSource(mode)
   317  
   318  	// HTTP filter configuration
   319  	manager := &hcm.HttpConnectionManager{
   320  		CodecType:  hcm.HttpConnectionManager_AUTO,
   321  		StatPrefix: "http",
   322  		RouteSpecifier: &hcm.HttpConnectionManager_Rds{
   323  			Rds: &hcm.Rds{
   324  				ConfigSource:    rdsSource,
   325  				RouteConfigName: route,
   326  			},
   327  		},
   328  		HttpFilters: []*hcm.HttpFilter{{
   329  			Name: wellknown.Router,
   330  		}},
   331  	}
   332  	pbst, err := ptypes.MarshalAny(manager)
   333  	if err != nil {
   334  		panic(err)
   335  	}
   336  
   337  	return &core.TypedExtensionConfig{
   338  		Name:        extensionConfigName,
   339  		TypedConfig: pbst,
   340  	}
   341  }
   342  
   343  // TestSnapshot holds parameters for a synthetic snapshot.
   344  type TestSnapshot struct {
   345  	// Xds indicates snapshot mode: ads, xds, rest, or delta
   346  	Xds string
   347  	// Version for the snapshot.
   348  	Version string
   349  	// UpstreamPort for the single endpoint on the localhost.
   350  	UpstreamPort uint32
   351  	// BasePort is the initial port for the listeners.
   352  	BasePort uint32
   353  	// NumClusters is the total number of clusters to generate.
   354  	NumClusters int
   355  	// NumHTTPListeners is the total number of HTTP listeners to generate.
   356  	NumHTTPListeners int
   357  	// NumTCPListeners is the total number of TCP listeners to generate.
   358  	// Listeners are assigned clusters in a round-robin fashion.
   359  	NumTCPListeners int
   360  	// NumRuntimes is the total number of RTDS layers to generate.
   361  	NumRuntimes int
   362  	// TLS enables SDS-enabled TLS mode on all listeners
   363  	TLS bool
   364  	// NumExtension is the total number of Extension Config
   365  	NumExtension int
   366  }
   367  
   368  // Generate produces a snapshot from the parameters.
   369  func (ts TestSnapshot) Generate() cache.Snapshot {
   370  	clusters := make([]types.Resource, ts.NumClusters)
   371  	endpoints := make([]types.Resource, ts.NumClusters)
   372  	for i := 0; i < ts.NumClusters; i++ {
   373  		name := fmt.Sprintf("cluster-%s-%d", ts.Version, i)
   374  		clusters[i] = MakeCluster(ts.Xds, name)
   375  		endpoints[i] = MakeEndpoint(name, ts.UpstreamPort)
   376  	}
   377  
   378  	routes := make([]types.Resource, ts.NumHTTPListeners)
   379  	for i := 0; i < ts.NumHTTPListeners; i++ {
   380  		name := fmt.Sprintf("route-%s-%d", ts.Version, i)
   381  		routes[i] = MakeRoute(name, cache.GetResourceName(clusters[i%ts.NumClusters]))
   382  	}
   383  
   384  	total := ts.NumHTTPListeners + ts.NumTCPListeners
   385  	listeners := make([]types.Resource, total)
   386  	for i := 0; i < total; i++ {
   387  		port := ts.BasePort + uint32(i)
   388  		// listener name must be same since ports are shared and previous listener is drained
   389  		name := fmt.Sprintf("listener-%d", port)
   390  		var listener *listener.Listener
   391  		if i < ts.NumHTTPListeners {
   392  			listener = MakeHTTPListener(ts.Xds, name, port, cache.GetResourceName(routes[i]))
   393  		} else {
   394  			listener = MakeTCPListener(name, port, cache.GetResourceName(clusters[i%ts.NumClusters]))
   395  		}
   396  
   397  		if ts.TLS {
   398  			for i, chain := range listener.FilterChains {
   399  				tlsc := &auth.DownstreamTlsContext{
   400  					CommonTlsContext: &auth.CommonTlsContext{
   401  						TlsCertificateSdsSecretConfigs: []*auth.SdsSecretConfig{{
   402  							Name:      tlsName,
   403  							SdsConfig: configSource(ts.Xds),
   404  						}},
   405  						ValidationContextType: &auth.CommonTlsContext_ValidationContextSdsSecretConfig{
   406  							ValidationContextSdsSecretConfig: &auth.SdsSecretConfig{
   407  								Name:      rootName,
   408  								SdsConfig: configSource(ts.Xds),
   409  							},
   410  						},
   411  					},
   412  				}
   413  				mt, _ := ptypes.MarshalAny(tlsc)
   414  				chain.TransportSocket = &core.TransportSocket{
   415  					Name: "envoy.transport_sockets.tls",
   416  					ConfigType: &core.TransportSocket_TypedConfig{
   417  						TypedConfig: mt,
   418  					},
   419  				}
   420  				listener.FilterChains[i] = chain
   421  			}
   422  		}
   423  
   424  		listeners[i] = listener
   425  	}
   426  
   427  	runtimes := make([]types.Resource, ts.NumRuntimes)
   428  	for i := 0; i < ts.NumRuntimes; i++ {
   429  		name := fmt.Sprintf("runtime-%d", i)
   430  		runtimes[i] = MakeRuntime(name)
   431  	}
   432  
   433  	var secrets []types.Resource
   434  	if ts.TLS {
   435  		for _, s := range MakeSecrets(tlsName, rootName) {
   436  			secrets = append(secrets, s)
   437  		}
   438  	}
   439  
   440  	extensions := make([]types.Resource, ts.NumExtension)
   441  	for i := 0; i < ts.NumExtension; i++ {
   442  		routeName := fmt.Sprintf("route-%s-%d", ts.Version, i)
   443  		extensionConfigName := fmt.Sprintf("extensionConfig-%d", i)
   444  		extensions[i] = MakeExtensionConfig(Ads, extensionConfigName, routeName)
   445  	}
   446  
   447  	out, _ := cache.NewSnapshot(ts.Version, map[resource.Type][]types.Resource{
   448  		resource.EndpointType:        endpoints,
   449  		resource.ClusterType:         clusters,
   450  		resource.RouteType:           routes,
   451  		resource.ListenerType:        listeners,
   452  		resource.RuntimeType:         runtimes,
   453  		resource.SecretType:          secrets,
   454  		resource.ExtensionConfigType: extensions,
   455  	})
   456  
   457  	return out
   458  }