diff --git a/collector/zfs.go b/collector/zfs.go index 459cf49..c44524c 100644 --- a/collector/zfs.go +++ b/collector/zfs.go @@ -121,11 +121,7 @@ func (c *ZFS) Collect(ch chan<- prometheus.Metric) { c.ready <- struct{}{} }() - pools, err := c.getPools(c.Pools) - if err != nil { - _ = level.Error(c.logger).Log("msg", "Error finding pools", "err", err) - return - } + pools, poolErr := c.getPools(c.Pools) for name, state := range c.Collectors { if !*state.Enabled { @@ -133,6 +129,12 @@ func (c *ZFS) Collect(ch chan<- prometheus.Metric) { continue } + if poolErr != nil { + c.publishCollectorMetrics(ctx, name, poolErr, 0, proxy) + wg.Done() + continue + } + collector, err := state.factory(c.logger, c.client, strings.Split(*state.Properties, `,`)) if err != nil { _ = level.Error(c.logger).Log("Error instantiating collector", "collector", name, "err", err) @@ -147,7 +149,7 @@ func (c *ZFS) Collect(ch chan<- prometheus.Metric) { // Wait for completion or timeout <-ctx.Done() - err = ctx.Err() + err := ctx.Err() if err == context.Canceled { finalize() } else if err != nil { @@ -206,6 +208,11 @@ func (c *ZFS) execute(ctx context.Context, name string, collector Collector, ch begin := time.Now() err := collector.update(ch, pools, c.excludes) duration := time.Since(begin) + + c.publishCollectorMetrics(ctx, name, err, duration, ch) +} + +func (c *ZFS) publishCollectorMetrics(ctx context.Context, name string, err error, duration time.Duration, ch chan<- metric) { var success float64 if err != nil { diff --git a/collector/zfs_test.go b/collector/zfs_test.go new file mode 100644 index 0000000..b45928f --- /dev/null +++ b/collector/zfs_test.go @@ -0,0 +1,43 @@ +package collector + +import ( + "context" + "fmt" + "testing" + + "github.com/golang/mock/gomock" + "github.com/pdf/zfs_exporter/v2/zfs/mock_zfs" +) + +func TestZFSCollectInvalidPools(t *testing.T) { + const result = `# HELP zfs_scrape_collector_duration_seconds zfs_exporter: Duration of a collector scrape. +# TYPE zfs_scrape_collector_duration_seconds gauge +zfs_scrape_collector_duration_seconds{collector="pool"} 0 +# HELP zfs_scrape_collector_success zfs_exporter: Whether a collector succeeded. +# TYPE zfs_scrape_collector_success gauge +zfs_scrape_collector_success{collector="pool"} 0 +` + + ctrl, ctx := gomock.WithContext(context.Background(), t) + zfsClient := mock_zfs.NewMockClient(ctrl) + zfsClient.EXPECT().PoolNames().Return(nil, fmt.Errorf(`Error returned from PoolNames()`)).Times(1) + + config := defaultConfig(zfsClient) + config.DisableMetrics = false + collector, err := NewZFS(config) + collector.Collectors = map[string]State{ + `pool`: { + Name: "pool", + Enabled: boolPointer(true), + Properties: stringPointer(``), + factory: newPoolCollector, + }, + } + if err != nil { + t.Fatal(err) + } + + if err = callCollector(ctx, collector, []byte(result), []string{`zfs_scrape_collector_duration_seconds`, `zfs_scrape_collector_success`}); err != nil { + t.Fatal(err) + } +}