diff --git a/.github/workflows/test_unit.yml b/.github/workflows/test_unit.yml index 3bbdc3d1..43ad7b77 100644 --- a/.github/workflows/test_unit.yml +++ b/.github/workflows/test_unit.yml @@ -23,6 +23,4 @@ jobs: - name: Test run: | go mod download - go install github.com/matryer/moq@v0.3.3 - go generate ./... go test --race --coverprofile cover.out -v ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6608da08..d41d1df3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,6 @@ go.work gen -# generated interface mock files -*_moq.go - # test configs for debug launch configuration .vscode/config diff --git a/internal/helper/retry.go b/internal/helper/retry.go index 4d772397..d96d5610 100644 --- a/internal/helper/retry.go +++ b/internal/helper/retry.go @@ -48,13 +48,10 @@ func Retry(effector Effector, rc RetryConfig) func(ctx context.Context) error { delay := getExpBackoff(rc.Delay, r) log.WarnContext(ctx, fmt.Sprintf("Effector call failed, retrying in %v", delay)) - timer := time.NewTimer(delay) - defer timer.Stop() //nolint:gocritic //TODO: check if this is correct - select { case <-ctx.Done(): return ctx.Err() - case <-timer.C: + case <-time.After(delay): } } } diff --git a/pkg/checks/checks_moq.go b/pkg/checks/checks_moq.go new file mode 100644 index 00000000..664c81b5 --- /dev/null +++ b/pkg/checks/checks_moq.go @@ -0,0 +1,441 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package checks + +import ( + "context" + "github.com/caas-team/sparrow/pkg/api" + "github.com/getkin/kin-openapi/openapi3" + "github.com/prometheus/client_golang/prometheus" + "net/http" + "sync" +) + +// Ensure, that CheckMock does implement Check. +// If this is not the case, regenerate this file with moq. +var _ Check = &CheckMock{} + +// CheckMock is a mock implementation of Check. +// +// func TestSomethingThatUsesCheck(t *testing.T) { +// +// // make and configure a mocked Check +// mockedCheck := &CheckMock{ +// DeregisterHandlerFunc: func(ctx context.Context, router *api.RoutingTree) { +// panic("mock out the DeregisterHandler method") +// }, +// GetMetricCollectorsFunc: func() []prometheus.Collector { +// panic("mock out the GetMetricCollectors method") +// }, +// RegisterHandlerFunc: func(ctx context.Context, router *api.RoutingTree) { +// panic("mock out the RegisterHandler method") +// }, +// RunFunc: func(ctx context.Context) error { +// panic("mock out the Run method") +// }, +// SchemaFunc: func() (*openapi3.SchemaRef, error) { +// panic("mock out the Schema method") +// }, +// SetClientFunc: func(c *http.Client) { +// panic("mock out the SetClient method") +// }, +// SetConfigFunc: func(ctx context.Context, config any) error { +// panic("mock out the SetConfig method") +// }, +// ShutdownFunc: func(ctx context.Context) error { +// panic("mock out the Shutdown method") +// }, +// StartupFunc: func(ctx context.Context, cResult chan<- Result) error { +// panic("mock out the Startup method") +// }, +// } +// +// // use mockedCheck in code that requires Check +// // and then make assertions. +// +// } +type CheckMock struct { + // DeregisterHandlerFunc mocks the DeregisterHandler method. + DeregisterHandlerFunc func(ctx context.Context, router *api.RoutingTree) + + // GetMetricCollectorsFunc mocks the GetMetricCollectors method. + GetMetricCollectorsFunc func() []prometheus.Collector + + // RegisterHandlerFunc mocks the RegisterHandler method. + RegisterHandlerFunc func(ctx context.Context, router *api.RoutingTree) + + // RunFunc mocks the Run method. + RunFunc func(ctx context.Context) error + + // SchemaFunc mocks the Schema method. + SchemaFunc func() (*openapi3.SchemaRef, error) + + // SetClientFunc mocks the SetClient method. + SetClientFunc func(c *http.Client) + + // SetConfigFunc mocks the SetConfig method. + SetConfigFunc func(ctx context.Context, config any) error + + // ShutdownFunc mocks the Shutdown method. + ShutdownFunc func(ctx context.Context) error + + // StartupFunc mocks the Startup method. + StartupFunc func(ctx context.Context, cResult chan<- Result) error + + // calls tracks calls to the methods. + calls struct { + // DeregisterHandler holds details about calls to the DeregisterHandler method. + DeregisterHandler []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Router is the router argument value. + Router *api.RoutingTree + } + // GetMetricCollectors holds details about calls to the GetMetricCollectors method. + GetMetricCollectors []struct { + } + // RegisterHandler holds details about calls to the RegisterHandler method. + RegisterHandler []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Router is the router argument value. + Router *api.RoutingTree + } + // Run holds details about calls to the Run method. + Run []struct { + // Ctx is the ctx argument value. + Ctx context.Context + } + // Schema holds details about calls to the Schema method. + Schema []struct { + } + // SetClient holds details about calls to the SetClient method. + SetClient []struct { + // C is the c argument value. + C *http.Client + } + // SetConfig holds details about calls to the SetConfig method. + SetConfig []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Config is the config argument value. + Config any + } + // Shutdown holds details about calls to the Shutdown method. + Shutdown []struct { + // Ctx is the ctx argument value. + Ctx context.Context + } + // Startup holds details about calls to the Startup method. + Startup []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // CResult is the cResult argument value. + CResult chan<- Result + } + } + lockDeregisterHandler sync.RWMutex + lockGetMetricCollectors sync.RWMutex + lockRegisterHandler sync.RWMutex + lockRun sync.RWMutex + lockSchema sync.RWMutex + lockSetClient sync.RWMutex + lockSetConfig sync.RWMutex + lockShutdown sync.RWMutex + lockStartup sync.RWMutex +} + +// DeregisterHandler calls DeregisterHandlerFunc. +func (mock *CheckMock) DeregisterHandler(ctx context.Context, router *api.RoutingTree) { + if mock.DeregisterHandlerFunc == nil { + panic("CheckMock.DeregisterHandlerFunc: method is nil but Check.DeregisterHandler was just called") + } + callInfo := struct { + Ctx context.Context + Router *api.RoutingTree + }{ + Ctx: ctx, + Router: router, + } + mock.lockDeregisterHandler.Lock() + mock.calls.DeregisterHandler = append(mock.calls.DeregisterHandler, callInfo) + mock.lockDeregisterHandler.Unlock() + mock.DeregisterHandlerFunc(ctx, router) +} + +// DeregisterHandlerCalls gets all the calls that were made to DeregisterHandler. +// Check the length with: +// +// len(mockedCheck.DeregisterHandlerCalls()) +func (mock *CheckMock) DeregisterHandlerCalls() []struct { + Ctx context.Context + Router *api.RoutingTree +} { + var calls []struct { + Ctx context.Context + Router *api.RoutingTree + } + mock.lockDeregisterHandler.RLock() + calls = mock.calls.DeregisterHandler + mock.lockDeregisterHandler.RUnlock() + return calls +} + +// GetMetricCollectors calls GetMetricCollectorsFunc. +func (mock *CheckMock) GetMetricCollectors() []prometheus.Collector { + if mock.GetMetricCollectorsFunc == nil { + panic("CheckMock.GetMetricCollectorsFunc: method is nil but Check.GetMetricCollectors was just called") + } + callInfo := struct { + }{} + mock.lockGetMetricCollectors.Lock() + mock.calls.GetMetricCollectors = append(mock.calls.GetMetricCollectors, callInfo) + mock.lockGetMetricCollectors.Unlock() + return mock.GetMetricCollectorsFunc() +} + +// GetMetricCollectorsCalls gets all the calls that were made to GetMetricCollectors. +// Check the length with: +// +// len(mockedCheck.GetMetricCollectorsCalls()) +func (mock *CheckMock) GetMetricCollectorsCalls() []struct { +} { + var calls []struct { + } + mock.lockGetMetricCollectors.RLock() + calls = mock.calls.GetMetricCollectors + mock.lockGetMetricCollectors.RUnlock() + return calls +} + +// RegisterHandler calls RegisterHandlerFunc. +func (mock *CheckMock) RegisterHandler(ctx context.Context, router *api.RoutingTree) { + if mock.RegisterHandlerFunc == nil { + panic("CheckMock.RegisterHandlerFunc: method is nil but Check.RegisterHandler was just called") + } + callInfo := struct { + Ctx context.Context + Router *api.RoutingTree + }{ + Ctx: ctx, + Router: router, + } + mock.lockRegisterHandler.Lock() + mock.calls.RegisterHandler = append(mock.calls.RegisterHandler, callInfo) + mock.lockRegisterHandler.Unlock() + mock.RegisterHandlerFunc(ctx, router) +} + +// RegisterHandlerCalls gets all the calls that were made to RegisterHandler. +// Check the length with: +// +// len(mockedCheck.RegisterHandlerCalls()) +func (mock *CheckMock) RegisterHandlerCalls() []struct { + Ctx context.Context + Router *api.RoutingTree +} { + var calls []struct { + Ctx context.Context + Router *api.RoutingTree + } + mock.lockRegisterHandler.RLock() + calls = mock.calls.RegisterHandler + mock.lockRegisterHandler.RUnlock() + return calls +} + +// Run calls RunFunc. +func (mock *CheckMock) Run(ctx context.Context) error { + if mock.RunFunc == nil { + panic("CheckMock.RunFunc: method is nil but Check.Run was just called") + } + callInfo := struct { + Ctx context.Context + }{ + Ctx: ctx, + } + mock.lockRun.Lock() + mock.calls.Run = append(mock.calls.Run, callInfo) + mock.lockRun.Unlock() + return mock.RunFunc(ctx) +} + +// RunCalls gets all the calls that were made to Run. +// Check the length with: +// +// len(mockedCheck.RunCalls()) +func (mock *CheckMock) RunCalls() []struct { + Ctx context.Context +} { + var calls []struct { + Ctx context.Context + } + mock.lockRun.RLock() + calls = mock.calls.Run + mock.lockRun.RUnlock() + return calls +} + +// Schema calls SchemaFunc. +func (mock *CheckMock) Schema() (*openapi3.SchemaRef, error) { + if mock.SchemaFunc == nil { + panic("CheckMock.SchemaFunc: method is nil but Check.Schema was just called") + } + callInfo := struct { + }{} + mock.lockSchema.Lock() + mock.calls.Schema = append(mock.calls.Schema, callInfo) + mock.lockSchema.Unlock() + return mock.SchemaFunc() +} + +// SchemaCalls gets all the calls that were made to Schema. +// Check the length with: +// +// len(mockedCheck.SchemaCalls()) +func (mock *CheckMock) SchemaCalls() []struct { +} { + var calls []struct { + } + mock.lockSchema.RLock() + calls = mock.calls.Schema + mock.lockSchema.RUnlock() + return calls +} + +// SetClient calls SetClientFunc. +func (mock *CheckMock) SetClient(c *http.Client) { + if mock.SetClientFunc == nil { + panic("CheckMock.SetClientFunc: method is nil but Check.SetClient was just called") + } + callInfo := struct { + C *http.Client + }{ + C: c, + } + mock.lockSetClient.Lock() + mock.calls.SetClient = append(mock.calls.SetClient, callInfo) + mock.lockSetClient.Unlock() + mock.SetClientFunc(c) +} + +// SetClientCalls gets all the calls that were made to SetClient. +// Check the length with: +// +// len(mockedCheck.SetClientCalls()) +func (mock *CheckMock) SetClientCalls() []struct { + C *http.Client +} { + var calls []struct { + C *http.Client + } + mock.lockSetClient.RLock() + calls = mock.calls.SetClient + mock.lockSetClient.RUnlock() + return calls +} + +// SetConfig calls SetConfigFunc. +func (mock *CheckMock) SetConfig(ctx context.Context, config any) error { + if mock.SetConfigFunc == nil { + panic("CheckMock.SetConfigFunc: method is nil but Check.SetConfig was just called") + } + callInfo := struct { + Ctx context.Context + Config any + }{ + Ctx: ctx, + Config: config, + } + mock.lockSetConfig.Lock() + mock.calls.SetConfig = append(mock.calls.SetConfig, callInfo) + mock.lockSetConfig.Unlock() + return mock.SetConfigFunc(ctx, config) +} + +// SetConfigCalls gets all the calls that were made to SetConfig. +// Check the length with: +// +// len(mockedCheck.SetConfigCalls()) +func (mock *CheckMock) SetConfigCalls() []struct { + Ctx context.Context + Config any +} { + var calls []struct { + Ctx context.Context + Config any + } + mock.lockSetConfig.RLock() + calls = mock.calls.SetConfig + mock.lockSetConfig.RUnlock() + return calls +} + +// Shutdown calls ShutdownFunc. +func (mock *CheckMock) Shutdown(ctx context.Context) error { + if mock.ShutdownFunc == nil { + panic("CheckMock.ShutdownFunc: method is nil but Check.Shutdown was just called") + } + callInfo := struct { + Ctx context.Context + }{ + Ctx: ctx, + } + mock.lockShutdown.Lock() + mock.calls.Shutdown = append(mock.calls.Shutdown, callInfo) + mock.lockShutdown.Unlock() + return mock.ShutdownFunc(ctx) +} + +// ShutdownCalls gets all the calls that were made to Shutdown. +// Check the length with: +// +// len(mockedCheck.ShutdownCalls()) +func (mock *CheckMock) ShutdownCalls() []struct { + Ctx context.Context +} { + var calls []struct { + Ctx context.Context + } + mock.lockShutdown.RLock() + calls = mock.calls.Shutdown + mock.lockShutdown.RUnlock() + return calls +} + +// Startup calls StartupFunc. +func (mock *CheckMock) Startup(ctx context.Context, cResult chan<- Result) error { + if mock.StartupFunc == nil { + panic("CheckMock.StartupFunc: method is nil but Check.Startup was just called") + } + callInfo := struct { + Ctx context.Context + CResult chan<- Result + }{ + Ctx: ctx, + CResult: cResult, + } + mock.lockStartup.Lock() + mock.calls.Startup = append(mock.calls.Startup, callInfo) + mock.lockStartup.Unlock() + return mock.StartupFunc(ctx, cResult) +} + +// StartupCalls gets all the calls that were made to Startup. +// Check the length with: +// +// len(mockedCheck.StartupCalls()) +func (mock *CheckMock) StartupCalls() []struct { + Ctx context.Context + CResult chan<- Result +} { + var calls []struct { + Ctx context.Context + CResult chan<- Result + } + mock.lockStartup.RLock() + calls = mock.calls.Startup + mock.lockStartup.RUnlock() + return calls +} diff --git a/pkg/checks/health.go b/pkg/checks/health.go index e7f5bc7d..6c5817b3 100644 --- a/pkg/checks/health.go +++ b/pkg/checks/health.go @@ -73,7 +73,10 @@ type Target struct { func NewHealthCheck() Check { return &Health{ route: "health", + config: HealthConfig{}, metrics: newHealthMetrics(), + c: nil, + done: make(chan bool, 1), } } @@ -113,8 +116,8 @@ func (h *Health) Startup(_ context.Context, cResult chan<- Result) error { // Shutdown is called once when the check is unregistered or sparrow shuts down func (h *Health) Shutdown(_ context.Context) error { - http.Handle(h.route, http.NotFoundHandler()) h.done <- true + close(h.done) return nil } diff --git a/pkg/checks/health_test.go b/pkg/checks/health_test.go index c195bd61..a1dff995 100644 --- a/pkg/checks/health_test.go +++ b/pkg/checks/health_test.go @@ -278,3 +278,36 @@ func TestHealth_Check(t *testing.T) { }) } } + +func TestHealth_Shutdown(t *testing.T) { + cDone := make(chan bool, 1) + c := Health{ + done: cDone, + } + err := c.Shutdown(context.Background()) + if err != nil { + t.Errorf("Shutdown() error = %v", err) + } + + if !<-cDone { + t.Error("Channel should be done") + } + + assert.Panics(t, func() { + cDone <- true + }, "Channel is closed, should panic") + + hc := NewHealthCheck() + err = hc.Shutdown(context.Background()) + if err != nil { + t.Errorf("Shutdown() error = %v", err) + } + + if !<-hc.(*Health).done { + t.Error("Channel should be done") + } + + assert.Panics(t, func() { + hc.(*Health).done <- true + }, "Channel is closed, should panic") +} diff --git a/pkg/config/http.go b/pkg/config/http.go index ccd4f2e3..afc18a8e 100644 --- a/pkg/config/http.go +++ b/pkg/config/http.go @@ -68,13 +68,10 @@ func (gl *HttpLoader) Run(ctx context.Context) { log.Info("Successfully got remote runtime configuration") gl.cCfgChecks <- runtimeCfg.Checks - timer := time.NewTimer(gl.cfg.Loader.Interval) - defer timer.Stop() //nolint:gocritic // TODO: check if this is right - select { case <-ctx.Done(): return - case <-timer.C: + case <-time.After(gl.cfg.Loader.Interval): } } } diff --git a/pkg/sparrow/metrics_moq.go b/pkg/sparrow/metrics_moq.go new file mode 100644 index 00000000..19cc3e8b --- /dev/null +++ b/pkg/sparrow/metrics_moq.go @@ -0,0 +1,68 @@ +// Code generated by moq; DO NOT EDIT. +// github.com/matryer/moq + +package sparrow + +import ( + "github.com/prometheus/client_golang/prometheus" + "sync" +) + +// Ensure, that MetricsMock does implement Metrics. +// If this is not the case, regenerate this file with moq. +var _ Metrics = &MetricsMock{} + +// MetricsMock is a mock implementation of Metrics. +// +// func TestSomethingThatUsesMetrics(t *testing.T) { +// +// // make and configure a mocked Metrics +// mockedMetrics := &MetricsMock{ +// GetRegistryFunc: func() *prometheus.Registry { +// panic("mock out the GetRegistry method") +// }, +// } +// +// // use mockedMetrics in code that requires Metrics +// // and then make assertions. +// +// } +type MetricsMock struct { + // GetRegistryFunc mocks the GetRegistry method. + GetRegistryFunc func() *prometheus.Registry + + // calls tracks calls to the methods. + calls struct { + // GetRegistry holds details about calls to the GetRegistry method. + GetRegistry []struct { + } + } + lockGetRegistry sync.RWMutex +} + +// GetRegistry calls GetRegistryFunc. +func (mock *MetricsMock) GetRegistry() *prometheus.Registry { + if mock.GetRegistryFunc == nil { + panic("MetricsMock.GetRegistryFunc: method is nil but Metrics.GetRegistry was just called") + } + callInfo := struct { + }{} + mock.lockGetRegistry.Lock() + mock.calls.GetRegistry = append(mock.calls.GetRegistry, callInfo) + mock.lockGetRegistry.Unlock() + return mock.GetRegistryFunc() +} + +// GetRegistryCalls gets all the calls that were made to GetRegistry. +// Check the length with: +// +// len(mockedMetrics.GetRegistryCalls()) +func (mock *MetricsMock) GetRegistryCalls() []struct { +} { + var calls []struct { + } + mock.lockGetRegistry.RLock() + calls = mock.calls.GetRegistry + mock.lockGetRegistry.RUnlock() + return calls +}