Skip to content

Commit 142015c

Browse files
authored
feat(analyzer): enable host os info analyzer to support multiple nodes (#1618)
1 parent 83f02f4 commit 142015c

File tree

5 files changed

+208
-33
lines changed

5 files changed

+208
-33
lines changed

pkg/analyze/host_os_info.go

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ type AnalyzeHostOS struct {
1616
hostAnalyzer *troubleshootv1beta2.HostOSAnalyze
1717
}
1818

19+
type NodeOSInfo struct {
20+
NodeName string
21+
collect.HostOSInfo
22+
}
23+
1924
func (a *AnalyzeHostOS) Title() string {
2025
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Host OS Info")
2126
}
@@ -27,30 +32,93 @@ func (a *AnalyzeHostOS) IsExcluded() (bool, error) {
2732
func (a *AnalyzeHostOS) Analyze(
2833
getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents,
2934
) ([]*AnalyzeResult, error) {
30-
35+
var nodesOSInfo []NodeOSInfo
3136
result := AnalyzeResult{}
3237
result.Title = a.Title()
3338

39+
// check if the host os info file exists (local mode)
3440
contents, err := getCollectedFileContents(collect.HostOSInfoPath)
3541
if err != nil {
36-
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
42+
//check if the node list file exists (remote mode)
43+
contents, err := getCollectedFileContents(collect.NODE_LIST_FILE)
44+
if err != nil {
45+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
46+
}
47+
48+
var nodes collect.HostOSInfoNodes
49+
if err := json.Unmarshal(contents, &nodes); err != nil {
50+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to unmarshal host os info nodes")
51+
}
52+
53+
// iterate over each node and analyze the host os info
54+
for _, node := range nodes.Nodes {
55+
contents, err := getCollectedFileContents(collect.NodeInfoBaseDir + "/" + node + "/" + collect.HostInfoFileName)
56+
if err != nil {
57+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
58+
}
59+
60+
var osInfo collect.HostOSInfo
61+
if err := json.Unmarshal(contents, &osInfo); err != nil {
62+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to unmarshal host os info")
63+
}
64+
65+
nodesOSInfo = append(nodesOSInfo, NodeOSInfo{NodeName: node, HostOSInfo: osInfo})
66+
}
67+
68+
results, err := analyzeOSVersionResult(nodesOSInfo, a.hostAnalyzer.Outcomes, a.Title())
69+
if err != nil {
70+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to analyze os version result")
71+
}
72+
return results, nil
3773
}
3874

3975
var osInfo collect.HostOSInfo
4076
if err := json.Unmarshal(contents, &osInfo); err != nil {
4177
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to unmarshal host os info")
4278
}
79+
nodesOSInfo = append(nodesOSInfo, NodeOSInfo{NodeName: "", HostOSInfo: osInfo})
80+
return analyzeOSVersionResult(nodesOSInfo, a.hostAnalyzer.Outcomes, a.Title())
81+
}
82+
83+
func analyzeOSVersionResult(nodesOSInfo []NodeOSInfo, outcomes []*troubleshootv1beta2.Outcome, title string) ([]*AnalyzeResult, error) {
84+
var results []*AnalyzeResult
85+
for _, osInfo := range nodesOSInfo {
86+
if title == "" {
87+
title = "Host OS Info"
88+
}
89+
90+
analyzeResult, err := analyzeByOutcomes(outcomes, osInfo, title)
91+
if err != nil {
92+
return nil, errors.Wrap(err, "failed to analyze condition")
93+
}
94+
results = append(results, analyzeResult...)
95+
}
4396

44-
return analyzeOSVersionResult(osInfo, a.hostAnalyzer.Outcomes, a.Title())
97+
return results, nil
4598
}
4699

47-
func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv1beta2.Outcome, title string) ([]*AnalyzeResult, error) {
100+
var rx = regexp.MustCompile(`^[0-9]+\.?[0-9]*\.?[0-9]*`)
48101

49-
if title == "" {
50-
title = "Host OS Info"
102+
func fixVersion(versionStr string) string {
103+
104+
splitStr := strings.Split(versionStr, ".")
105+
for i := 0; i < len(splitStr); i++ {
106+
if splitStr[i] != "0" {
107+
splitStr[i] = strings.TrimPrefix(splitStr[i], "0")
108+
}
51109
}
110+
fixTrailZero := strings.Join(splitStr, ".")
111+
version := rx.FindString(fixTrailZero)
112+
version = strings.TrimRight(version, ".")
113+
return version
114+
}
52115

116+
func analyzeByOutcomes(outcomes []*troubleshootv1beta2.Outcome, osInfo NodeOSInfo, title string) ([]*AnalyzeResult, error) {
117+
var results []*AnalyzeResult
53118
for _, outcome := range outcomes {
119+
if osInfo.NodeName != "" {
120+
title = fmt.Sprintf("%s - Node %s", title, osInfo.NodeName)
121+
}
54122

55123
result := AnalyzeResult{
56124
Title: title,
@@ -82,7 +150,8 @@ func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv
82150
result.URI = uri
83151
// When is usually empty as the final case and should be treated as true
84152
if when == "" {
85-
return []*AnalyzeResult{&result}, nil
153+
results = append(results, &result)
154+
return results, nil
86155
}
87156

88157
parts := strings.Split(when, " ")
@@ -109,7 +178,8 @@ func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv
109178
return []*AnalyzeResult{}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
110179
}
111180
if whenRange(toleratedKernelVer) {
112-
return []*AnalyzeResult{&result}, nil
181+
results = append(results, &result)
182+
return results, nil
113183
}
114184
}
115185

@@ -125,7 +195,8 @@ func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv
125195
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
126196
}
127197
if whenRange(toleratedKernelVer) {
128-
return []*AnalyzeResult{&result}, nil
198+
results = append(results, &result)
199+
return results, nil
129200
}
130201
}
131202
// Match the platform version
@@ -137,26 +208,10 @@ func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv
137208
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedDistVer)
138209
}
139210
if whenRange(toleratedDistVer) {
140-
return []*AnalyzeResult{&result}, nil
211+
results = append(results, &result)
212+
return results, nil
141213
}
142214
}
143215
}
144-
145-
return []*AnalyzeResult{}, nil
146-
}
147-
148-
var rx = regexp.MustCompile(`^[0-9]+\.?[0-9]*\.?[0-9]*`)
149-
150-
func fixVersion(versionStr string) string {
151-
152-
splitStr := strings.Split(versionStr, ".")
153-
for i := 0; i < len(splitStr); i++ {
154-
if splitStr[i] != "0" {
155-
splitStr[i] = strings.TrimPrefix(splitStr[i], "0")
156-
}
157-
}
158-
fixTrailZero := strings.Join(splitStr, ".")
159-
version := rx.FindString(fixTrailZero)
160-
version = strings.TrimRight(version, ".")
161-
return version
216+
return results, nil
162217
}

