Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global target pass-down to the Checks #50

Merged
merged 8 commits into from
Dec 20, 2023
58 changes: 56 additions & 2 deletions pkg/sparrow/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"context"
"fmt"
"net/http"
"slices"

targets "github.com/caas-team/sparrow/pkg/sparrow/targets"

Expand Down Expand Up @@ -117,17 +118,18 @@ func (s *Sparrow) ReconcileChecks(ctx context.Context) {
name := name
log := logger.FromContext(ctx).With("name", name)

c := s.updateCheckTargets(checkCfg)
if existingCheck, ok := s.checks[name]; ok {
// Check already registered, reset config
err := existingCheck.SetConfig(ctx, checkCfg)
err := existingCheck.SetConfig(ctx, c)
if err != nil {
log.ErrorContext(ctx, "Failed to reset config for check, check will run with last applies config", "error", err)
}
continue
}

// Check is a new Check and needs to be registered
s.registerCheck(ctx, name, checkCfg)
s.registerCheck(ctx, name, c)
}

for existingCheckName, existingCheck := range s.checks {
Expand All @@ -141,6 +143,58 @@ func (s *Sparrow) ReconcileChecks(ctx context.Context) {
}
}

// updateCheckTargets updates the targets of a check with the
// global targets. The targets are merged per default, if found in the
// passed config.
func (s *Sparrow) updateCheckTargets(cfg any) any {
if cfg == nil {
return nil
}

// check if map with targets
checkCfg, ok := cfg.(map[string]any)
if !ok {
return checkCfg
}
if _, ok = checkCfg["targets"]; !ok {
return checkCfg
}

// Check if targets is a slice
actuali, ok := checkCfg["targets"].([]any)
if !ok {
return checkCfg
}
if len(actuali) == 0 {
return checkCfg
}

// convert to string slice
var actual []string
for _, v := range actuali {
if _, ok := v.(string); !ok {
return checkCfg
}
actual = append(actual, v.(string))
puffitos marked this conversation as resolved.
Show resolved Hide resolved
}
var urls []string
gt := s.targets.GetTargets()

// filter out globalTargets that are already in the config and self
for _, t := range gt {
if slices.Contains(actual, t.Url) {
continue
}
if t.Url == fmt.Sprintf("https://%s", s.cfg.SparrowName) {
puffitos marked this conversation as resolved.
Show resolved Hide resolved
continue
}
urls = append(urls, t.Url)
}

checkCfg["targets"] = append(actual, urls...)
return checkCfg
}

// registerCheck registers and executes a new check
func (s *Sparrow) registerCheck(ctx context.Context, name string, checkCfg any) {
log := logger.FromContext(ctx).With("name", name)
Expand Down
124 changes: 124 additions & 0 deletions pkg/sparrow/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"testing"
"time"

gitlabmock "github.com/caas-team/sparrow/pkg/sparrow/targets/test"

"github.com/getkin/kin-openapi/openapi3"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -221,3 +223,125 @@ func TestSparrow_Run(t *testing.T) {
t.Log("Letting API run shortly")
time.Sleep(time.Millisecond * 150)
}

// TestSparrow_updateCheckTargets tests that the updateCheckTargets method
// updates the check targets, if they exists in the config of the checks.
func TestSparrow_updateCheckTargets(t *testing.T) {
now := time.Now()
gt := []checks.GlobalTarget{
{
Url: "https://localhost.de",
LastSeen: now,
},
}
tests := []struct {
name string
config any
globalTargets []checks.GlobalTarget
expected any
}{
{
name: "no config",
config: nil,
globalTargets: gt,
expected: nil,
},
{
name: "config with no targets",
config: map[string]any{
"targets": nil,
},
globalTargets: gt,
expected: map[string]any{
"targets": nil,
},
},
{
name: "config with non-expected targets type",
config: map[string]any{
"targets": "not a slice",
},
globalTargets: gt,
expected: map[string]any{
"targets": "not a slice",
},
},
{
name: "config with empty targets",
config: map[string]any{
"targets": []any{},
},
globalTargets: gt,
expected: map[string]any{
"targets": []any{},
},
},
{
name: "config with non string target slice",
config: map[string]any{
"targets": []any{1, 2, 3},
},
globalTargets: gt,
expected: map[string]any{
"targets": []any{1, 2, 3},
},
},
{
name: "config with mixed target slice",
config: map[string]any{
"targets": []any{"https://gitlab.com", 1, 3},
},
globalTargets: gt,
expected: map[string]any{
"targets": []any{"https://gitlab.com", 1, 3},
},
},
{
name: "config with targets",
config: map[string]any{
"targets": []any{"https://gitlab.com"},
},
globalTargets: gt,
expected: map[string]any{
"targets": []string{"https://gitlab.com", "https://localhost.de"},
},
},
{
name: "config has a target already present in global targets - no duplicates",
config: map[string]any{
"targets": []any{"https://localhost.de"},
},
globalTargets: gt,
expected: map[string]any{
"targets": []string{"https://localhost.de"},
},
},
{
name: "global targets contains self - do not add to config",
config: map[string]any{
"targets": []any{"https://localhost.de"},
},
globalTargets: append(gt, checks.GlobalTarget{
Url: "https://wonderhost.usa",
}),
expected: map[string]any{
"targets": []string{"https://localhost.de"},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &Sparrow{
targets: &gitlabmock.MockTargetManager{
Targets: tt.globalTargets,
},
cfg: &config.Config{
SparrowName: "wonderhost.usa",
},
}
got := s.updateCheckTargets(tt.config)
assert.Equal(t, tt.expected, got)
})
}
}
30 changes: 30 additions & 0 deletions pkg/sparrow/targets/test/mocktargets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package gitlabmock

import (
"context"

"github.com/caas-team/sparrow/internal/logger"
"github.com/caas-team/sparrow/pkg/checks"
)

// MockTargetManager is a mock implementation of the TargetManager interface
type MockTargetManager struct {
Targets []checks.GlobalTarget
}

func (m *MockTargetManager) Reconcile(ctx context.Context) {
log := logger.FromContext(ctx)
log.Info("MockReconcile called")
}

func (m *MockTargetManager) Shutdown(ctx context.Context) error {
log := logger.FromContext(ctx)
log.Info("MockShutdown called")
return nil
}

func (m *MockTargetManager) GetTargets() []checks.GlobalTarget {
log := logger.FromContext(context.Background())
log.Info("MockGetTargets called, returning", "targets", len(m.Targets))
return m.Targets
}