Skip to content

Commit fb9ea28

Browse files
authored
improve the host OS collector and analyzer (#1743)
The OS version analyzer did not allow checking for things like "redhat 8.x" - this equates to >= 8 && < 9 in the new code. Also, we previously only collected the OS name (like redhat, centos, or ubuntu) not the OS family (which would be rhel, rhel, and debian for the previous OSes) - this greatly reduces the number of cases required in an analyzer.
1 parent 51c3a0c commit fb9ea28

File tree

6 files changed

+142
-10
lines changed

6 files changed

+142
-10
lines changed

examples/sdk/helm-template/go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ require (
6363
gopkg.in/inf.v0 v0.9.1 // indirect
6464
gopkg.in/yaml.v2 v2.4.0 // indirect
6565
gopkg.in/yaml.v3 v3.0.1 // indirect
66-
k8s.io/api v0.32.1 // indirect
67-
k8s.io/apiextensions-apiserver v0.32.1 // indirect
68-
k8s.io/apimachinery v0.32.1 // indirect
69-
k8s.io/client-go v0.32.1 // indirect
66+
k8s.io/api v0.32.2 // indirect
67+
k8s.io/apiextensions-apiserver v0.32.2 // indirect
68+
k8s.io/apimachinery v0.32.2 // indirect
69+
k8s.io/client-go v0.32.2 // indirect
7070
k8s.io/klog/v2 v2.130.1 // indirect
7171
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
7272
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
73-
sigs.k8s.io/controller-runtime v0.20.1 // indirect
73+
sigs.k8s.io/controller-runtime v0.20.2 // indirect
7474
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
7575
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
7676
)

examples/sdk/helm-template/go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,16 @@ helm.sh/helm/v3 v3.17.1 h1:gzVoAD+qVuoJU6KDMSAeo0xRJ6N1znRxz3wyuXRmJDk=
165165
helm.sh/helm/v3 v3.17.1/go.mod h1:nvreuhuR+j78NkQcLC3TYoprCKStLyw5P4T7E5itv2w=
166166
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
167167
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
168+
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
168169
k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
169170
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
171+
k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA=
170172
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
171173
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
174+
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
172175
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
173176
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
177+
k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
174178
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
175179
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
176180
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
@@ -179,6 +183,7 @@ k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6J
179183
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
180184
sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE=
181185
sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU=
186+
sigs.k8s.io/controller-runtime v0.20.2/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
182187
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
183188
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
184189
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=

pkg/analyze/host_os_info.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,46 @@ func (a *AnalyzeHostOS) CheckCondition(when string, data []byte) (bool, error) {
6060
if len(parts) < 3 {
6161
return false, errors.New("when condition must have at least 3 parts")
6262
}
63+
64+
// handle things like "ubuntu == 20.04", but also "ubuntu == 20.04 || < 20.04" or "ubuntu == 20.04 || < 20.04 || >= 20.04" etc
65+
// the number of parts should be a multiple of 3
66+
if len(parts)%3 != 0 {
67+
return false, errors.New("when condition must have a multiple of 3 parts, such as 'ubuntu == 20.04' or 'rhel >= 8 && < 9'")
68+
}
69+
70+
stringToParse := ""
6371
expectedVer := fixVersion(parts[2])
6472
toleratedVer, err := semver.ParseTolerant(expectedVer)
6573
if err != nil {
6674
return false, errors.Wrapf(err, "failed to parse version: %s", expectedVer)
6775
}
68-
when = fmt.Sprintf("%s %v", parts[1], toleratedVer)
69-
whenRange, err := semver.ParseRange(when)
76+
stringToParse = fmt.Sprintf("%s %s", parts[1], toleratedVer.String())
77+
78+
trimmedParts := strings.Split(when, " ")
79+
// read through the next three parts if they exist - this could look like "|| < 20.04" or "&& >= 8"
80+
for len(trimmedParts) > 3 {
81+
trimmedParts = trimmedParts[3:]
82+
83+
expectedVer = fixVersion(trimmedParts[2])
84+
toleratedVer, err = semver.ParseTolerant(expectedVer)
85+
if err != nil {
86+
return false, errors.Wrapf(err, "failed to parse version: %s", expectedVer)
87+
}
88+
89+
// first part is either "||" or "&&"
90+
// if it's "&&", it is assumed by the semver package and should not be included
91+
// second part is the conditional
92+
// third part is the version
93+
if trimmedParts[0] == "||" {
94+
stringToParse = fmt.Sprintf("%s %s %s %s", stringToParse, trimmedParts[0], trimmedParts[1], toleratedVer.String())
95+
} else if trimmedParts[0] == "&&" {
96+
stringToParse = fmt.Sprintf("%s %s %s", stringToParse, trimmedParts[1], toleratedVer.String())
97+
} else {
98+
return false, errors.Errorf("invalid conditional, expected either && or ||, got %q", trimmedParts[0])
99+
}
100+
}
101+
102+
whenRange, err := semver.ParseRange(stringToParse)
70103
if err != nil {
71104
return false, errors.Wrapf(err, "failed to parse version range: %s", when)
72105
}
@@ -97,7 +130,7 @@ func (a *AnalyzeHostOS) CheckCondition(when string, data []byte) (bool, error) {
97130
return true, nil
98131
}
99132
}
100-
} else if platform == osInfo.Platform {
133+
} else if platform == osInfo.Platform || platform == osInfo.PlatformFamily {
101134
fixedDistVer := fixVersion(osInfo.PlatformVersion)
102135
toleratedDistVer, err := semver.ParseTolerant(fixedDistVer)
103136
if err != nil {

pkg/analyze/host_os_info_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,47 @@ func TestAnalyzeHostOSCheckCondition(t *testing.T) {
4949
expected: true,
5050
expectErr: false,
5151
},
52+
{
53+
name: "centos < 8 when actual is 7.2",
54+
conditional: "centos < 8",
55+
osInfo: collect.HostOSInfo{
56+
Platform: "centos",
57+
PlatformVersion: "7.2",
58+
},
59+
expected: true,
60+
expectErr: false,
61+
},
62+
{
63+
name: "centos < 8 when actual is 8.2",
64+
conditional: "centos < 8",
65+
osInfo: collect.HostOSInfo{
66+
Platform: "centos",
67+
PlatformVersion: "8.2",
68+
},
69+
expected: false,
70+
expectErr: false,
71+
},
72+
{
73+
name: "centos < 8 when actual is rhel 7.2", // this tests that we properly exclude other OSes despite the version matching
74+
conditional: "centos < 8",
75+
osInfo: collect.HostOSInfo{
76+
Platform: "redhat",
77+
PlatformVersion: "7.2",
78+
},
79+
expected: false,
80+
expectErr: false,
81+
},
82+
{
83+
name: "rhel == 8.2 when actual is 8.2", // this tests that we match on either platform or platform family
84+
conditional: "rhel == 8.2",
85+
osInfo: collect.HostOSInfo{
86+
Platform: "centos",
87+
PlatformFamily: "rhel",
88+
PlatformVersion: "8.2",
89+
},
90+
expected: true,
91+
expectErr: false,
92+
},
5293
{
5394
name: "ubuntu == 20.04 when actual is 18.04",
5495
conditional: "ubuntu == 20.04",
@@ -69,6 +110,56 @@ func TestAnalyzeHostOSCheckCondition(t *testing.T) {
69110
expected: false,
70111
expectErr: true,
71112
},
113+
{
114+
name: "multiple conditionals, neither true",
115+
conditional: "ubuntu > 20.04 || <= 18.04",
116+
osInfo: collect.HostOSInfo{
117+
Platform: "ubuntu",
118+
PlatformVersion: "20.04",
119+
},
120+
expected: false,
121+
expectErr: false,
122+
},
123+
{
124+
name: "multiple conditionals, first true",
125+
conditional: "ubuntu > 20.04 || <= 18.04",
126+
osInfo: collect.HostOSInfo{
127+
Platform: "ubuntu",
128+
PlatformVersion: "22.04",
129+
},
130+
expected: true,
131+
expectErr: false,
132+
},
133+
{
134+
name: "multiple conditionals, second true",
135+
conditional: "ubuntu > 20.04 || <= 18.04",
136+
osInfo: collect.HostOSInfo{
137+
Platform: "ubuntu",
138+
PlatformVersion: "18.04",
139+
},
140+
expected: true,
141+
expectErr: false,
142+
},
143+
{
144+
name: "multiple conditionals, between the two",
145+
conditional: "redhat >= 8 && < 9",
146+
osInfo: collect.HostOSInfo{
147+
Platform: "redhat",
148+
PlatformVersion: "8.2",
149+
},
150+
expected: true,
151+
expectErr: false,
152+
},
153+
{
154+
name: "multiple conditionals, outside the two",
155+
conditional: "redhat >= 8 && < 9",
156+
osInfo: collect.HostOSInfo{
157+
Platform: "redhat",
158+
PlatformVersion: "9.2",
159+
},
160+
expected: false,
161+
expectErr: false,
162+
},
72163
}
73164

74165
for _, test := range tests {

pkg/collect/host_os_info.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type HostOSInfo struct {
1414
KernelVersion string `json:"kernelVersion"`
1515
PlatformVersion string `json:"platformVersion"`
1616
Platform string `json:"platform"`
17+
PlatformFamily string `json:"platformFamily"`
1718
}
1819

1920
type HostOSInfoNodes struct {
@@ -44,6 +45,7 @@ func (c *CollectHostOS) Collect(progressChan chan<- interface{}) (map[string][]b
4445
}
4546
hostInfo := HostOSInfo{}
4647
hostInfo.Platform = infoStat.Platform
48+
hostInfo.PlatformFamily = infoStat.PlatformFamily
4749
hostInfo.KernelVersion = infoStat.KernelVersion
4850
hostInfo.Name = infoStat.Hostname
4951
hostInfo.PlatformVersion = infoStat.PlatformVersion

pkg/collect/host_os_info_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ func TestCollectHostOS_Collect(t *testing.T) {
2525
require.NoError(t, err)
2626

2727
// Check if values exist. They will be different on different machines.
28-
assert.Equal(t, 4, len(m))
28+
assert.Equal(t, 5, len(m))
2929
assert.Contains(t, m, "name")
3030
assert.Contains(t, m, "kernelVersion")
3131
assert.Contains(t, m, "platformVersion")
32-
assert.Contains(t, m, "platformVersion")
32+
assert.Contains(t, m, "platform")
33+
assert.Contains(t, m, "platformFamily")
3334
}

0 commit comments

Comments
 (0)