pkg/analyze/host_os_info_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,102 @@ func TestAnalyzeHostOS(t *testing.T) {
371371
})
372372
}
373373
}
374+
375+
func TestAnalyzeOSVersionResult(t *testing.T) {
376+
tests := []struct {
377+
name string
378+
outcomes []*troubleshootv1beta2.Outcome
379+
nodeOSInfo []NodeOSInfo
380+
expectResult []*AnalyzeResult
381+
}{
382+
{
383+
name: "pass if ubuntu >= 0.1.2",
384+
nodeOSInfo: []NodeOSInfo{
385+
{
386+
NodeName: "node1",
387+
HostOSInfo: collect.HostOSInfo{
388+
Name: "myhost",
389+
KernelVersion: "5.4.0-1034-gcp",
390+
PlatformVersion: "00.1.2",
391+
Platform: "ubuntu",
392+
},
393+
},
394+
},
395+
outcomes: []*troubleshootv1beta2.Outcome{
396+
{
397+
Pass: &troubleshootv1beta2.SingleOutcome{
398+
When: "ubuntu >= 00.1.2",
399+
Message: "supported distribution matches ubuntu >= 00.1.2",
400+
},
401+
},
402+
{
403+
Fail: &troubleshootv1beta2.SingleOutcome{
404+
Message: "unsupported distribution",
405+
},
406+
},
407+
},
408+
expectResult: []*AnalyzeResult{
409+
{
410+
Title: "Host OS Info - Node node1",
411+
IsPass: true,
412+
Message: "supported distribution matches ubuntu >= 00.1.2",
413+
},
414+
},
415+
},
416+
{
417+
name: "fail if ubuntu <= 11.04",
418+
nodeOSInfo: []NodeOSInfo{
419+
{
420+
NodeName: "node1",
421+
HostOSInfo: collect.HostOSInfo{
422+
Name: "myhost",
423+
KernelVersion: "5.4.0-1034-gcp",
424+
PlatformVersion: "11.04",
425+
Platform: "ubuntu",
426+
},
427+
},
428+
{
429+
NodeName: "node2",
430+
HostOSInfo: collect.HostOSInfo{
431+
Name: "myhost",
432+
KernelVersion: "5.4.0-1034-gcp",
433+
PlatformVersion: "11.04",
434+
Platform: "ubuntu",
435+
},
436+
},
437+
},
438+
outcomes: []*troubleshootv1beta2.Outcome{
439+
{
440+
Fail: &troubleshootv1beta2.SingleOutcome{
441+
When: "ubuntu <= 11.04",
442+
Message: "unsupported ubuntu version 11.04",
443+
},
444+
},
445+
{
446+
Pass: &troubleshootv1beta2.SingleOutcome{
447+
Message: "supported distribution",
448+
},
449+
},
450+
},
451+
expectResult: []*AnalyzeResult{
452+
{
453+
Title: "Host OS Info - Node node1",
454+
IsFail: true,
455+
Message: "unsupported ubuntu version 11.04",
456+
},
457+
{
458+
Title: "Host OS Info - Node node2",
459+
IsFail: true,
460+
Message: "unsupported ubuntu version 11.04",
461+
},
462+
},
463+
},
464+
}
465+
for _, test := range tests {
466+
t.Run(test.name, func(t *testing.T) {
467+
result, err := analyzeOSVersionResult(test.nodeOSInfo, test.outcomes, "Host OS Info")
468+
require.NoError(t, err)
469+
assert.Equal(t, test.expectResult, result)
470+
})
471+
}
472+
}

