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 }