From 1dbd47dec822388b08befc266820445153669a42 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 24 Mar 2026 11:01:08 +0100 Subject: [PATCH 1/5] Add `Health` to audit interface to check backend health. --- auditing/async.go | 6 +++ auditing/async_test.go | 25 +++++++++-- auditing/auditing.go | 12 ++---- auditing/memory.go | 14 +++++- auditing/splunk.go | 96 +++++++++++++++++++++++++++++++++++++---- auditing/splunk_test.go | 41 ++++++++++++++++++ auditing/timescaledb.go | 23 +++++++++- 7 files changed, 194 insertions(+), 23 deletions(-) diff --git a/auditing/async.go b/auditing/async.go index 0ffa3cb6..b1dc452c 100644 --- a/auditing/async.go +++ b/auditing/async.go @@ -5,6 +5,8 @@ import ( "fmt" "log/slog" "time" + + "github.com/metal-stack/metal-lib/pkg/healthstatus" ) const ( @@ -84,3 +86,7 @@ func (a *asyncAuditing) Index(entry Entry) error { func (a *asyncAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entry, error) { return a.a.Search(ctx, filter) } + +func (a *asyncAuditing) Health(ctx context.Context) *healthstatus.HealthResult { + return a.a.Health(ctx) +} diff --git a/auditing/async_test.go b/auditing/async_test.go index d526182a..21a74367 100644 --- a/auditing/async_test.go +++ b/auditing/async_test.go @@ -3,12 +3,14 @@ package auditing import ( "context" "errors" + "fmt" "log/slog" "os" "sync" "testing" "time" + "github.com/metal-stack/metal-lib/pkg/healthstatus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -110,10 +112,11 @@ func Test_asyncAuditing_Index(t *testing.T) { } type testBackend struct { - mutex sync.Mutex - done chan bool - count int - idxFn func(count int) error + mutex sync.Mutex + done chan bool + count int + idxFn func(count int) error + health error } func (t *testBackend) Index(e Entry) error { @@ -135,3 +138,17 @@ func (t *testBackend) Index(e Entry) error { func (t *testBackend) Search(ctx context.Context, filter EntryFilter) ([]Entry, error) { panic("not required") } + +func (t *testBackend) Health(context.Context) *healthstatus.HealthResult { + if t.health != nil { + return &healthstatus.HealthResult{ + Status: healthstatus.HealthStatusUnhealthy, + Message: fmt.Sprintf("audit backend is unhealthy: %s", t.health.Error()), + } + } + + return &healthstatus.HealthResult{ + Status: healthstatus.HealthStatusHealthy, + Message: "audit backend is healthy", + } +} diff --git a/auditing/auditing.go b/auditing/auditing.go index 3eb5b228..71a2bbe9 100644 --- a/auditing/auditing.go +++ b/auditing/auditing.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" "time" + + "github.com/metal-stack/metal-lib/pkg/healthstatus" ) type Config struct { @@ -15,14 +17,6 @@ type Config struct { IndexTimeout time.Duration } -type Interval string - -var ( - HourlyInterval Interval = "@hourly" - DailyInterval Interval = "@daily" - MonthlyInterval Interval = "@monthly" -) - type EntryType string const ( @@ -135,6 +129,8 @@ type Auditing interface { // By default only recent entries will be returned. // The returned entries will be sorted by timestamp in descending order. Search(context.Context, EntryFilter) ([]Entry, error) + // Health returns info about the audit backend health. + Health(ctx context.Context) *healthstatus.HealthResult } func defaultComponent() (string, error) { diff --git a/auditing/memory.go b/auditing/memory.go index 5c5b6d11..1661ba4b 100644 --- a/auditing/memory.go +++ b/auditing/memory.go @@ -10,6 +10,11 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/metal-stack/metal-lib/pkg/healthstatus" +) + +const ( + MemoryBackendName = "memory" ) type ( @@ -43,7 +48,7 @@ func NewMemory(c Config, mc MemoryConfig) (Auditing, error) { a := &memoryAuditing{ component: c.Component, - log: c.Log.WithGroup("auditing").With("audit-backend", "memory"), + log: c.Log.WithGroup("auditing").With("audit-backend", MemoryBackendName), memory: []Entry{}, config: &mc, } @@ -201,3 +206,10 @@ func (a *memoryAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entr return entries, nil } + +func (a *memoryAuditing) Health(context.Context) *healthstatus.HealthResult { + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is healthy", MemoryBackendName), + Status: healthstatus.HealthStatusHealthy, + } +} diff --git a/auditing/splunk.go b/auditing/splunk.go index 156ac629..601b1523 100644 --- a/auditing/splunk.go +++ b/auditing/splunk.go @@ -7,12 +7,21 @@ import ( "encoding/json" "errors" "fmt" + "io" "log/slog" "net/http" "time" + + "github.com/metal-stack/metal-lib/pkg/healthstatus" ) -const spunkIndexTimeout = 5 * time.Second +const ( + SplunkBackendName = "splunk" + + spunkIndexTimeout = 5 * time.Second + // See on their docs in troubleshoot-http-event-collector (Possible_error_codes) + splunkHealthyCode = 17 +) type ( SplunkConfig struct { @@ -52,6 +61,12 @@ type ( // Event is the actual event data in whatever format you want: a string, a number, another JSON object, and so on. Event Entry `json:"event,omitempty"` } + + splunkRequestEndpoint struct { + path string + method string + body []byte + } ) // NewSplunk returns a new auditing backend for splunk. It supports the HTTP event collector interface. @@ -91,7 +106,7 @@ func NewSplunk(c Config, sc SplunkConfig) (Auditing, error) { a := &splunkAuditing{ component: c.Component, - log: c.Log.WithGroup("auditing").With("audit-backend", "splunk"), + log: c.Log.WithGroup("auditing").With("audit-backend", SplunkBackendName), indexTimeout: c.IndexTimeout, client: &http.Client{Transport: &http.Transport{TLSClientConfig: sc.TlsConfig}}, endpoint: endpoint, @@ -128,24 +143,89 @@ func (a *splunkAuditing) Index(entry Entry) error { ctx, cancel := context.WithTimeout(context.Background(), a.indexTimeout) defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.endpoint+"/services/collector", bytes.NewBuffer(e)) + _, err = a.splunkRequest(ctx, splunkRequestEndpoint{ + path: "/services/collector", + method: http.MethodPost, + body: e, + }) if err != nil { return err } + return nil +} + +func (a *splunkAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entry, error) { + return nil, fmt.Errorf("search not implemented for splunk audit backend") +} + +func (a *splunkAuditing) Health(ctx context.Context) *healthstatus.HealthResult { + resp, err := a.splunkRequest(ctx, splunkRequestEndpoint{ + path: "/services/collector/health", + method: http.MethodGet, + body: nil, + }) + if err != nil { + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is unhealthy, collector is unhealthy: %s", SplunkBackendName, err.Error()), + Status: healthstatus.HealthStatusUnhealthy, + } + } + + type healthResp struct { + Text string `json:"text"` + Code int `json:"code"` + } + + health := healthResp{} + + if err := json.Unmarshal(resp, &health); err != nil { + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is unhealthy, unable to unmarshal health response: %s", SplunkBackendName, err.Error()), + Status: healthstatus.HealthStatusUnhealthy, + } + } + + if health.Code != splunkHealthyCode { + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is degraded: %s", SplunkBackendName, health.Text), + Status: healthstatus.HealthStatusDegraded, + } + } + + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is healthy: %s", SplunkBackendName, health.Text), + Status: healthstatus.HealthStatusHealthy, + } +} + +func (a *splunkAuditing) splunkRequest(ctx context.Context, ep splunkRequestEndpoint) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), a.indexTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, ep.method, a.endpoint+ep.path, bytes.NewBuffer(ep.body)) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Splunk "+a.hecToken) resp, err := a.client.Do(req) if err != nil { - return fmt.Errorf("error indexing audit entry in splunk: %w", err) + return nil, fmt.Errorf("error indexing audit entry in splunk: %w", err) } defer func() { _ = resp.Body.Close() }() - return nil -} + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response: %w", err) + } -func (a *splunkAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entry, error) { - return nil, fmt.Errorf("search not implemented for splunk audit backend") + if code := resp.StatusCode; code >= http.StatusBadRequest { + return nil, fmt.Errorf("splunk endpoint %q did not return ok (%d)", ep.path, code) + } + + return bytes, nil } diff --git a/auditing/splunk_test.go b/auditing/splunk_test.go index 21f61ec0..76edc013 100644 --- a/auditing/splunk_test.go +++ b/auditing/splunk_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/metal-stack/metal-lib/pkg/healthstatus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -101,3 +102,43 @@ func Test_splunkAuditing_Index(t *testing.T) { }) } } + +func Test_splunkAuditing_Health(t *testing.T) { + tests := []struct { + name string + want *healthstatus.HealthResult + }{ + { + name: "healthy", + want: &healthstatus.HealthResult{ + Status: healthstatus.HealthStatusHealthy, + Message: `audit backend "splunk" is healthy: HEC is healthy`, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/services/collector/health", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(`{"text":"HEC is healthy","code":17}`)) + w.WriteHeader(http.StatusOK) + }) + server := httptest.NewServer(mux) + defer server.Close() + + a, err := NewSplunk(Config{ + Component: "metal-lib", + Log: slog.Default(), + }, SplunkConfig{ + Endpoint: server.URL, + HECToken: "test-hec", + Index: "test-index", + Host: "test-host", + }) + require.NoError(t, err) + + got := a.Health(t.Context()) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/auditing/timescaledb.go b/auditing/timescaledb.go index 856f2034..ff9532b4 100644 --- a/auditing/timescaledb.go +++ b/auditing/timescaledb.go @@ -12,11 +12,16 @@ import ( "github.com/jmoiron/sqlx" "github.com/lopezator/migrator" + "github.com/metal-stack/metal-lib/pkg/healthstatus" _ "github.com/lib/pq" ) -const timescaleDbIndexTimeout = 2 * time.Second +const ( + TimescaleDbBackendName = "timescaledb" + + timescaleDbIndexTimeout = 2 * time.Second +) type ( TimescaleDbConfig struct { @@ -106,7 +111,7 @@ func NewTimescaleDB(c Config, tc TimescaleDbConfig) (Auditing, error) { a := ×caleAuditing{ component: c.Component, indexTimeout: c.IndexTimeout, - log: c.Log.WithGroup("auditing").With("audit-backend", "timescaledb"), + log: c.Log.WithGroup("auditing").With("audit-backend", TimescaleDbBackendName), db: db, config: &tc, } @@ -371,3 +376,17 @@ func (a *timescaleAuditing) Search(ctx context.Context, filter EntryFilter) ([]E return entries, nil } + +func (a *timescaleAuditing) Health(ctx context.Context) *healthstatus.HealthResult { + if err := a.db.PingContext(ctx); err != nil { + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is unhealthy, database is not reachable: %s", TimescaleDbBackendName, err.Error()), + Status: healthstatus.HealthStatusUnhealthy, + } + } + + return &healthstatus.HealthResult{ + Message: fmt.Sprintf("audit backend %q is healthy", TimescaleDbBackendName), + Status: healthstatus.HealthStatusHealthy, + } +} From 8c4ae41ab07db9142f285a6bbfd76884a1b1f515 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 24 Mar 2026 11:05:28 +0100 Subject: [PATCH 2/5] Linter. --- auditing/splunk.go | 2 +- auditing/splunk_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/auditing/splunk.go b/auditing/splunk.go index 601b1523..e6d88584 100644 --- a/auditing/splunk.go +++ b/auditing/splunk.go @@ -200,7 +200,7 @@ func (a *splunkAuditing) Health(ctx context.Context) *healthstatus.HealthResult } func (a *splunkAuditing) splunkRequest(ctx context.Context, ep splunkRequestEndpoint) ([]byte, error) { - ctx, cancel := context.WithTimeout(context.Background(), a.indexTimeout) + ctx, cancel := context.WithTimeout(ctx, a.indexTimeout) defer cancel() req, err := http.NewRequestWithContext(ctx, ep.method, a.endpoint+ep.path, bytes.NewBuffer(ep.body)) diff --git a/auditing/splunk_test.go b/auditing/splunk_test.go index 76edc013..8a7fd671 100644 --- a/auditing/splunk_test.go +++ b/auditing/splunk_test.go @@ -120,7 +120,7 @@ func Test_splunkAuditing_Health(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mux := http.NewServeMux() mux.HandleFunc("/services/collector/health", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(`{"text":"HEC is healthy","code":17}`)) + _, _ = w.Write([]byte(`{"text":"HEC is healthy","code":17}`)) w.WriteHeader(http.StatusOK) }) server := httptest.NewServer(mux) From cac3869fa14182ed035ad5f2173be835c0440af5 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 24 Mar 2026 11:06:22 +0100 Subject: [PATCH 3/5] Don't change error message. --- auditing/splunk.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auditing/splunk.go b/auditing/splunk.go index e6d88584..1ea1cf5d 100644 --- a/auditing/splunk.go +++ b/auditing/splunk.go @@ -149,7 +149,7 @@ func (a *splunkAuditing) Index(entry Entry) error { body: e, }) if err != nil { - return err + return fmt.Errorf("error indexing audit entry in splunk: %w", err) } return nil @@ -212,7 +212,7 @@ func (a *splunkAuditing) splunkRequest(ctx context.Context, ep splunkRequestEndp resp, err := a.client.Do(req) if err != nil { - return nil, fmt.Errorf("error indexing audit entry in splunk: %w", err) + return nil, fmt.Errorf("error during splunk request: %w", err) } defer func() { _ = resp.Body.Close() From 9a94165055c471af60710307f20951e16b032ff9 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 24 Mar 2026 11:17:31 +0100 Subject: [PATCH 4/5] Tests. --- auditing/memory_test.go | 13 +++++++++++++ auditing/timescaledb_integration_test.go | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/auditing/memory_test.go b/auditing/memory_test.go index 72242b97..0435a3ab 100644 --- a/auditing/memory_test.go +++ b/auditing/memory_test.go @@ -8,6 +8,8 @@ import ( "testing" "github.com/metal-stack/metal-lib/auditing" + "github.com/metal-stack/metal-lib/pkg/healthstatus" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,4 +33,15 @@ func TestAuditing_Memory(t *testing.T) { tt.t(t, auditing) }) } + + auditing, err := auditing.NewMemory(auditing.Config{ + Log: slog.Default(), + }, auditing.MemoryConfig{}) + require.NoError(t, err) + + healthResult := auditing.Health(t.Context()) + assert.Equal(t, &healthstatus.HealthResult{ + Status: healthstatus.HealthStatusHealthy, + Message: `audit backend "memory" is healthy`, + }, healthResult) } diff --git a/auditing/timescaledb_integration_test.go b/auditing/timescaledb_integration_test.go index 40cf5fad..20bb65f5 100644 --- a/auditing/timescaledb_integration_test.go +++ b/auditing/timescaledb_integration_test.go @@ -10,6 +10,8 @@ import ( "github.com/jmoiron/sqlx" "github.com/metal-stack/metal-lib/auditing" + "github.com/metal-stack/metal-lib/pkg/healthstatus" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -34,6 +36,12 @@ func TestAuditing_TimescaleDB(t *testing.T) { tt.t(t, aud) }) } + + healthResult := aud.Health(t.Context()) + assert.Equal(t, &healthstatus.HealthResult{ + Status: healthstatus.HealthStatusHealthy, + Message: `audit backend "timescaledb" is healthy`, + }, healthResult) } func StartTimescaleDB(t testing.TB, config auditing.Config) (testcontainers.Container, *sqlx.DB, auditing.Auditing) { From eae0e83f1e0044103bb26a1808bd4ebf463e3487 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 24 Mar 2026 11:49:15 +0100 Subject: [PATCH 5/5] Implement health check interface. --- auditing/async.go | 8 ++++++-- auditing/async_test.go | 15 +++++++------- auditing/auditing.go | 5 +++-- auditing/memory.go | 10 +++++++--- auditing/memory_test.go | 6 ++++-- auditing/splunk.go | 25 ++++++++++-------------- auditing/splunk_test.go | 7 ++++--- auditing/timescaledb.go | 15 +++++++------- auditing/timescaledb_integration_test.go | 6 ++++-- 9 files changed, 54 insertions(+), 43 deletions(-) diff --git a/auditing/async.go b/auditing/async.go index b1dc452c..d225bf2f 100644 --- a/auditing/async.go +++ b/auditing/async.go @@ -87,6 +87,10 @@ func (a *asyncAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entry return a.a.Search(ctx, filter) } -func (a *asyncAuditing) Health(ctx context.Context) *healthstatus.HealthResult { - return a.a.Health(ctx) +func (a *asyncAuditing) ServiceName() string { + return a.a.ServiceName() +} + +func (a *asyncAuditing) Check(ctx context.Context) (healthstatus.HealthResult, error) { + return a.a.Check(ctx) } diff --git a/auditing/async_test.go b/auditing/async_test.go index 21a74367..98159342 100644 --- a/auditing/async_test.go +++ b/auditing/async_test.go @@ -139,16 +139,17 @@ func (t *testBackend) Search(ctx context.Context, filter EntryFilter) ([]Entry, panic("not required") } -func (t *testBackend) Health(context.Context) *healthstatus.HealthResult { +func (t *testBackend) ServiceName() string { + return "test" +} + +func (t *testBackend) Check(ctx context.Context) (healthstatus.HealthResult, error) { if t.health != nil { - return &healthstatus.HealthResult{ - Status: healthstatus.HealthStatusUnhealthy, - Message: fmt.Sprintf("audit backend is unhealthy: %s", t.health.Error()), - } + return healthstatus.HealthResult{}, fmt.Errorf("audit backend is unhealthy: %s", t.health.Error()) } - return &healthstatus.HealthResult{ + return healthstatus.HealthResult{ Status: healthstatus.HealthStatusHealthy, Message: "audit backend is healthy", - } + }, nil } diff --git a/auditing/auditing.go b/auditing/auditing.go index 71a2bbe9..41024381 100644 --- a/auditing/auditing.go +++ b/auditing/auditing.go @@ -129,8 +129,9 @@ type Auditing interface { // By default only recent entries will be returned. // The returned entries will be sorted by timestamp in descending order. Search(context.Context, EntryFilter) ([]Entry, error) - // Health returns info about the audit backend health. - Health(ctx context.Context) *healthstatus.HealthResult + + // Implements the health check interface + healthstatus.HealthCheck } func defaultComponent() (string, error) { diff --git a/auditing/memory.go b/auditing/memory.go index 1661ba4b..7ee0550e 100644 --- a/auditing/memory.go +++ b/auditing/memory.go @@ -207,9 +207,13 @@ func (a *memoryAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entr return entries, nil } -func (a *memoryAuditing) Health(context.Context) *healthstatus.HealthResult { - return &healthstatus.HealthResult{ +func (a *memoryAuditing) ServiceName() string { + return MemoryBackendName +} + +func (a *memoryAuditing) Check(ctx context.Context) (healthstatus.HealthResult, error) { + return healthstatus.HealthResult{ Message: fmt.Sprintf("audit backend %q is healthy", MemoryBackendName), Status: healthstatus.HealthStatusHealthy, - } + }, nil } diff --git a/auditing/memory_test.go b/auditing/memory_test.go index 0435a3ab..00d041b4 100644 --- a/auditing/memory_test.go +++ b/auditing/memory_test.go @@ -39,8 +39,10 @@ func TestAuditing_Memory(t *testing.T) { }, auditing.MemoryConfig{}) require.NoError(t, err) - healthResult := auditing.Health(t.Context()) - assert.Equal(t, &healthstatus.HealthResult{ + healthResult, err := auditing.Check(t.Context()) + + require.NoError(t, err) + assert.Equal(t, healthstatus.HealthResult{ Status: healthstatus.HealthStatusHealthy, Message: `audit backend "memory" is healthy`, }, healthResult) diff --git a/auditing/splunk.go b/auditing/splunk.go index 1ea1cf5d..cc44437e 100644 --- a/auditing/splunk.go +++ b/auditing/splunk.go @@ -159,17 +159,18 @@ func (a *splunkAuditing) Search(ctx context.Context, filter EntryFilter) ([]Entr return nil, fmt.Errorf("search not implemented for splunk audit backend") } -func (a *splunkAuditing) Health(ctx context.Context) *healthstatus.HealthResult { +func (a *splunkAuditing) ServiceName() string { + return SplunkBackendName +} + +func (a *splunkAuditing) Check(ctx context.Context) (healthstatus.HealthResult, error) { resp, err := a.splunkRequest(ctx, splunkRequestEndpoint{ path: "/services/collector/health", method: http.MethodGet, body: nil, }) if err != nil { - return &healthstatus.HealthResult{ - Message: fmt.Sprintf("audit backend %q is unhealthy, collector is unhealthy: %s", SplunkBackendName, err.Error()), - Status: healthstatus.HealthStatusUnhealthy, - } + return healthstatus.HealthResult{}, fmt.Errorf("audit backend %q is unhealthy, collector is unhealthy: %w", SplunkBackendName, err) } type healthResp struct { @@ -180,23 +181,17 @@ func (a *splunkAuditing) Health(ctx context.Context) *healthstatus.HealthResult health := healthResp{} if err := json.Unmarshal(resp, &health); err != nil { - return &healthstatus.HealthResult{ - Message: fmt.Sprintf("audit backend %q is unhealthy, unable to unmarshal health response: %s", SplunkBackendName, err.Error()), - Status: healthstatus.HealthStatusUnhealthy, - } + return healthstatus.HealthResult{}, fmt.Errorf("unable to unmarshal health response: %w", err) } if health.Code != splunkHealthyCode { - return &healthstatus.HealthResult{ - Message: fmt.Sprintf("audit backend %q is degraded: %s", SplunkBackendName, health.Text), - Status: healthstatus.HealthStatusDegraded, - } + return healthstatus.HealthResult{}, fmt.Errorf("audit backend %q is degraded: %s", SplunkBackendName, health.Text) } - return &healthstatus.HealthResult{ + return healthstatus.HealthResult{ Message: fmt.Sprintf("audit backend %q is healthy: %s", SplunkBackendName, health.Text), Status: healthstatus.HealthStatusHealthy, - } + }, nil } func (a *splunkAuditing) splunkRequest(ctx context.Context, ep splunkRequestEndpoint) ([]byte, error) { diff --git a/auditing/splunk_test.go b/auditing/splunk_test.go index 8a7fd671..5ac9116b 100644 --- a/auditing/splunk_test.go +++ b/auditing/splunk_test.go @@ -106,11 +106,11 @@ func Test_splunkAuditing_Index(t *testing.T) { func Test_splunkAuditing_Health(t *testing.T) { tests := []struct { name string - want *healthstatus.HealthResult + want healthstatus.HealthResult }{ { name: "healthy", - want: &healthstatus.HealthResult{ + want: healthstatus.HealthResult{ Status: healthstatus.HealthStatusHealthy, Message: `audit backend "splunk" is healthy: HEC is healthy`, }, @@ -137,7 +137,8 @@ func Test_splunkAuditing_Health(t *testing.T) { }) require.NoError(t, err) - got := a.Health(t.Context()) + got, err := a.Check(t.Context()) + require.NoError(t, err) require.Equal(t, tt.want, got) }) } diff --git a/auditing/timescaledb.go b/auditing/timescaledb.go index ff9532b4..ce7510fa 100644 --- a/auditing/timescaledb.go +++ b/auditing/timescaledb.go @@ -377,16 +377,17 @@ func (a *timescaleAuditing) Search(ctx context.Context, filter EntryFilter) ([]E return entries, nil } -func (a *timescaleAuditing) Health(ctx context.Context) *healthstatus.HealthResult { +func (a *timescaleAuditing) ServiceName() string { + return TimescaleDbBackendName +} + +func (a *timescaleAuditing) Check(ctx context.Context) (healthstatus.HealthResult, error) { if err := a.db.PingContext(ctx); err != nil { - return &healthstatus.HealthResult{ - Message: fmt.Sprintf("audit backend %q is unhealthy, database is not reachable: %s", TimescaleDbBackendName, err.Error()), - Status: healthstatus.HealthStatusUnhealthy, - } + return healthstatus.HealthResult{}, fmt.Errorf("audit backend %q is unhealthy, database is not reachable: %w", TimescaleDbBackendName, err) } - return &healthstatus.HealthResult{ + return healthstatus.HealthResult{ Message: fmt.Sprintf("audit backend %q is healthy", TimescaleDbBackendName), Status: healthstatus.HealthStatusHealthy, - } + }, nil } diff --git a/auditing/timescaledb_integration_test.go b/auditing/timescaledb_integration_test.go index 20bb65f5..4b2c3299 100644 --- a/auditing/timescaledb_integration_test.go +++ b/auditing/timescaledb_integration_test.go @@ -37,8 +37,10 @@ func TestAuditing_TimescaleDB(t *testing.T) { }) } - healthResult := aud.Health(t.Context()) - assert.Equal(t, &healthstatus.HealthResult{ + healthResult, err := aud.Check(t.Context()) + + require.NoError(t, err) + assert.Equal(t, healthstatus.HealthResult{ Status: healthstatus.HealthStatusHealthy, Message: `audit backend "timescaledb" is healthy`, }, healthResult)