pkg/collect/collect.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"k8s.io/client-go/rest"
1313
)
1414

15+
const NODE_LIST_FILE = "host-collectors/system/node_list.json"
16+
1517
var (
1618
// ErrCollectorNotFound is returned when an undefined host collector is
1719
// specified by the user.

pkg/collect/host_os_info.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package collect
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
7+
"os"
68

79
"github.com/pkg/errors"
810
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -17,7 +19,13 @@ type HostOSInfo struct {
1719
Platform string `json:"platform"`
1820
}
1921

22+
type HostOSInfoNodes struct {
23+
Nodes []string `json:"nodes"`
24+
}
25+
2026
const HostOSInfoPath = `host-collectors/system/hostos_info.json`
27+
const NodeInfoBaseDir = `host-collectors/system`
28+
const HostInfoFileName = `hostos_info.json`
2129

2230
type CollectHostOS struct {
2331
hostCollector *troubleshootv1beta2.HostOS
@@ -87,9 +95,10 @@ func (c *CollectHostOS) RemoteCollect(progressChan chan<- interface{}) (map[stri
8795
}
8896

8997
output := NewResult()
98+
nodes := []string{}
9099

91100
// save the first result we find in the node and save it
92-
for _, result := range results.AllCollectedData {
101+
for node, result := range results.AllCollectedData {
93102
var nodeResult map[string]string
94103
if err := json.Unmarshal(result, &nodeResult); err != nil {
95104
return nil, errors.Wrap(err, "failed to marshal node results")
@@ -105,10 +114,20 @@ func (c *CollectHostOS) RemoteCollect(progressChan chan<- interface{}) (map[stri
105114
if err != nil {
106115
return nil, errors.Wrap(err, "failed to marshal host os info")
107116
}
108-
output.SaveResult(c.BundlePath, HostOSInfoPath, bytes.NewBuffer(b))
109-
return output, nil
117+
nodes = append(nodes, node)
118+
output.SaveResult(c.BundlePath, fmt.Sprintf("host-collectors/system/%s/%s", node, HostInfoFileName), bytes.NewBuffer(b))
110119
}
111120
}
112121

113-
return nil, errors.New("failed to find host os info")
122+
// check if NODE_LIST_FILE exists
123+
_, err = os.Stat(NODE_LIST_FILE)
124+
// if it not exists, save the nodes list
125+
if err != nil {
126+
nodesBytes, err := json.MarshalIndent(HostOSInfoNodes{Nodes: nodes}, "", " ")
127+
if err != nil {
128+
return nil, errors.Wrap(err, "failed to marshal host os info nodes")
129+
}
130+
output.SaveResult(c.BundlePath, NODE_LIST_FILE, bytes.NewBuffer(nodesBytes))
131+
}
132+
return output, nil
114133
}

test/e2e/support-bundle/hostOS_remote_collector_e2e_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestHostOSRemoteCollector(t *testing.T) {
2020
}{
2121
{
2222
paths: []string{
23-
"hostos_info.json",
23+
"node_list.json",
2424
},
2525
expectType: "file",
2626
},

0 commit comments

Comments
 (0)