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 }