diff --git a/internal/collector/sysinfo/export_test.go b/internal/collector/sysinfo/export_test.go index d3332cf..29a13c8 100644 --- a/internal/collector/sysinfo/export_test.go +++ b/internal/collector/sysinfo/export_test.go @@ -1,18 +1,29 @@ package sysinfo import ( + "log/slog" + "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/hardware" "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/software" ) +// WithHardwareCollector overrides the default hardware collector. func WithHardwareCollector(hw hardware.Collector) Options { return func(o *options) { o.hw = hw } } +// WithSoftwareCollector overrides the default software collector. func WithSoftwareCollector(sw software.Collector) Options { return func(o *options) { o.sw = sw } } + +// WithLogger overrides the default logger. +func WithLogger(logger slog.Handler) Options { + return func(o *options) { + o.log = slog.New(logger) + } +} diff --git a/internal/collector/sysinfo/hardware/hardware_linux_test.go b/internal/collector/sysinfo/hardware/hardware_linux_test.go index d49fd2d..f49dc8c 100644 --- a/internal/collector/sysinfo/hardware/hardware_linux_test.go +++ b/internal/collector/sysinfo/hardware/hardware_linux_test.go @@ -302,13 +302,13 @@ func TestCollectLinux(t *testing.T) { got, err := s.Collect() if tc.wantErr { - require.Error(t, err, "Collect should return an error and didn’t") + require.Error(t, err, "Collect should return an error and didn't") return } require.NoError(t, err, "Collect should not return an error") want := testutils.LoadWithUpdateFromGoldenYAML(t, got) - assert.Equal(t, want, got, "Collect should return expected sys information") + assert.Equal(t, want, got, "Collect should return expected hardware information") if !l.AssertLevels(t, tc.logs) { l.OutputLogs(t) diff --git a/internal/collector/sysinfo/sysinfo.go b/internal/collector/sysinfo/sysinfo.go index e5d228a..bae419d 100644 --- a/internal/collector/sysinfo/sysinfo.go +++ b/internal/collector/sysinfo/sysinfo.go @@ -2,6 +2,9 @@ package sysinfo import ( + "fmt" + "log/slog" + "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/hardware" "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/software" ) @@ -15,15 +18,17 @@ type Collector interface { type Options func(*options) type options struct { - hw hardware.Collector - sw software.Collector + hw hardware.Collector + sw software.Collector + log *slog.Logger } // Manager handles dependencies for collecting software & hardware information. // Manager implements sysinfo.Collector. type Manager struct { - hw hardware.Collector - sw software.Collector + hw hardware.Collector + sw software.Collector + log *slog.Logger } // Info contains Software and Hardware information of the system. @@ -35,8 +40,9 @@ type Info struct { // New returns a new SysInfo. func New(args ...Options) Manager { opts := &options{ - hw: hardware.New(), - sw: software.New(), + hw: hardware.New(), + sw: software.New(), + log: slog.Default(), } for _, opt := range args { @@ -44,20 +50,26 @@ func New(args ...Options) Manager { } return Manager{ - hw: opts.hw, - sw: opts.sw, + hw: opts.hw, + sw: opts.sw, + log: opts.log, } } -// Collect gather system information and return it. +// Collect gathers system information and returns it. +// Will only return an error if both hardware and software collection fail. func (s Manager) Collect() (Info, error) { - hwInfo, err := s.hw.Collect() - if err != nil { - return Info{}, err + hwInfo, hwErr := s.hw.Collect() + swInfo, swErr := s.sw.Collect() + + if hwErr != nil { + s.log.Warn("failed to collect hardware information", "error", hwErr) + } + if swErr != nil { + s.log.Warn("failed to collect software information", "error", swErr) } - swInfo, err := s.sw.Collect() - if err != nil { - return Info{}, err + if hwErr != nil && swErr != nil { + return Info{}, fmt.Errorf("failed to collect system information") } return Info{ diff --git a/internal/collector/sysinfo/sysinfo_test.go b/internal/collector/sysinfo/sysinfo_test.go index b83e0a1..01788b1 100644 --- a/internal/collector/sysinfo/sysinfo_test.go +++ b/internal/collector/sysinfo/sysinfo_test.go @@ -1,12 +1,16 @@ package sysinfo_test import ( + "fmt" + "log/slog" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo" "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/hardware" "github.com/ubuntu/ubuntu-insights/internal/collector/sysinfo/software" + "github.com/ubuntu/ubuntu-insights/internal/testutils" ) // fakeCollector implements Collector (for several interfaces). @@ -26,7 +30,7 @@ func makeFakeCollector[T any](info T, err error) fakeCollector[T] { } } -func TestNewLinux(t *testing.T) { +func TestNew(t *testing.T) { t.Parallel() tests := map[string]struct { @@ -46,3 +50,54 @@ func TestNewLinux(t *testing.T) { }) } } + +func TestCollect(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + hw hardware.Info + hwErr error + + sw software.Info + swErr error + + logs map[slog.Level]uint + }{ + "Hardware and Software error is error": { + hwErr: fmt.Errorf("fake hardware error"), + swErr: fmt.Errorf("fake software error"), + + logs: map[slog.Level]uint{ + slog.LevelWarn: 2, + }, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + + l := testutils.NewMockHandler() + + s := sysinfo.New( + sysinfo.WithHardwareCollector(makeFakeCollector(tc.hw, tc.hwErr)), + sysinfo.WithSoftwareCollector(makeFakeCollector(tc.sw, tc.swErr)), + sysinfo.WithLogger(&l), + ) + + got, err := s.Collect() + + if tc.hwErr != nil && tc.swErr != nil { + require.Error(t, err, "Collect should return an error and didn't") + return + } + require.NoError(t, err, "Collect should not return an error and did") + + want := testutils.LoadWithUpdateFromGoldenYAML(t, got) + assert.Equal(t, want, got, "Collect should return expected sys information") + + if !l.AssertLevels(t, tc.logs) { + l.OutputLogs(t) + } + }) + } +} diff --git a/internal/fileutils/json.go b/internal/fileutils/json.go index e176b94..e35c1dd 100644 --- a/internal/fileutils/json.go +++ b/internal/fileutils/json.go @@ -7,7 +7,7 @@ import ( "io" ) -// parseJSON unmarshals the data in r into v. +// ParseJSON unmarshals the data in r into v. func ParseJSON(r io.Reader, v any) error { // Read the entire content of the io.Reader first to check for errors even if valid json is first. buf, err := io.ReadAll(r)