// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package xdscommon import ( envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" "github.com/hashicorp/go-hclog" "google.golang.org/protobuf/proto" ) const ( // PublicListenerName is the name we give the public listener in Envoy config. PublicListenerName = "public_listener" // OutboundListenerName is the name we give the outbound Envoy listener when transparent proxy mode is enabled. OutboundListenerName = "outbound_listener" // LocalAppClusterName is the name we give the local application "cluster" in // Envoy config. Note that all cluster names may collide with service names // since we want cluster names and service names to match to enable nice // metrics correlation without massaging prefixes on cluster names. // // We should probably make this more unlikely to collide however changing it // potentially breaks upgrade compatibility without restarting all Envoy's as // it will no longer match their existing cluster name. Changing this will // affect metrics output so could break dashboards (for local app traffic). // // We should probably just make it configurable if anyone actually has // services named "local_app" in the future. LocalAppClusterName = "local_app" // Resource types in xDS v3. These are copied from // envoyproxy/go-control-plane/pkg/resource/v3/resource.go since we don't need any of // the rest of that package. apiTypePrefix = "type.googleapis.com/" // EndpointType is the TypeURL for Endpoint discovery responses. EndpointType = apiTypePrefix + "envoy.config.endpoint.v3.ClusterLoadAssignment" // ClusterType is the TypeURL for Cluster discovery responses. ClusterType = apiTypePrefix + "envoy.config.cluster.v3.Cluster" // RouteType is the TypeURL for Route discovery responses. RouteType = apiTypePrefix + "envoy.config.route.v3.RouteConfiguration" // ListenerType is the TypeURL for Listener discovery responses. ListenerType = apiTypePrefix + "envoy.config.listener.v3.Listener" // SecretType is the TypeURL for Secret discovery responses. SecretType = apiTypePrefix + "envoy.extensions.transport_sockets.tls.v3.Secret" FailoverClusterNamePrefix = "failover-target~" // BlackHoleClusterName is the cluster we use for black-holing traffic for cases when a workload // has no inbound ports to route to. BlackHoleClusterName = "black-hole-cluster" ) type IndexedResources struct { // Index is a map of typeURL => resourceName => resource Index map[string]map[string]proto.Message // ChildIndex is a map of typeURL => parentResourceName => list of // childResourceNames. This only applies if the child and parent do not // share a name. ChildIndex map[string]map[string][]string } // Clone makes a deep copy of the IndexedResources value at the given pointer and // returns a pointer to the copy. func Clone(i *IndexedResources) *IndexedResources { if i == nil { return nil } iCopy := EmptyIndexedResources() for typeURL, typeMap := range i.Index { for name, msg := range typeMap { clone := proto.Clone(msg) iCopy.Index[typeURL][name] = clone } } for typeURL, parentMap := range i.ChildIndex { for name, childName := range parentMap { iCopy.ChildIndex[typeURL][name] = childName } } return iCopy } func IndexResources(logger hclog.Logger, resources map[string][]proto.Message) *IndexedResources { data := EmptyIndexedResources() for typeURL, typeRes := range resources { for _, res := range typeRes { name := GetResourceName(res) if name == "" { logger.Warn("skipping unexpected xDS type found in delta snapshot", "typeURL", typeURL) } else { data.Index[typeURL][name] = res } } } return data } func GetResourceName(res proto.Message) string { // NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS/SDS switch x := res.(type) { case *envoy_listener_v3.Listener: // LDS return x.Name case *envoy_route_v3.RouteConfiguration: // RDS return x.Name case *envoy_cluster_v3.Cluster: // CDS return x.Name case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS return x.ClusterName case *envoy_tls_v3.Secret: // SDS return x.Name default: return "" } } func EmptyIndexedResources() *IndexedResources { return &IndexedResources{ Index: map[string]map[string]proto.Message{ ListenerType: make(map[string]proto.Message), RouteType: make(map[string]proto.Message), ClusterType: make(map[string]proto.Message), EndpointType: make(map[string]proto.Message), SecretType: make(map[string]proto.Message), }, ChildIndex: map[string]map[string][]string{ ListenerType: make(map[string][]string), ClusterType: make(map[string][]string), }, } }