// 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 ) ,
} ,
}
}