2023-03-28 22:48:58 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2023-02-02 22:24:18 +00:00
|
|
|
package troubleshoot
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2023-02-17 15:43:05 +00:00
|
|
|
|
2023-02-02 22:24:18 +00:00
|
|
|
envoy_admin_v3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
|
2023-02-09 20:06:31 +00:00
|
|
|
"github.com/hashicorp/consul/troubleshoot/validate"
|
2023-02-02 22:24:18 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type statsJson struct {
|
|
|
|
Stats []simpleMetric `json:"stats"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type simpleMetric struct {
|
|
|
|
Value int64 `json:"value,omitempty"`
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
}
|
|
|
|
|
2023-02-09 20:06:31 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 15:43:05 +00:00
|
|
|
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),
|
|
|
|
})
|
|
|
|
}
|
2023-02-09 20:06:31 +00:00
|
|
|
|
|
|
|
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,
|
2023-02-17 15:43:05 +00:00
|
|
|
Message: fmt.Sprintf("Envoy has detected %v connection failure(s)", totalCxFailures),
|
2023-02-09 20:06:31 +00:00
|
|
|
})
|
|
|
|
return statMessages, nil
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:24:18 +00:00
|
|
|
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
|
|
|
|
}
|