From 197f6de425a43f68e39ec10e310a9f6577843207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Antunes?= Date: Fri, 8 Nov 2024 18:55:24 +0000 Subject: [PATCH] feat(host_analyzer): add host sysctl analyzer (#1681) * feat(host_analyzer): add host sysctl analyzer * chore: add e2e tests to support bundle collection * chore: missing spec e2e test update * chore: cleanup remote collector and use parse operator * chore: update schemas --- config/crds/troubleshoot.sh_analyzers.yaml | 49 +++ .../crds/troubleshoot.sh_hostcollectors.yaml | 49 +++ .../crds/troubleshoot.sh_hostpreflights.yaml | 49 +++ .../crds/troubleshoot.sh_supportbundles.yaml | 49 +++ examples/preflight/host/sysctl.yaml | 14 +- pkg/analyze/host_analyzer.go | 2 + pkg/analyze/host_sysctl.go | 106 ++++++ pkg/analyze/host_sysctl_test.go | 321 ++++++++++++++++++ pkg/analyze/host_time.go | 2 +- .../v1beta2/hostanalyzer_shared.go | 7 + .../v1beta2/zz_generated.deepcopy.go | 32 ++ pkg/collect/host_sysctl.go | 1 + schemas/analyzer-troubleshoot-v1beta2.json | 76 +++++ .../supportbundle-troubleshoot-v1beta2.json | 76 +++++ .../host_local_collector_e2e_test.go | 1 + .../spec/localHostCollectors.yaml | 1 + .../spec/remoteHostCollectors.yaml | 1 + 17 files changed, 832 insertions(+), 4 deletions(-) create mode 100644 pkg/analyze/host_sysctl.go create mode 100644 pkg/analyze/host_sysctl_test.go diff --git a/config/crds/troubleshoot.sh_analyzers.yaml b/config/crds/troubleshoot.sh_analyzers.yaml index 9151c9954..eabb62b3b 100644 --- a/config/crds/troubleshoot.sh_analyzers.yaml +++ b/config/crds/troubleshoot.sh_analyzers.yaml @@ -2655,6 +2655,55 @@ spec: required: - outcomes type: object + sysctl: + properties: + annotations: + additionalProperties: + type: string + type: object + checkName: + type: string + collectorName: + type: string + exclude: + type: BoolString + outcomes: + items: + properties: + fail: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + pass: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + warn: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + type: object + type: array + strict: + type: BoolString + required: + - outcomes + type: object systemPackages: properties: annotations: diff --git a/config/crds/troubleshoot.sh_hostcollectors.yaml b/config/crds/troubleshoot.sh_hostcollectors.yaml index df14701be..a3c884f5b 100644 --- a/config/crds/troubleshoot.sh_hostcollectors.yaml +++ b/config/crds/troubleshoot.sh_hostcollectors.yaml @@ -895,6 +895,55 @@ spec: required: - outcomes type: object + sysctl: + properties: + annotations: + additionalProperties: + type: string + type: object + checkName: + type: string + collectorName: + type: string + exclude: + type: BoolString + outcomes: + items: + properties: + fail: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + pass: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + warn: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + type: object + type: array + strict: + type: BoolString + required: + - outcomes + type: object systemPackages: properties: annotations: diff --git a/config/crds/troubleshoot.sh_hostpreflights.yaml b/config/crds/troubleshoot.sh_hostpreflights.yaml index 120852ea9..3df2d7db9 100644 --- a/config/crds/troubleshoot.sh_hostpreflights.yaml +++ b/config/crds/troubleshoot.sh_hostpreflights.yaml @@ -895,6 +895,55 @@ spec: required: - outcomes type: object + sysctl: + properties: + annotations: + additionalProperties: + type: string + type: object + checkName: + type: string + collectorName: + type: string + exclude: + type: BoolString + outcomes: + items: + properties: + fail: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + pass: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + warn: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + type: object + type: array + strict: + type: BoolString + required: + - outcomes + type: object systemPackages: properties: annotations: diff --git a/config/crds/troubleshoot.sh_supportbundles.yaml b/config/crds/troubleshoot.sh_supportbundles.yaml index e6ed0e2a2..e0dc56819 100644 --- a/config/crds/troubleshoot.sh_supportbundles.yaml +++ b/config/crds/troubleshoot.sh_supportbundles.yaml @@ -19542,6 +19542,55 @@ spec: required: - outcomes type: object + sysctl: + properties: + annotations: + additionalProperties: + type: string + type: object + checkName: + type: string + collectorName: + type: string + exclude: + type: BoolString + outcomes: + items: + properties: + fail: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + pass: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + warn: + properties: + message: + type: string + uri: + type: string + when: + type: string + type: object + type: object + type: array + strict: + type: BoolString + required: + - outcomes + type: object systemPackages: properties: annotations: diff --git a/examples/preflight/host/sysctl.yaml b/examples/preflight/host/sysctl.yaml index c7a7170f3..888a74573 100644 --- a/examples/preflight/host/sysctl.yaml +++ b/examples/preflight/host/sysctl.yaml @@ -5,6 +5,14 @@ metadata: spec: collectors: - sysctl: - collectorName: sysctl - #TODO add analyzer once implemented - analyzers: [] + collectorName: host sysctl + analyzers: + - sysctl: + collectorName: host sysctl + outcomes: + - warn: + when: 'kern.ostype == Darwin' + message: "Running sysctl on a Darwin host" + - pass: + when: 'net.ipv4.conf.default.arp_ignore > 0' + message: "ARP ignore is enabled for the default interfaces interfaces on the host." diff --git a/pkg/analyze/host_analyzer.go b/pkg/analyze/host_analyzer.go index 47caa3797..4bdada822 100644 --- a/pkg/analyze/host_analyzer.go +++ b/pkg/analyze/host_analyzer.go @@ -63,6 +63,8 @@ func GetHostAnalyzer(analyzer *troubleshootv1beta2.HostAnalyze) (HostAnalyzer, b return &AnalyzeHostJsonCompare{analyzer.JsonCompare}, true case analyzer.NetworkNamespaceConnectivity != nil: return &AnalyzeHostNetworkNamespaceConnectivity{analyzer.NetworkNamespaceConnectivity}, true + case analyzer.Sysctl != nil: + return &AnalyzeHostSysctl{analyzer.Sysctl}, true default: return nil, false } diff --git a/pkg/analyze/host_sysctl.go b/pkg/analyze/host_sysctl.go new file mode 100644 index 000000000..74ff36c13 --- /dev/null +++ b/pkg/analyze/host_sysctl.go @@ -0,0 +1,106 @@ +package analyzer + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/collect" +) + +// Ensure `AnalyzeHostSysctl` implements `HostAnalyzer` interface at compile time. +var _ HostAnalyzer = (*AnalyzeHostSysctl)(nil) + +type AnalyzeHostSysctl struct { + hostAnalyzer *troubleshootv1beta2.HostSysctlAnalyze +} + +func (a *AnalyzeHostSysctl) Title() string { + return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Sysctl") +} + +func (a *AnalyzeHostSysctl) IsExcluded() (bool, error) { + return isExcluded(a.hostAnalyzer.Exclude) +} + +func (a *AnalyzeHostSysctl) Analyze( + getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, +) ([]*AnalyzeResult, error) { + result := AnalyzeResult{Title: a.Title()} + + // Use the generic function to collect both local and remote data + collectedContents, err := retrieveCollectedContents( + getCollectedFileContents, + collect.HostSysctlPath, // Local path + collect.NodeInfoBaseDir, // Remote base directory + collect.HostSysctlFileName, // Remote file name + ) + if err != nil { + return []*AnalyzeResult{&result}, err + } + + results, err := analyzeHostCollectorResults(collectedContents, a.hostAnalyzer.Outcomes, a.CheckCondition, a.Title()) + if err != nil { + return nil, errors.Wrap(err, "failed to analyze sysctl output") + } + + return results, nil +} + +// checkCondition checks the condition of the when clause +func (a *AnalyzeHostSysctl) CheckCondition(when string, data []byte) (bool, error) { + + sysctl := map[string]string{} + if err := json.Unmarshal(data, &sysctl); err != nil { + return false, errors.Wrap(err, "failed to unmarshal data") + } + + // <1:key> <2:operator> <3:value> + matches := sysctlWhenRX.FindStringSubmatch(when) + if len(matches) < 4 { + return false, fmt.Errorf("expected 3 parts in when %q", when) + } + + param := matches[1] + expected := matches[3] + opString := matches[2] + operator, err := ParseComparisonOperator(opString) + if err != nil { + return false, errors.Wrap(err, fmt.Sprintf("failed to parse comparison operator %q", opString)) + } + + if _, ok := sysctl[param]; !ok { + return false, fmt.Errorf("kernel parameter %q does not exist on collected sysctl output", param) + } + + switch operator { + case Equal: + return expected == sysctl[param], nil + } + + // operator used is an inequality operator, the only valid inputs should be ints, if not we'll error out + value, err := strconv.Atoi(sysctl[param]) + if err != nil { + return false, fmt.Errorf("collected sysctl param %q has value %q, cannot be used with provided operator %q", param, sysctl[param], opString) + } + expectedInt, err := strconv.Atoi(expected) + if err != nil { + return false, fmt.Errorf("expected value for sysctl param %q has value %q, cannot be used with provided operator %q", param, expected, opString) + } + + switch operator { + case LessThan: + return value < expectedInt, nil + case LessThanOrEqual: + return value <= expectedInt, nil + case GreaterThan: + return value > expectedInt, nil + case GreaterThanOrEqual: + return value >= expectedInt, nil + default: + return false, fmt.Errorf("unsupported operator %q", opString) + } + +} diff --git a/pkg/analyze/host_sysctl_test.go b/pkg/analyze/host_sysctl_test.go new file mode 100644 index 000000000..fe8118e16 --- /dev/null +++ b/pkg/analyze/host_sysctl_test.go @@ -0,0 +1,321 @@ +package analyzer + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/collect" + "github.com/replicatedhq/troubleshoot/pkg/constants" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAnalyzeHostSysctlCheckCondition(t *testing.T) { + tests := []struct { + name string + conditional string + collected string + expected bool + expectErr string + }{ + { + name: "errors out if we can't unmarshal data", + conditional: "net.ipv4.conf.all.arp_filter = 0", + collected: `{not JSON}`, + expected: false, + expectErr: "failed to unmarshal data", + }, + { + name: "errors out if the matched conditional is missing elements", + conditional: "net.ipv4.conf.all.arp_filter =", + collected: `{}`, + expected: false, + expectErr: `expected 3 parts in when "net.ipv4.conf.all.arp_filter ="`, + }, + { + name: "errors out if the parameter in the condition was not collected", + conditional: "net.ipv4.conf.all.arp_filter = 0", + collected: `{"net.ipv4.conf.all.arp_ignore": "0"}`, + expected: false, + expectErr: `"net.ipv4.conf.all.arp_filter" does not exist on collected sysctl`, + }, + { + name: "errors out if the collected parameter does not support inequalities", + conditional: "net.ipv4.tcp_available_congestion_control > 0", + collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`, + expected: false, + expectErr: `has value "reno cubic", cannot be used with provided operator ">"`, + }, + { + name: "errors out if the provided value for the conditional does not support inequalities", + conditional: "net.ipv4.conf.all.arp_filter > broken", + collected: `{"net.ipv4.conf.all.arp_filter": "0"}`, + expected: false, + expectErr: `has value "broken", cannot be used with provided operator ">"`, + }, + { + name: "errors out if the provided operator is unsupported", + conditional: "net.ipv4.conf.all.arp_filter <== 0", + collected: `{"net.ipv4.conf.all.arp_filter": "0"}`, + expected: false, + expectErr: `failed to parse comparison operator "<=="`, + }, + { + name: "equals with ints", + conditional: "net.ipv4.conf.all.arp_filter = 0", + collected: `{"net.ipv4.conf.all.arp_filter": "0"}`, + expected: true, + }, + { + name: "equals with different data types", + conditional: "net.ipv4.conf.all.arp_filter = will be false", + collected: `{"net.ipv4.conf.all.arp_filter": "0"}`, + expected: false, + }, + { + name: "equals with strings", + conditional: "net.ipv4.tcp_available_congestion_control = reno cubic", + collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`, + expected: true, + }, + { + name: "triple equals works", + conditional: "net.ipv4.tcp_available_congestion_control === reno cubic", + collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`, + expected: true, + }, + { + name: "double equals works", + conditional: "net.ipv4.tcp_available_congestion_control == reno cubic", + collected: `{"net.ipv4.tcp_available_congestion_control": "reno cubic"}`, + expected: true, + }, + { + name: "lower than succeeds", + conditional: "net.ipv4.conf.default.arp_ignore < 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "0"}`, + expected: true, + }, + { + name: "lower than fails", + conditional: "net.ipv4.conf.default.arp_ignore < 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`, + expected: false, + }, + { + name: "lower than or equals succeeds", + conditional: "net.ipv4.conf.default.arp_ignore <= 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`, + expected: true, + }, + { + name: "lower than or equals fails", + conditional: "net.ipv4.conf.default.arp_ignore <= 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "2"}`, + expected: false, + }, + { + name: "higher than succeeds", + conditional: "net.ipv4.conf.default.arp_ignore > 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "2"}`, + expected: true, + }, + { + name: "higher than fails", + conditional: "net.ipv4.conf.default.arp_ignore > 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`, + expected: false, + }, + { + name: "higher than or equals succeeds", + conditional: "net.ipv4.conf.default.arp_ignore >= 1", + collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`, + expected: true, + }, + { + name: "higher than or equals fails", + conditional: "net.ipv4.conf.default.arp_ignore >= 2", + collected: `{"net.ipv4.conf.default.arp_ignore": "1"}`, + expected: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + analyzeHostSysctl := AnalyzeHostSysctl{} + + // JSON encoded sysctl collected output + data := []byte(test.collected) + + // Call the CheckCondition method + result, err := analyzeHostSysctl.CheckCondition(test.conditional, data) + if test.expectErr != "" { + req.ErrorContains(err, test.expectErr) + } else { + req.NoError(err) + } + assert.Equal(t, test.expected, result) + }) + } +} + +func TestAnalyzeHostSysctl(t *testing.T) { + tests := []struct { + name string + hostAnalyzer *troubleshootv1beta2.HostSysctlAnalyze + getCollectedFileContents func(string) ([]byte, error) + expectedResults []*AnalyzeResult + expectedError string + }{ + { + name: "Pass on successful condition (local)", + hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Pass: &troubleshootv1beta2.SingleOutcome{ + When: "net.ipv4.conf.default.arp_ignore >= 1", + Message: "ARP ignore is enabled", + }, + }, + }, + }, + getCollectedFileContents: func(path string) ([]byte, error) { + // Simulate local sysctl content retrieval + if path == collect.HostSysctlPath { + + data := map[string]string{ + "net.ipv4.conf.default.arp_ignore": "2", + } + + return json.Marshal(data) + } + return nil, errors.New("file not found") + }, + expectedResults: []*AnalyzeResult{ + { + Title: "Sysctl", + IsPass: true, + Message: "ARP ignore is enabled", + }, + }, + expectedError: "", + }, + { + name: "Fail on condition (remote node)", + hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Fail: &troubleshootv1beta2.SingleOutcome{ + When: "net.ipv4.conf.default.arp_filter = 0", + Message: "ARP filter is disabled, please enable it via `sysctl net.ipv4.conf.default.arp_filter=1`", + }, + }, + }, + }, + getCollectedFileContents: func(path string) ([]byte, error) { + // Simulate remote node list and sysctl content retrieval + if path == constants.NODE_LIST_FILE { + nodeNames := nodeNames{Nodes: []string{"node1"}} + return json.Marshal(nodeNames) + } + if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostSysctlFileName) { + data := map[string]string{ + "net.ipv4.conf.default.arp_filter": "0", + } + + return json.Marshal(data) + } + return nil, errors.New("file not found") + }, + expectedResults: []*AnalyzeResult{ + { + Title: "Sysctl - Node node1", + IsFail: true, + Message: "ARP filter is disabled, please enable it via `sysctl net.ipv4.conf.default.arp_filter=1`", + }, + }, + expectedError: "", + }, + { + name: "Warn on condition(remote node)", + hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Warn: &troubleshootv1beta2.SingleOutcome{ + When: "net.ipv4.tcp_available_congestion_control = reno cubic", + Message: "Unexpected TCP congestion control algorithm available", + }, + }, + }, + }, + getCollectedFileContents: func(path string) ([]byte, error) { + // Simulate remote node list and sysctl content retrieval + if path == constants.NODE_LIST_FILE { + nodeNames := nodeNames{Nodes: []string{"node1"}} + return json.Marshal(nodeNames) + } + if path == fmt.Sprintf("%s/node1/%s", collect.NodeInfoBaseDir, collect.HostSysctlFileName) { + data := map[string]string{ + "net.ipv4.tcp_available_congestion_control": "reno cubic", + } + + return json.Marshal(data) + } + return nil, errors.New("file not found") + }, + expectedResults: []*AnalyzeResult{ + { + Title: "Sysctl - Node node1", + IsWarn: true, + Message: "Unexpected TCP congestion control algorithm available", + }, + }, + expectedError: "", + }, + { + name: "Return error if collection fails", + hostAnalyzer: &troubleshootv1beta2.HostSysctlAnalyze{ + Outcomes: []*troubleshootv1beta2.Outcome{ + { + Warn: &troubleshootv1beta2.SingleOutcome{ + When: "net.ipv4.tcp_available_congestion_control = reno cubic", + Message: "Unexpected TCP congestion control algorithm available", + }, + }, + }, + }, + getCollectedFileContents: func(path string) ([]byte, error) { + return nil, errors.New("file not found") + }, + expectedResults: []*AnalyzeResult{ + { + Title: "Sysctl", + }, + }, + expectedError: "file not found", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := require.New(t) + + analyzeHostSysctl := AnalyzeHostSysctl{ + hostAnalyzer: test.hostAnalyzer, + } + + results, err := analyzeHostSysctl.Analyze(test.getCollectedFileContents, nil) + + if test.expectedError != "" { + req.ErrorContains(err, test.expectedError) + } else { + req.NoError(err) + } + req.Equal(test.expectedResults, results) + }) + } +} diff --git a/pkg/analyze/host_time.go b/pkg/analyze/host_time.go index 735c39e79..34d2b4bdb 100644 --- a/pkg/analyze/host_time.go +++ b/pkg/analyze/host_time.go @@ -38,7 +38,7 @@ func (a *AnalyzeHostTime) Analyze( getCollectedFileContents, collect.HostTimePath, collect.NodeInfoBaseDir, - collect.HostMemoryFileName, + collect.HostTimeFileName, ) if err != nil { return []*AnalyzeResult{&result}, err diff --git a/pkg/apis/troubleshoot/v1beta2/hostanalyzer_shared.go b/pkg/apis/troubleshoot/v1beta2/hostanalyzer_shared.go index 3d5c92984..02c6d3e7f 100644 --- a/pkg/apis/troubleshoot/v1beta2/hostanalyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/hostanalyzer_shared.go @@ -135,6 +135,12 @@ type KernelConfigsAnalyze struct { Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` } +type HostSysctlAnalyze struct { + AnalyzeMeta `json:",inline" yaml:",inline"` + CollectorName string `json:"collectorName,omitempty" yaml:"collectorName,omitempty"` + Outcomes []*Outcome `json:"outcomes" yaml:"outcomes"` +} + type HostAnalyze struct { CPU *CPUAnalyze `json:"cpu,omitempty" yaml:"cpu,omitempty"` TCPLoadBalancer *TCPLoadBalancerAnalyze `json:"tcpLoadBalancer,omitempty" yaml:"tcpLoadBalancer,omitempty"` @@ -160,4 +166,5 @@ type HostAnalyze struct { KernelConfigs *KernelConfigsAnalyze `json:"kernelConfigs,omitempty" yaml:"kernelConfigs,omitempty"` JsonCompare *JsonCompare `json:"jsonCompare,omitempty" yaml:"jsonCompare,omitempty"` NetworkNamespaceConnectivity *NetworkNamespaceConnectivityAnalyze `json:"networkNamespaceConnectivity,omitempty" yaml:"networkNamespaceConnectivity,omitempty"` + Sysctl *HostSysctlAnalyze `json:"sysctl,omitempty" yaml:"sysctl,omitempty"` } diff --git a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go index 74d0ddb92..7f40bc835 100644 --- a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go @@ -1925,6 +1925,11 @@ func (in *HostAnalyze) DeepCopyInto(out *HostAnalyze) { *out = new(NetworkNamespaceConnectivityAnalyze) (*in).DeepCopyInto(*out) } + if in.Sysctl != nil { + in, out := &in.Sysctl, &out.Sysctl + *out = new(HostSysctlAnalyze) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostAnalyze. @@ -2707,6 +2712,33 @@ func (in *HostSysctl) DeepCopy() *HostSysctl { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostSysctlAnalyze) DeepCopyInto(out *HostSysctlAnalyze) { + *out = *in + in.AnalyzeMeta.DeepCopyInto(&out.AnalyzeMeta) + if in.Outcomes != nil { + in, out := &in.Outcomes, &out.Outcomes + *out = make([]*Outcome, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Outcome) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostSysctlAnalyze. +func (in *HostSysctlAnalyze) DeepCopy() *HostSysctlAnalyze { + if in == nil { + return nil + } + out := new(HostSysctlAnalyze) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HostSystemPackages) DeepCopyInto(out *HostSystemPackages) { *out = *in diff --git a/pkg/collect/host_sysctl.go b/pkg/collect/host_sysctl.go index 08aeb8803..f69c4f632 100644 --- a/pkg/collect/host_sysctl.go +++ b/pkg/collect/host_sysctl.go @@ -19,6 +19,7 @@ var _ HostCollector = (*CollectHostSysctl)(nil) var execCommand = exec.Command const HostSysctlPath = `host-collectors/system/sysctl.json` +const HostSysctlFileName = `sysctl.json` type CollectHostSysctl struct { hostCollector *troubleshootv1beta2.HostSysctl diff --git a/schemas/analyzer-troubleshoot-v1beta2.json b/schemas/analyzer-troubleshoot-v1beta2.json index f6bd0768e..31e556ffe 100644 --- a/schemas/analyzer-troubleshoot-v1beta2.json +++ b/schemas/analyzer-troubleshoot-v1beta2.json @@ -4052,6 +4052,82 @@ } } }, + "sysctl": { + "type": "object", + "required": [ + "outcomes" + ], + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "checkName": { + "type": "string" + }, + "collectorName": { + "type": "string" + }, + "exclude": { + "oneOf": [{"type": "string"},{"type": "boolean"}] + }, + "outcomes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fail": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + }, + "pass": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + }, + "warn": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + } + } + } + }, + "strict": { + "oneOf": [{"type": "string"},{"type": "boolean"}] + } + } + }, "systemPackages": { "type": "object", "required": [ diff --git a/schemas/supportbundle-troubleshoot-v1beta2.json b/schemas/supportbundle-troubleshoot-v1beta2.json index f6dcd8137..387e23727 100644 --- a/schemas/supportbundle-troubleshoot-v1beta2.json +++ b/schemas/supportbundle-troubleshoot-v1beta2.json @@ -18507,6 +18507,82 @@ } } }, + "sysctl": { + "type": "object", + "required": [ + "outcomes" + ], + "properties": { + "annotations": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "checkName": { + "type": "string" + }, + "collectorName": { + "type": "string" + }, + "exclude": { + "oneOf": [{"type": "string"},{"type": "boolean"}] + }, + "outcomes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fail": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + }, + "pass": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + }, + "warn": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "when": { + "type": "string" + } + } + } + } + } + }, + "strict": { + "oneOf": [{"type": "string"},{"type": "boolean"}] + } + } + }, "systemPackages": { "type": "object", "required": [ diff --git a/test/e2e/support-bundle/host_local_collector_e2e_test.go b/test/e2e/support-bundle/host_local_collector_e2e_test.go index 01611761a..787712bf0 100644 --- a/test/e2e/support-bundle/host_local_collector_e2e_test.go +++ b/test/e2e/support-bundle/host_local_collector_e2e_test.go @@ -25,6 +25,7 @@ func TestHostLocalCollector(t *testing.T) { "hostos_info.json", "ipv4Interfaces.json", "memory.json", + "sysctl.json", }, notExpectedPaths: []string{ "node_list.json", diff --git a/test/e2e/support-bundle/spec/localHostCollectors.yaml b/test/e2e/support-bundle/spec/localHostCollectors.yaml index 374fcd5b7..2e4951a84 100644 --- a/test/e2e/support-bundle/spec/localHostCollectors.yaml +++ b/test/e2e/support-bundle/spec/localHostCollectors.yaml @@ -11,6 +11,7 @@ spec: - memory: {} - blockDevices: {} - kernelConfigs: {} + - sysctl: {} - copy: collectorName: etc-resolv path: /etc/resolv.conf diff --git a/test/e2e/support-bundle/spec/remoteHostCollectors.yaml b/test/e2e/support-bundle/spec/remoteHostCollectors.yaml index 698c1ec80..e402aaaf8 100644 --- a/test/e2e/support-bundle/spec/remoteHostCollectors.yaml +++ b/test/e2e/support-bundle/spec/remoteHostCollectors.yaml @@ -12,6 +12,7 @@ spec: - memory: {} - blockDevices: {} - kernelConfigs: {} + - sysctl: {} - copy: collectorName: etc-resolv path: /etc/resolv.conf