mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-04-15 07:16:34 +00:00
Have all in-cluster analysers implement the same interface. This will help with the implementation of code that requires making calls to all analysers Fixes #995
198 lines
4.9 KiB
Go
198 lines
4.9 KiB
Go
package analyzer
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
|
)
|
|
|
|
// relevant fields from https://github.com/weaveworks/weave/blob/e3712152d2a0fe3bc998964c948e45bdf8ff6144/prog/weaver/http.go#L295
|
|
type WeaveReport struct {
|
|
Router WeaveRouter
|
|
IPAM WeaveIPAM
|
|
}
|
|
|
|
type WeaveRouter struct {
|
|
NickName string // this is the hostname
|
|
Connections []WeaveConnection
|
|
}
|
|
|
|
type WeaveIPAM struct {
|
|
RangeNumIPs int
|
|
ActiveIPs int
|
|
PendingAllocates []string
|
|
}
|
|
|
|
type WeaveConnection struct {
|
|
State string
|
|
Info string
|
|
Attrs WeaveAttributes
|
|
}
|
|
|
|
type WeaveAttributes struct {
|
|
Encrypted bool `json:"encrypted"`
|
|
MTU int `json:"mtu"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type AnalyzeWeaveReport struct {
|
|
analyzer *troubleshootv1beta2.WeaveReportAnalyze
|
|
}
|
|
|
|
func (a *AnalyzeWeaveReport) Title() string {
|
|
return "Weave CNI"
|
|
}
|
|
|
|
func (a *AnalyzeWeaveReport) IsExcluded() (bool, error) {
|
|
return isExcluded(a.analyzer.Exclude)
|
|
}
|
|
|
|
func (a *AnalyzeWeaveReport) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
results, err := analyzeWeaveReport(a.analyzer, findFiles)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range results {
|
|
results[i].Strict = a.analyzer.Strict.BoolOrDefaultFalse()
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
func analyzeWeaveReport(analyzer *troubleshootv1beta2.WeaveReportAnalyze, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
|
|
excludeFiles := []string{}
|
|
files, err := findFiles(analyzer.ReportFileGlob, excludeFiles)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to find weave report files in %q", analyzer.ReportFileGlob)
|
|
}
|
|
|
|
if len(files) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
reports := map[string]WeaveReport{}
|
|
|
|
for name, file := range files {
|
|
report := WeaveReport{}
|
|
|
|
if err := json.Unmarshal(file, &report); err != nil {
|
|
return nil, errors.Wrapf(err, "failed to unmarshal weave report json from %s", name)
|
|
}
|
|
|
|
reports[report.Router.NickName] = report
|
|
}
|
|
|
|
results := []*AnalyzeResult{}
|
|
|
|
if result := analyzeWeaveIPAMPools(reports); result != nil {
|
|
results = append(results, result)
|
|
}
|
|
|
|
if result := analyzeWeavePendingAllocation(reports); result != nil {
|
|
results = append(results, result)
|
|
}
|
|
|
|
if result := analyzeWeaveConnections(reports); result != nil {
|
|
results = append(results, result)
|
|
}
|
|
|
|
if len(results) == 0 {
|
|
results = append(results, &AnalyzeResult{
|
|
Title: "Weave Report",
|
|
IsPass: true,
|
|
Message: "No issues detected in weave report",
|
|
})
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
func analyzeWeaveIPAMPools(reports map[string]WeaveReport) *AnalyzeResult {
|
|
for _, report := range reports {
|
|
if result := analyzeWeaveIPAMPool(&report); result != nil {
|
|
return result
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func analyzeWeaveIPAMPool(report *WeaveReport) *AnalyzeResult {
|
|
if report.IPAM.RangeNumIPs == 0 {
|
|
return nil
|
|
}
|
|
|
|
ipsUsed := float64(report.IPAM.ActiveIPs) / float64(report.IPAM.RangeNumIPs)
|
|
if ipsUsed < 0.85 {
|
|
return nil
|
|
}
|
|
|
|
return &AnalyzeResult{
|
|
Title: "Available Pod IPs",
|
|
IsWarn: true,
|
|
Message: fmt.Sprintf("%d of %d total available IPs have been assigned", report.IPAM.ActiveIPs, report.IPAM.RangeNumIPs),
|
|
}
|
|
}
|
|
|
|
func analyzeWeavePendingAllocation(reports map[string]WeaveReport) *AnalyzeResult {
|
|
for _, report := range reports {
|
|
if len(report.IPAM.PendingAllocates) > 0 {
|
|
return &AnalyzeResult{
|
|
Title: "Pending IP Allocation",
|
|
IsWarn: true,
|
|
Message: "Waiting for IPs to become available",
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Get the peer hostname for logging purposes from a string like: "encrypted fastdp 1a:5b:a9:53:2b:11(areed-aka-kkz0)"
|
|
var weaveConnectionInfoPeerRegex = regexp.MustCompile(`\(([^)]+)\)$`)
|
|
|
|
func parseWeaveConnectionInfoHostname(info string) string {
|
|
matches := weaveConnectionInfoPeerRegex.FindStringSubmatch(info)
|
|
if len(matches) == 2 {
|
|
return matches[1]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func analyzeWeaveConnections(reports map[string]WeaveReport) *AnalyzeResult {
|
|
for host, report := range reports {
|
|
for _, connection := range report.Router.Connections {
|
|
// older versions of weave show connection to self as failed
|
|
if strings.HasPrefix(connection.Info, "cannot connect to ourself") {
|
|
continue
|
|
}
|
|
|
|
peer := parseWeaveConnectionInfoHostname(connection.Info)
|
|
if peer == "" {
|
|
peer = "peer"
|
|
}
|
|
|
|
if connection.State != "established" {
|
|
return &AnalyzeResult{
|
|
Title: "Weave Inter-Node Connections",
|
|
IsWarn: true,
|
|
Message: fmt.Sprintf("Connection from %s to %s is %s", host, peer, connection.State),
|
|
}
|
|
}
|
|
|
|
if connection.Attrs.Name != "" && connection.Attrs.Name != "fastdp" {
|
|
return &AnalyzeResult{
|
|
Title: "Weave Inter-Node Connections",
|
|
IsWarn: true,
|
|
Message: fmt.Sprintf("Connection from %s to %s protocol is %q, not fastdp", host, peer, connection.Attrs.Name),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|