2023-03-28 22:48:58 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-28 22:48:58 +00:00
|
|
|
|
2023-02-07 22:57:31 +00:00
|
|
|
package troubleshoot
|
|
|
|
|
|
|
|
import (
|
2023-02-13 20:50:32 +00:00
|
|
|
"fmt"
|
2023-02-17 15:43:05 +00:00
|
|
|
|
2023-02-07 22:57:31 +00:00
|
|
|
envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
|
|
|
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
2023-02-13 20:50:32 +00:00
|
|
|
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
2023-02-07 22:57:31 +00:00
|
|
|
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
2023-02-13 20:50:32 +00:00
|
|
|
"google.golang.org/protobuf/proto"
|
2023-02-07 22:57:31 +00:00
|
|
|
|
|
|
|
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
|
|
|
|
)
|
|
|
|
|
|
|
|
type UpstreamIP struct {
|
|
|
|
IPs []string
|
|
|
|
IsVirtual bool
|
|
|
|
ClusterNames map[string]struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Troubleshoot) GetUpstreams() ([]string, []UpstreamIP, error) {
|
|
|
|
|
|
|
|
upstream_envoy_ids := []string{}
|
|
|
|
upstream_ips := []UpstreamIP{}
|
|
|
|
|
|
|
|
err := t.GetEnvoyConfigDump()
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, cfg := range t.envoyConfigDump.Configs {
|
|
|
|
switch cfg.TypeUrl {
|
2023-02-17 15:43:05 +00:00
|
|
|
case listenersType:
|
2023-02-07 22:57:31 +00:00
|
|
|
lcd := &envoy_admin_v3.ListenersConfigDump{}
|
|
|
|
|
|
|
|
err := proto.Unmarshal(cfg.GetValue(), lcd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, listener := range lcd.GetDynamicListeners() {
|
|
|
|
|
|
|
|
eid := envoyID(listener.Name)
|
|
|
|
|
|
|
|
if eid != "" && eid != "public_listener" &&
|
|
|
|
eid != "outbound_listener" && eid != "inbound_listener" {
|
|
|
|
upstream_envoy_ids = append(upstream_envoy_ids, eid)
|
|
|
|
} else if eid == "outbound_listener" {
|
|
|
|
l := &envoy_listener_v3.Listener{}
|
|
|
|
err = proto.Unmarshal(listener.GetActiveState().GetListener().GetValue(), l)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2023-02-13 20:50:32 +00:00
|
|
|
upstream_ips, err = getUpstreamIPsFromFilterChain(l.GetFilterChains(), t.envoyConfigDump)
|
2023-02-07 22:57:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return upstream_envoy_ids, upstream_ips, nil
|
|
|
|
}
|
|
|
|
|
2023-02-13 20:50:32 +00:00
|
|
|
func getUpstreamIPsFromFilterChain(filterChains []*envoy_listener_v3.FilterChain,
|
|
|
|
cfgDump *envoy_admin_v3.ConfigDump) ([]UpstreamIP, error) {
|
|
|
|
var err error
|
2023-02-07 22:57:31 +00:00
|
|
|
if filterChains == nil {
|
|
|
|
return []UpstreamIP{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
upstreamIPs := []UpstreamIP{}
|
|
|
|
for _, fc := range filterChains {
|
|
|
|
|
|
|
|
if fc.GetFilters() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if fc.GetFilterChainMatch() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if fc.GetFilterChainMatch().GetPrefixRanges() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
cidrs := fc.GetFilterChainMatch().GetPrefixRanges()
|
|
|
|
ips := []string{}
|
|
|
|
|
|
|
|
for _, cidr := range cidrs {
|
|
|
|
ips = append(ips, cidr.AddressPrefix)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, filter := range fc.GetFilters() {
|
|
|
|
isVirtual := false
|
|
|
|
|
|
|
|
if filter.GetTypedConfig() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
clusterNames := map[string]struct{}{}
|
|
|
|
if config := envoy_resource_v3.GetHTTPConnectionManager(filter); config != nil {
|
|
|
|
isVirtual = true
|
|
|
|
cfg := config.GetRouteConfig()
|
|
|
|
|
2023-02-13 20:50:32 +00:00
|
|
|
if cfg != nil {
|
|
|
|
clusterNames = extensioncommon.RouteClusterNames(cfg)
|
|
|
|
} else {
|
|
|
|
// If there are no route configs, look for RDS.
|
|
|
|
routeName := config.GetRds().GetRouteConfigName()
|
|
|
|
if routeName != "" {
|
|
|
|
clusterNames, err = getClustersFromRoutes(routeName, cfgDump)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error in getting clusters for route %q: %w", routeName, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-07 22:57:31 +00:00
|
|
|
}
|
|
|
|
if config := extensioncommon.GetTCPProxy(filter); config != nil {
|
|
|
|
if config.GetCluster() != "" {
|
|
|
|
clusterNames[config.GetCluster()] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
upstreamIPs = append(upstreamIPs, UpstreamIP{
|
|
|
|
IPs: ips,
|
|
|
|
IsVirtual: isVirtual,
|
|
|
|
ClusterNames: clusterNames,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return upstreamIPs, nil
|
|
|
|
}
|
2023-02-13 20:50:32 +00:00
|
|
|
|
|
|
|
func getClustersFromRoutes(routeName string, cfgDump *envoy_admin_v3.ConfigDump) (map[string]struct{}, error) {
|
|
|
|
|
|
|
|
for _, cfg := range cfgDump.Configs {
|
|
|
|
switch cfg.TypeUrl {
|
2023-02-17 15:43:05 +00:00
|
|
|
case routesType:
|
2023-02-13 20:50:32 +00:00
|
|
|
rcd := &envoy_admin_v3.RoutesConfigDump{}
|
|
|
|
|
|
|
|
err := proto.Unmarshal(cfg.GetValue(), rcd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, route := range rcd.GetDynamicRouteConfigs() {
|
|
|
|
|
|
|
|
routeConfig := &envoy_route_v3.RouteConfiguration{}
|
|
|
|
err = proto.Unmarshal(route.GetRouteConfig().GetValue(), routeConfig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if routeConfig.GetName() == routeName {
|
|
|
|
return extensioncommon.RouteClusterNames(routeConfig), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|