Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
3.2 KiB

package troubleshoot
import (
"fmt"
"net"
envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/go-multierror"
"google.golang.org/protobuf/proto"
)
const (
listeners string = "type.googleapis.com/envoy.admin.v3.ListenersConfigDump"
clusters string = "type.googleapis.com/envoy.admin.v3.ClustersConfigDump"
routes string = "type.googleapis.com/envoy.admin.v3.RoutesConfigDump"
endpoints string = "type.googleapis.com/envoy.admin.v3.EndpointsConfigDump"
bootstrap string = "type.googleapis.com/envoy.admin.v3.BootstrapConfigDump"
)
type Troubleshoot struct {
client *api.Client
envoyAddr net.IPAddr
envoyAdminPort string
TroubleshootInfo
}
type TroubleshootInfo struct {
envoyClusters *envoy_admin_v3.Clusters
envoyConfigDump *envoy_admin_v3.ConfigDump
envoyCerts *envoy_admin_v3.Certificates
envoyStats []*envoy_admin_v3.SimpleMetric
}
func NewTroubleshoot(envoyIP *net.IPAddr, envoyPort string) (*Troubleshoot, error) {
cfg := api.DefaultConfig()
c, err := api.NewClient(cfg)
if err != nil {
return nil, err
}
return &Troubleshoot{
client: c,
envoyAddr: *envoyIP,
envoyAdminPort: envoyPort,
}, nil
}
func (t *Troubleshoot) RunAllTests(envoyID string) ([]string, error) {
var resultErr error
var output []string
// Validate certs
certs, err := t.getEnvoyCerts()
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to get certs: %w", err))
}
if certs != nil && len(certs.GetCertificates()) != 0 {
err = t.validateCerts(certs)
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to validate certs: %w", err))
} else {
output = append(output, "certs are valid")
}
} else {
resultErr = multierror.Append(resultErr, fmt.Errorf("no certificate found"))
}
// getStats usage example
// rejectionStats, err := t.getEnvoyStats("update_rejected")
// if err != nil {
// resultErr = multierror.Append(resultErr, err)
// }
// Validate listeners, routes, clusters, endpoints
t.GetEnvoyConfigDump()
t.getEnvoyClusters()
indexedResources, err := ProxyConfigDumpToIndexedResources(t.envoyConfigDump)
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to index resources: %v", err))
}
err = Validate(indexedResources, envoyID, "", true, t.envoyClusters)
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to validate proxy config: %v", err))
}
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
}