diff --git a/pkg/collector/collection.go b/pkg/collector/collection.go index d424cfa7..cb4e2579 100644 --- a/pkg/collector/collection.go +++ b/pkg/collector/collection.go @@ -280,14 +280,7 @@ func (c *Collection) Build(ctx context.Context, logger *slog.Logger) error { // errors.ErrUnsupported marks a collector whose subsystem is not // available on this host, e.g. a Windows feature that is not installed. - if errors.Is(err, errors.ErrUnsupported) || - errors.Is(err, pdh.ErrNoData) || - errors.Is(err, winregistry.ErrNotExist) || - errors.Is(err, pdh.NewPdhError(pdh.CstatusNoObject)) || - errors.Is(err, pdh.NewPdhError(pdh.CstatusNoCounter)) || - errors.Is(err, mi.MI_RESULT_INVALID_OPERATION_TIMEOUT) || - errors.Is(err, mi.MI_RESULT_INVALID_NAMESPACE) || - errors.Is(err, mi.MI_RESULT_INVALID_CLASS) { + if expectedBuildError(err) { logger.LogAttrs(ctx, slog.LevelWarn, "couldn't initialize collector", slog.Any("err", err), ) @@ -421,3 +414,31 @@ func (c *Collection) WithCollectors(collectors []string) (*Collection, error) { func (c *Collection) GetStartTime() gotime.Time { return c.startTime } + +// expectedBuildError tolerates an error tree only if every independent cause is +// an expected initialization failure for an optional Windows subsystem. +func expectedBuildError(err error) bool { + if joined, ok := err.(interface{ Unwrap() []error }); ok { + causes := joined.Unwrap() + for _, cause := range causes { + if !expectedBuildError(cause) { + return false + } + } + + return len(causes) != 0 + } + + if wrapped, ok := err.(interface{ Unwrap() error }); ok { + return expectedBuildError(wrapped.Unwrap()) + } + + return errors.Is(err, errors.ErrUnsupported) || + errors.Is(err, pdh.ErrNoData) || + errors.Is(err, winregistry.ErrNotExist) || + errors.Is(err, pdh.NewPdhError(pdh.CstatusNoObject)) || + errors.Is(err, pdh.NewPdhError(pdh.CstatusNoCounter)) || + errors.Is(err, mi.MI_RESULT_INVALID_OPERATION_TIMEOUT) || + errors.Is(err, mi.MI_RESULT_INVALID_NAMESPACE) || + errors.Is(err, mi.MI_RESULT_INVALID_CLASS) +} diff --git a/pkg/collector/collection_test.go b/pkg/collector/collection_test.go index f5cd24ea..ec1d5e58 100644 --- a/pkg/collector/collection_test.go +++ b/pkg/collector/collection_test.go @@ -72,6 +72,10 @@ func TestCollectionBuildAndClose(t *testing.T) { {name: "optional counter missing", buildErr: pdh.ErrNoData}, {name: "unsupported on this host", buildErr: fmt.Errorf("feature missing: %w", errors.ErrUnsupported)}, {name: "close failure", closeErr: closeFailure}, + {name: "all expected joined errors", buildErr: errors.Join(errors.ErrUnsupported, pdh.ErrNoData)}, + {name: "joined fatal error", buildErr: errors.Join(errors.ErrUnsupported, buildFailure), wantBuildErr: buildFailure}, + {name: "nested fatal error", buildErr: errors.Join(pdh.ErrNoData, fmt.Errorf("nested: %w", errors.Join(errors.ErrUnsupported, buildFailure))), wantBuildErr: buildFailure}, + {name: "multiple wrapped fatal error", buildErr: fmt.Errorf("%w and %w", errors.ErrUnsupported, buildFailure), wantBuildErr: buildFailure}, } { t.Run(tc.name, func(t *testing.T) { t.Parallel()