Skip to content

Commit 3724546

Browse files
authored
collector/analyzer for host operating system (#443)
* collector/analyzer for host operating system * address cr comments * cleanup * fix invoking the analyzer code cleanup * fix cr comments * add corner case unit-test * fix kernel version parsing * address review comments * add default case * parse using regex * added more testcases and fixed the bug found in cr * few small things
1 parent 5dece3e commit 3724546

File tree

12 files changed

+788
-2
lines changed

12 files changed

+788
-2
lines changed

config/crds/troubleshoot.sh_hostpreflights.yaml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,47 @@ spec:
257257
required:
258258
- outcomes
259259
type: object
260+
hostOS:
261+
properties:
262+
checkName:
263+
type: string
264+
exclude:
265+
type: BoolString
266+
outcomes:
267+
items:
268+
properties:
269+
fail:
270+
properties:
271+
message:
272+
type: string
273+
uri:
274+
type: string
275+
when:
276+
type: string
277+
type: object
278+
pass:
279+
properties:
280+
message:
281+
type: string
282+
uri:
283+
type: string
284+
when:
285+
type: string
286+
type: object
287+
warn:
288+
properties:
289+
message:
290+
type: string
291+
uri:
292+
type: string
293+
when:
294+
type: string
295+
type: object
296+
type: object
297+
type: array
298+
required:
299+
- outcomes
300+
type: object
260301
hostServices:
261302
properties:
262303
checkName:
@@ -758,6 +799,13 @@ spec:
758799
- backgroundWriteIOPSJobs
759800
- enableBackgroundIOPS
760801
type: object
802+
hostOS:
803+
properties:
804+
collectorName:
805+
type: string
806+
exclude:
807+
type: BoolString
808+
type: object
761809
hostServices:
762810
properties:
763811
collectorName:
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
apiVersion: troubleshoot.sh/v1beta2
2+
kind: HostPreflight
3+
metadata:
4+
name: hostos
5+
spec:
6+
collectors:
7+
- hostOS : {}
8+
analyzers:
9+
- hostOS:
10+
outcomes:
11+
- fail:
12+
when: "ubuntu-16.04-kernel < 4.15"
13+
message: unsupported distribution
14+
- pass:
15+
when: "ubuntu-18.04-kernel >= 4.15"
16+
message: supported distribution
17+
- warn:
18+
when: "ubuntu == 16.04"
19+
message: supported distribution
20+
- pass:
21+
when: "ubuntu == 18.04"
22+
message: supported distribution
23+
- pass:
24+
when: "ubuntu == 20.04"
25+
message: supported distribution
26+
- warn:
27+
when: "centos == 7.4"
28+
message: supported distribution
29+
- warn:
30+
when: "centos == 7.5"
31+
message: supported distribution
32+
- warn:
33+
when: "centos == 7.6"
34+
message: supported distribution
35+
- warn:
36+
when: "centos == 7.7"
37+
message: supported distribution
38+
- warn:
39+
when: "centos == 7.8"
40+
message: supported distribution
41+
- pass:
42+
when: "centos == 7.9"
43+
message: supported distribution
44+
- warn:
45+
when: "centos == 8.0"
46+
message: supported distribution
47+
- pass:
48+
when: "centos == 8.1"
49+
message: supported distribution
50+
- pass:
51+
when: "centos == 8.2"
52+
message: supported distribution
53+
- pass:
54+
when: "centos == 8.3"
55+
message: supported distribution
56+
- pass:
57+
when: "centos == 8.4"
58+
message: supported distribution
59+
- fail:
60+
when: "centos > 8.4"
61+
message: unsupported distribution
62+
- warn:
63+
when: "rhel == 7.4"
64+
message: supported distribution
65+
- warn:
66+
when: "rhel == 7.5"
67+
message: supported distribution
68+
- warn:
69+
when: "rhel == 7.6"
70+
message: supported distribution
71+
- warn:
72+
when: "rhel == 7.7"
73+
message: supported distribution
74+
- warn:
75+
when: "rhel == 7.8"
76+
message: supported distribution
77+
- pass:
78+
when: "rhel == 7.9"
79+
message: supported distribution
80+
- warn:
81+
when: "rhel == 8.0"
82+
message: supported distribution
83+
- pass:
84+
when: "rhel == 8.1"
85+
message: supported distribution
86+
- pass:
87+
when: "rhel == 8.2"
88+
message: supported distribution
89+
- pass:
90+
when: "rhel == 8.3"
91+
message: supported distribution
92+
- pass:
93+
when: "rhel == 8.4"
94+
message: supported distribution
95+
- fail:
96+
when: "rhel > 8.4"
97+
message: unsupported distribution
98+
- warn:
99+
when: "ol == 7.4"
100+
message: supported distribution
101+
- warn:
102+
when: "ol == 7.5"
103+
message: supported distribution
104+
- warn:
105+
when: "ol == 7.6"
106+
message: supported distribution
107+
- warn:
108+
when: "ol == 7.7"
109+
message: supported distribution
110+
- warn:
111+
when: "ol == 7.8"
112+
message: supported distribution
113+
- pass:
114+
when: "ol == 7.9"
115+
message: supported distribution
116+
- warn:
117+
when: "ol == 8.0"
118+
message: supported distribution
119+
- pass:
120+
when: "ol == 8.1"
121+
message: supported distribution
122+
- pass:
123+
when: "ol == 8.2"
124+
message: supported distribution
125+
- pass:
126+
when: "ol == 8.3"
127+
message: supported distribution
128+
- pass:
129+
when: "ol == 8.4"
130+
message: supported distribution
131+
- fail:
132+
when: "ol > 8.4"
133+
message: unsupported distribution
134+
- fail:
135+
message: unsupported distribution

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ require (
2323
github.com/google/gofuzz v1.1.0
2424
github.com/hashicorp/go-getter v1.3.1-0.20190627223108-da0323b9545e
2525
github.com/hashicorp/go-multierror v1.1.1
26+
github.com/hashicorp/go-version v1.3.0 // indirect
2627
github.com/kr/text v0.2.0 // indirect
2728
github.com/lib/pq v1.3.0
2829
github.com/longhorn/longhorn-manager v1.1.1

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,9 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
564564
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
565565
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
566566
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
567-
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
568567
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
568+
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
569+
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
569570
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
570571
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
571572
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=

pkg/analyze/host_analyzer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func GetHostAnalyzer(analyzer *troubleshootv1beta2.HostAnalyze) (HostAnalyzer, b
4040
return &AnalyzeHostCertificate{analyzer.Certificate}, true
4141
case analyzer.HostServices != nil:
4242
return &AnalyzeHostServices{analyzer.HostServices}, true
43+
case analyzer.HostOS != nil:
44+
return &AnalyzeHostOS{analyzer.HostOS}, true
4345
default:
4446
return nil, false
4547
}

pkg/analyze/host_os_info.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package analyzer
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"regexp"
7+
"strings"
8+
9+
"github.com/blang/semver"
10+
"github.com/pkg/errors"
11+
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
12+
"github.com/replicatedhq/troubleshoot/pkg/collect"
13+
)
14+
15+
type AnalyzeHostOS struct {
16+
hostAnalyzer *troubleshootv1beta2.HostOSAnalyze
17+
}
18+
19+
func (a *AnalyzeHostOS) Title() string {
20+
return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Host OS Info")
21+
}
22+
23+
func (a *AnalyzeHostOS) IsExcluded() (bool, error) {
24+
return isExcluded(a.hostAnalyzer.Exclude)
25+
}
26+
27+
func (a *AnalyzeHostOS) Analyze(getCollectedFileContents func(string) ([]byte, error)) ([]*AnalyzeResult, error) {
28+
29+
result := AnalyzeResult{}
30+
result.Title = a.Title()
31+
32+
contents, err := getCollectedFileContents("system/hostos_info.json")
33+
if err != nil {
34+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
35+
}
36+
37+
var osInfo collect.HostOSInfo
38+
if err := json.Unmarshal(contents, &osInfo); err != nil {
39+
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to unmarshal host os info")
40+
}
41+
42+
return analyzeOSVersionResult(osInfo, a.hostAnalyzer.Outcomes, a.Title())
43+
}
44+
45+
func analyzeOSVersionResult(osInfo collect.HostOSInfo, outcomes []*troubleshootv1beta2.Outcome, title string) ([]*AnalyzeResult, error) {
46+
47+
if title == "" {
48+
title = "Host OS Info"
49+
}
50+
51+
for _, outcome := range outcomes {
52+
53+
result := AnalyzeResult{
54+
Title: title,
55+
}
56+
when := ""
57+
message := ""
58+
uri := ""
59+
60+
if outcome.Fail != nil {
61+
result.IsFail = true
62+
when = outcome.Fail.When
63+
message = outcome.Fail.Message
64+
uri = outcome.Fail.URI
65+
} else if outcome.Warn != nil {
66+
result.IsWarn = true
67+
when = outcome.Warn.When
68+
message = outcome.Warn.Message
69+
uri = outcome.Warn.URI
70+
} else if outcome.Pass != nil {
71+
result.IsPass = true
72+
when = outcome.Pass.When
73+
message = outcome.Pass.Message
74+
uri = outcome.Pass.URI
75+
} else {
76+
return nil, errors.New("empty outcome")
77+
}
78+
79+
result.Message = message
80+
result.URI = uri
81+
// When is usually empty as the final case and should be treated as true
82+
if when == "" {
83+
return []*AnalyzeResult{&result}, nil
84+
}
85+
86+
parts := strings.Split(when, " ")
87+
platform := parts[0]
88+
expectedVer := fixVersion(parts[2])
89+
toleratedVer, err := semver.ParseTolerant(expectedVer)
90+
if err != nil {
91+
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %s", expectedVer)
92+
}
93+
94+
when = fmt.Sprintf("%s %v", parts[1], toleratedVer)
95+
whenRange, err := semver.ParseRange(when)
96+
if err != nil {
97+
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse range: %s", when)
98+
}
99+
100+
kernelInfo := fmt.Sprintf("%s-%s-kernel", osInfo.Platform, osInfo.PlatformVersion)
101+
if len(strings.Split(platform, "-")) == 3 && strings.Split(platform, "-")[2] == "kernel" {
102+
if platform == kernelInfo {
103+
fixedKernelVer := fixVersion(osInfo.KernelVersion)
104+
toleratedKernelVer, err := semver.ParseTolerant(fixedKernelVer)
105+
if err != nil {
106+
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedKernelVer)
107+
}
108+
if whenRange(toleratedKernelVer) {
109+
return []*AnalyzeResult{&result}, nil
110+
}
111+
}
112+
} else if platform == osInfo.Platform {
113+
fixedDistVer := fixVersion(osInfo.PlatformVersion)
114+
toleratedDistVer, err := semver.ParseTolerant(fixedDistVer)
115+
if err != nil {
116+
return []*AnalyzeResult{&result}, errors.Wrapf(err, "failed to parse tolerant: %v", fixedDistVer)
117+
}
118+
if whenRange(toleratedDistVer) {
119+
return []*AnalyzeResult{&result}, nil
120+
}
121+
}
122+
}
123+
124+
return []*AnalyzeResult{}, nil
125+
}
126+
127+
var rx = regexp.MustCompile(`^[0-9]+\.?[0-9]*\.?[0-9]*`)
128+
129+
func fixVersion(versionStr string) string {
130+
131+
splitStr := strings.Split(versionStr, ".")
132+
for i := 0; i < len(splitStr); i++ {
133+
if splitStr[i] != "0" {
134+
splitStr[i] = strings.TrimPrefix(splitStr[i], "0")
135+
}
136+
}
137+
fixTrailZero := strings.Join(splitStr, ".")
138+
version := rx.FindString(fixTrailZero)
139+
version = strings.TrimRight(version, ".")
140+
return version
141+
}

0 commit comments

Comments
 (0)