Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Andrew Lavery <laverya@umich.edu>
- Loading branch information
Showing
8 changed files
with
347 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
apiVersion: troubleshoot.sh/v1beta2 | ||
kind: HostPreflight | ||
metadata: | ||
name: ipv4Interfaces | ||
spec: | ||
collectors: | ||
- ipv4Interfaces: {} | ||
analyzers: | ||
- ipv4Interfaces: | ||
outcomes: | ||
- fail: | ||
when: "count == 0" | ||
message: No IPv4 interfaces detected | ||
- warn: | ||
when: "count >= 2" | ||
message: Multiple IPv4 interfaces detected | ||
- pass: | ||
when: "count == 1" | ||
message: IPv4 interface detected |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package analyzer | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" | ||
) | ||
|
||
func analyzeHostIPV4Interfaces(hostAnalyzer *troubleshootv1beta2.IPV4InterfacesAnalyze, getCollectedFileContents func(string) ([]byte, error)) (*AnalyzeResult, error) { | ||
contents, err := getCollectedFileContents("system/ipv4Interfaces.json") | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to get collected file") | ||
} | ||
|
||
var ipv4Interfaces []net.Interface | ||
if err := json.Unmarshal(contents, &ipv4Interfaces); err != nil { | ||
return nil, errors.Wrap(err, "failed to unmarshal ipv4Interfaces") | ||
} | ||
|
||
result := AnalyzeResult{} | ||
|
||
title := hostAnalyzer.CheckName | ||
if title == "" { | ||
title = "IPv4 Interfaces" | ||
} | ||
result.Title = title | ||
|
||
for _, outcome := range hostAnalyzer.Outcomes { | ||
if outcome.Fail != nil { | ||
if outcome.Fail.When == "" { | ||
result.IsFail = true | ||
result.Message = outcome.Fail.Message | ||
result.URI = outcome.Fail.URI | ||
|
||
return &result, nil | ||
} | ||
|
||
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Fail.When, ipv4Interfaces) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Fail.When) | ||
} | ||
|
||
if isMatch { | ||
result.IsFail = true | ||
result.Message = outcome.Fail.Message | ||
result.URI = outcome.Fail.URI | ||
|
||
return &result, nil | ||
} | ||
} else if outcome.Warn != nil { | ||
if outcome.Warn.When == "" { | ||
result.IsWarn = true | ||
result.Message = outcome.Warn.Message | ||
result.URI = outcome.Warn.URI | ||
|
||
return &result, nil | ||
} | ||
|
||
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Warn.When, ipv4Interfaces) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Warn.When) | ||
} | ||
|
||
if isMatch { | ||
result.IsWarn = true | ||
result.Message = outcome.Warn.Message | ||
result.URI = outcome.Warn.URI | ||
|
||
return &result, nil | ||
} | ||
} else if outcome.Pass != nil { | ||
if outcome.Pass.When == "" { | ||
result.IsPass = true | ||
result.Message = outcome.Pass.Message | ||
result.URI = outcome.Pass.URI | ||
|
||
return &result, nil | ||
} | ||
|
||
isMatch, err := compareHostIPV4InterfacesConditionalToActual(outcome.Pass.When, ipv4Interfaces) | ||
if err != nil { | ||
return nil, errors.Wrapf(err, "failed to compare %s", outcome.Pass.When) | ||
} | ||
|
||
if isMatch { | ||
result.IsPass = true | ||
result.Message = outcome.Pass.Message | ||
result.URI = outcome.Pass.URI | ||
|
||
return &result, nil | ||
} | ||
} | ||
} | ||
|
||
return &result, nil | ||
} | ||
|
||
func compareHostIPV4InterfacesConditionalToActual(conditional string, ipv4Interfaces []net.Interface) (res bool, err error) { | ||
parts := strings.Split(conditional, " ") | ||
if len(parts) != 3 { | ||
return false, fmt.Errorf("Expected exactly 3 parts in conditional, got %d", len(parts)) | ||
} | ||
|
||
keyword := parts[0] | ||
operator := parts[1] | ||
desired := parts[2] | ||
|
||
if keyword != "count" { | ||
return false, fmt.Errorf(`Only supported keyword is "count", got %q`, keyword) | ||
} | ||
|
||
desiredInt, err := strconv.ParseInt(desired, 10, 64) | ||
if err != nil { | ||
return false, errors.Wrapf(err, "failed to parse %q as int", desired) | ||
} | ||
|
||
actualCount := len(ipv4Interfaces) | ||
|
||
switch operator { | ||
case "<": | ||
return actualCount < int(desiredInt), nil | ||
case "<=": | ||
return actualCount <= int(desiredInt), nil | ||
case ">": | ||
return actualCount > int(desiredInt), nil | ||
case ">=": | ||
return actualCount >= int(desiredInt), nil | ||
case "=", "==", "===": | ||
return actualCount == int(desiredInt), nil | ||
} | ||
|
||
return false, fmt.Errorf("Unknown operator %q. Supported operators are: <, <=, ==, >=, >", operator) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package analyzer | ||
|
||
import ( | ||
"encoding/json" | ||
"net" | ||
"testing" | ||
|
||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestAnalyzeIPV4Interfaces(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
interfaces []net.Interface | ||
hostAnalyzer *troubleshootv1beta2.IPV4InterfacesAnalyze | ||
result *AnalyzeResult | ||
expectErr bool | ||
}{ | ||
{ | ||
name: "fail when no ipv4 interfaces detected", | ||
interfaces: nil, | ||
hostAnalyzer: &troubleshootv1beta2.IPV4InterfacesAnalyze{ | ||
Outcomes: []*troubleshootv1beta2.Outcome{ | ||
{ | ||
Pass: &troubleshootv1beta2.SingleOutcome{ | ||
When: "count > 0", | ||
Message: "IPv4 interface available", | ||
}, | ||
}, | ||
{ | ||
Fail: &troubleshootv1beta2.SingleOutcome{ | ||
When: "count == 0", | ||
Message: "No IPv4 interfaces detected", | ||
}, | ||
}, | ||
}, | ||
}, | ||
result: &AnalyzeResult{ | ||
Title: "IPv4 Interfaces", | ||
IsFail: true, | ||
Message: "No IPv4 interfaces detected", | ||
}, | ||
}, | ||
{ | ||
name: "pass when ipv4 interfaces detected", | ||
interfaces: []net.Interface{ | ||
{ | ||
Index: 1, | ||
MTU: 1460, | ||
HardwareAddr: net.HardwareAddr("42010a80001d"), | ||
Name: "ens4", | ||
}, | ||
}, | ||
hostAnalyzer: &troubleshootv1beta2.IPV4InterfacesAnalyze{ | ||
Outcomes: []*troubleshootv1beta2.Outcome{ | ||
{ | ||
Fail: &troubleshootv1beta2.SingleOutcome{ | ||
When: "count == 0", | ||
Message: "No IPv4 interfaces detected", | ||
}, | ||
}, | ||
{ | ||
Pass: &troubleshootv1beta2.SingleOutcome{ | ||
When: "count > 0", | ||
Message: "IPv4 interface available", | ||
}, | ||
}, | ||
}, | ||
}, | ||
result: &AnalyzeResult{ | ||
Title: "IPv4 Interfaces", | ||
IsPass: true, | ||
Message: "IPv4 interface available", | ||
}, | ||
}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
req := require.New(t) | ||
b, err := json.Marshal(test.interfaces) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
getCollectedFileContents := func(filename string) ([]byte, error) { | ||
return b, nil | ||
} | ||
|
||
result, err := analyzeHostIPV4Interfaces(test.hostAnalyzer, getCollectedFileContents) | ||
if test.expectErr { | ||
req.Error(err) | ||
} else { | ||
req.NoError(err) | ||
} | ||
|
||
assert.Equal(t, test.result, result) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package collect | ||
|
||
import ( | ||
"encoding/json" | ||
"net" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
func HostIPV4Interfaces(c *HostCollector) (map[string][]byte, error) { | ||
var ipv4Interfaces []net.Interface | ||
|
||
interfaces, err := net.Interfaces() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "list host network interfaces") | ||
} | ||
|
||
for _, iface := range interfaces { | ||
if iface.Flags&net.FlagUp == 0 { | ||
continue | ||
} | ||
if iface.Flags&net.FlagLoopback != 0 { | ||
continue | ||
} | ||
ip, _ := getIPv4FromInterface(&iface) | ||
if ip == nil { | ||
continue | ||
} | ||
ipv4Interfaces = append(ipv4Interfaces, iface) | ||
} | ||
|
||
b, err := json.Marshal(ipv4Interfaces) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to marshal network interfaces") | ||
} | ||
|
||
return map[string][]byte{ | ||
"system/ipv4Interfaces.json": b, | ||
}, nil | ||
} |