// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package troubleshoot import ( "encoding/json" "fmt" envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3" "github.com/hashicorp/consul/troubleshoot/validate" ) type statsJson struct { Stats []simpleMetric `json:"stats"` } type simpleMetric struct { Value int64 `json:"value,omitempty"` Name string `json:"name,omitempty"` } func (t *Troubleshoot) troubleshootStats() (validate.Messages, error) { statMessages := validate.Messages{} rejectionStats, err := t.getEnvoyStats("update_rejected") if err != nil { return nil, fmt.Errorf("could not get config rejection stats from envoy admin API: %w", err) } totalConfigRejections := 0 for _, rs := range rejectionStats { if rs.Value != 0 { totalConfigRejections += int(rs.Value) } } if totalConfigRejections > 0 { statMessages = append(statMessages, validate.Message{ Message: fmt.Sprintf("Envoy has %v rejected configurations", totalConfigRejections), PossibleActions: []string{ "Check the logs of the Consul agent configuring the local proxy to see why Envoy rejected this configuration", }, }) } else { statMessages = append(statMessages, validate.Message{ Success: true, Message: fmt.Sprintf("Envoy has %v rejected configurations", totalConfigRejections), }) } connectionFailureStats, err := t.getEnvoyStats("upstream_cx_connect_fail") if err != nil { return nil, fmt.Errorf("could not get connection failure stats from envoy admin API: %w", err) } totalCxFailures := 0 for _, cfs := range connectionFailureStats { if cfs.Value != 0 { totalCxFailures += int(cfs.Value) } } statMessages = append(statMessages, validate.Message{ Success: true, Message: fmt.Sprintf("Envoy has detected %v connection failure(s)", totalCxFailures), }) return statMessages, nil } func (t *Troubleshoot) getEnvoyStats(filter string) ([]*envoy_admin_v3.SimpleMetric, error) { var resultErr error jsonRaw, err := t.request(fmt.Sprintf("stats?format=json&filter=%s&type=Counters", filter)) if err != nil { return nil, fmt.Errorf("error in requesting envoy Admin API /stats endpoint: %w", err) } var rawStats statsJson err = json.Unmarshal(jsonRaw, &rawStats) if err != nil { return nil, fmt.Errorf("could not unmarshal /stats response: %w", err) } stats := []*envoy_admin_v3.SimpleMetric{} for _, s := range rawStats.Stats { stat := &envoy_admin_v3.SimpleMetric{ Value: uint64(s.Value), Name: s.Name, } stats = append(stats, stat) } t.envoyStats = stats return stats, resultErr }