Files
troubleshoot/pkg/analyze/weave.go
Evans Mungai e156b8c215 chore: Refactor in cluster analysers (#999)
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
2023-02-03 15:26:36 +00:00

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
}