get upstream IPs (#16197)

* get upstream IPs

* separate test data

* fix lint issue

* fix lint issue
This commit is contained in:
malizz 2023-02-07 14:57:31 -08:00 committed by GitHub
parent ed7367b6f4
commit 0a544809c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 277 additions and 40 deletions

View File

@ -68,15 +68,20 @@ func (c *cmd) Run(args []string) int {
c.UI.Error("error generating troubleshoot client: " + err.Error()) c.UI.Error("error generating troubleshoot client: " + err.Error())
return 1 return 1
} }
upstreams, err := t.GetUpstreams() envoyIDs, upstreamIPs, err := t.GetUpstreams()
if err != nil { if err != nil {
c.UI.Error("error calling GetUpstreams: " + err.Error()) c.UI.Error("error calling GetUpstreams: " + err.Error())
return 1 return 1
} }
for _, u := range upstreams { for _, u := range envoyIDs {
c.UI.Output(u) c.UI.Output(u)
} }
for _, u := range upstreamIPs {
c.UI.Output(fmt.Sprintf("%+v %v %+v", u.IPs, u.IsVirtual, u.ClusterNames))
}
return 0 return 0
} }

View File

@ -0,0 +1,90 @@
{
"name": "outbound_listener:127.0.0.1:15001",
"address": {
"socket_address": {
"address": "127.0.0.1",
"port_value": 15001
}
},
"filter_chains": [
{
"filter_chain_match": {
"prefix_ranges": [
{
"address_prefix": "10.244.0.63",
"prefix_len": 32
},
{
"address_prefix": "10.244.0.64",
"prefix_len": 32
}
]
},
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"stat_prefix": "upstream.foo.default.default.dc1",
"cluster": "passthrough~foo.default.dc1.internal.dc1.consul"
}
}
]
},
{
"filter_chain_match": {
"prefix_ranges": [
{
"address_prefix": "10.96.5.96",
"prefix_len": 32
},
{
"address_prefix": "240.0.0.1",
"prefix_len": 32
}
]
},
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"stat_prefix": "upstream.foo.default.default.dc1",
"route_config": {
"name": "foo",
"virtual_hosts": [
{
"name": "foo.default.default.dc1",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "foo.default.dc1.internal.dc1.consul"
}
}
]
}
]
},
"http_filters": [
{
"name": "envoy.filters.http.router",
"typed_config": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"random_sampling": {}
}
}
}
]
}
]
}

View File

@ -5,9 +5,9 @@ import (
"net" "net"
envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3" envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"google.golang.org/protobuf/proto"
) )
const ( const (
@ -16,6 +16,7 @@ const (
routes string = "type.googleapis.com/envoy.admin.v3.RoutesConfigDump" routes string = "type.googleapis.com/envoy.admin.v3.RoutesConfigDump"
endpoints string = "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump" endpoints string = "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump"
bootstrap string = "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump" bootstrap string = "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump"
httpConnManager string = "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
) )
type Troubleshoot struct { type Troubleshoot struct {
@ -95,35 +96,3 @@ func (t *Troubleshoot) RunAllTests(envoyID string) ([]string, error) {
} }
return output, resultErr return output, resultErr
} }
func (t *Troubleshoot) GetUpstreams() ([]string, error) {
upstreams := []string{}
err := t.GetEnvoyConfigDump()
if err != nil {
return nil, err
}
for _, cfg := range t.envoyConfigDump.Configs {
switch cfg.TypeUrl {
case listeners:
lcd := &envoy_admin_v3.ListenersConfigDump{}
err := proto.Unmarshal(cfg.GetValue(), lcd)
if err != nil {
return nil, err
}
for _, listener := range lcd.GetDynamicListeners() {
upstream := envoyID(listener.Name)
if upstream != "" && upstream != "public_listener" &&
upstream != "outbound_listener" &&
upstream != "inbound_listener" {
upstreams = append(upstreams, upstream)
}
}
}
}
return upstreams, nil
}

View File

@ -0,0 +1,120 @@
package troubleshoot
import (
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"
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
"google.golang.org/protobuf/proto"
)
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 {
case listeners:
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
}
upstream_ips, err = getUpstreamIPsFromFilterChain(l.GetFilterChains())
if err != nil {
return nil, nil, err
}
}
}
}
}
return upstream_envoy_ids, upstream_ips, nil
}
func getUpstreamIPsFromFilterChain(filterChains []*envoy_listener_v3.FilterChain) ([]UpstreamIP, error) {
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()
clusterNames = extensioncommon.RouteClusterNames(cfg)
}
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
}

View File

@ -0,0 +1,53 @@
package troubleshoot
import (
"io"
"os"
"testing"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protojson"
)
func TestGetUpstreamIPsFromFilterChain(t *testing.T) {
file, err := os.Open("testdata/listeners.json")
require.NoError(t, err)
jsonBytes, err := io.ReadAll(file)
require.NoError(t, err)
expected := []UpstreamIP{
{
IPs: []string{
"10.244.0.63",
"10.244.0.64",
},
IsVirtual: false,
ClusterNames: map[string]struct{}{
"passthrough~foo.default.dc1.internal.dc1.consul": {},
},
},
{
IPs: []string{
"10.96.5.96",
"240.0.0.1",
},
IsVirtual: true,
ClusterNames: map[string]struct{}{
"foo.default.dc1.internal.dc1.consul": {},
},
},
}
var listener envoy_listener_v3.Listener
unmarshal := &protojson.UnmarshalOptions{
DiscardUnknown: true,
}
err = unmarshal.Unmarshal(jsonBytes, &listener)
require.NoError(t, err)
upstream_ips, err := getUpstreamIPsFromFilterChain(listener.GetFilterChains())
require.NoError(t, err)
require.Equal(t, expected, upstream_ips)
}