From f55ce1e352d5c85612bd78b847ff817c813cc2e8 Mon Sep 17 00:00:00 2001 From: JGAntunes Date: Wed, 6 Nov 2024 16:26:08 +0000 Subject: [PATCH] feat(host_sysctl): add host sysctl collector --- .../v1beta2/hostcollector_shared.go | 4 + .../v1beta2/zz_generated.deepcopy.go | 16 +++ pkg/collect/host_sysctl.go | 45 +++++++ pkg/collect/host_sysctl_test.go | 117 ++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 pkg/collect/host_sysctl.go create mode 100644 pkg/collect/host_sysctl_test.go diff --git a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go index 6cdbb7bc7..e2205c0c1 100644 --- a/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/hostcollector_shared.go @@ -231,6 +231,10 @@ type HostDNS struct { Hostnames []string `json:"hostnames" yaml:"hostnames"` } +type HostSysctl struct { + HostCollectorMeta `json:",inline" yaml:",inline"` +} + type HostCollect struct { CPU *CPU `json:"cpu,omitempty" yaml:"cpu,omitempty"` Memory *Memory `json:"memory,omitempty" yaml:"memory,omitempty"` diff --git a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go index a7e810864..c26134768 100644 --- a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go @@ -2686,6 +2686,22 @@ func (in *HostServicesAnalyze) DeepCopy() *HostServicesAnalyze { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HostSysctl) DeepCopyInto(out *HostSysctl) { + *out = *in + in.HostCollectorMeta.DeepCopyInto(&out.HostCollectorMeta) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostSysctl. +func (in *HostSysctl) DeepCopy() *HostSysctl { + if in == nil { + return nil + } + out := new(HostSysctl) + 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 new file mode 100644 index 000000000..c1bf69d90 --- /dev/null +++ b/pkg/collect/host_sysctl.go @@ -0,0 +1,45 @@ +package collect + +import ( + "bytes" + "os/exec" + + "github.com/pkg/errors" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" +) + +var _ HostCollector = (*CollectHostSysctl)(nil) + +var execCommand = exec.Command + +const HostSysctlPath = `host-collectors/system/sysctl.txt` + +type CollectHostSysctl struct { + hostCollector *troubleshootv1beta2.HostSysctl + BundlePath string +} + +func (c *CollectHostSysctl) Title() string { + return hostCollectorTitleOrDefault(c.hostCollector.HostCollectorMeta, "Sysctl") +} + +func (c *CollectHostSysctl) IsExcluded() (bool, error) { + return isExcluded(c.hostCollector.Exclude) +} + +func (c *CollectHostSysctl) Collect(progressChan chan<- interface{}) (map[string][]byte, error) { + + cmd := execCommand("sysctl", "-a") + out, err := cmd.Output() + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + return nil, errors.Wrapf(err, "failed to run sysctl exit-code=%d stderr=%s", exitErr.ExitCode(), exitErr.Stderr) + } else { + return nil, errors.Wrap(err, "failed to run sysctl") + } + } + + output := NewResult() + output.SaveResult(c.BundlePath, HostSysctlPath, bytes.NewBuffer(out)) + return output, nil +} diff --git a/pkg/collect/host_sysctl_test.go b/pkg/collect/host_sysctl_test.go new file mode 100644 index 000000000..af8b1164c --- /dev/null +++ b/pkg/collect/host_sysctl_test.go @@ -0,0 +1,117 @@ +package collect + +import ( + "io" + "os/exec" + "testing" + + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/multitype" + "github.com/stretchr/testify/require" +) + +type execStub struct { + cmd *exec.Cmd + name string + args []string +} + +func (s *execStub) testExecCommand(name string, args ...string) *exec.Cmd { + s.name = name + s.args = args + return s.cmd +} + +func setExecStub(c *exec.Cmd) { + e := &execStub{ + cmd: c, + } + execCommand = e.testExecCommand +} + +func TestCollectHostSysctl_Error(t *testing.T) { + req := require.New(t) + setExecStub(exec.Command("sh", "-c", "exit 1")) + + tmpDir := t.TempDir() + c := &CollectHostSysctl{ + BundlePath: tmpDir, + } + + _, err := c.Collect(nil) + req.ErrorContains(err, "failed to run sysctl exit-code=1") +} + +func TestCollectHostSysctl(t *testing.T) { + req := require.New(t) + cmdOut := ` + net.ipv4.conf.all.arp_evict_nocarrier = 1 + net.ipv4.conf.all.arp_filter = 0 + net.ipv4.conf.all.arp_ignore = 0 + ` + setExecStub(exec.Command("echo", "-n", cmdOut)) + + tmpDir := t.TempDir() + c := &CollectHostSysctl{ + BundlePath: tmpDir, + } + + out, err := c.Collect(nil) + req.NoError(err) + res := CollectorResult(out) + reader, err := res.GetReader(tmpDir, HostSysctlPath) + req.NoError(err) + actualOut, err := io.ReadAll(reader) + req.NoError(err) + req.Equal(string(actualOut), cmdOut) +} + +func TestCollectHostSysctl_Title(t *testing.T) { + req := require.New(t) + + // Default title is set + c := &CollectHostSysctl{ + hostCollector: &troubleshootv1beta2.HostSysctl{ + HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{}, + }, + } + req.Equal("Sysctl", c.Title()) + + // Configured title is set + c = &CollectHostSysctl{ + hostCollector: &troubleshootv1beta2.HostSysctl{ + HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{ + CollectorName: "foobar", + }, + }, + } + req.Equal("foobar", c.Title()) +} + +func TestCollectHostSysctl_IsExcluded(t *testing.T) { + req := require.New(t) + + // Exclude is true + c := &CollectHostSysctl{ + hostCollector: &troubleshootv1beta2.HostSysctl{ + HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{ + Exclude: multitype.FromBool(true), + }, + }, + } + isExcluded, err := c.IsExcluded() + req.NoError(err) + req.Equal(true, isExcluded) + + // Exclude is false + c = &CollectHostSysctl{ + hostCollector: &troubleshootv1beta2.HostSysctl{ + HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{ + Exclude: multitype.FromBool(false), + }, + }, + } + isExcluded, err = c.IsExcluded() + req.NoError(err) + req.Equal(false, isExcluded) +}