diff --git a/pkg/config/config.go b/pkg/config/config.go index d5377888..a7e8f1be 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -21,29 +21,18 @@ package config import ( "time" + "github.com/caas-team/sparrow/pkg/sparrow/targets" + "github.com/caas-team/sparrow/internal/helper" "github.com/caas-team/sparrow/pkg/api" ) -type GitlabTargetManagerConfig struct { - BaseURL string `yaml:"baseUrl" mapstructure:"baseUrl"` - Token string `yaml:"token" mapstructure:"token"` - ProjectID int `yaml:"projectId" mapstructure:"projectId"` -} - -type TargetManagerConfig struct { - CheckInterval time.Duration `yaml:"checkInterval" mapstructure:"checkInterval"` - RegistrationInterval time.Duration `yaml:"registrationInterval" mapstructure:"registrationInterval"` - UnhealthyThreshold time.Duration `yaml:"unhealthyThreshold" mapstructure:"unhealthyThreshold"` - Gitlab GitlabTargetManagerConfig `yaml:"gitlab" mapstructure:"gitlab"` -} - type Config struct { // SparrowName is the DNS name of the sparrow - SparrowName string `yaml:"name" mapstructure:"name"` - Loader LoaderConfig `yaml:"loader" mapstructure:"loader"` - Api api.Config `yaml:"api" mapstructure:"api"` - TargetManager TargetManagerConfig `yaml:"targetManager" mapstructure:"targetManager"` + SparrowName string `yaml:"name" mapstructure:"name"` + Loader LoaderConfig `yaml:"loader" mapstructure:"loader"` + Api api.Config `yaml:"api" mapstructure:"api"` + TargetManager targets.TargetManagerConfig `yaml:"targetManager" mapstructure:"targetManager"` } // LoaderConfig is the configuration for loader @@ -69,5 +58,5 @@ type FileLoaderConfig struct { // HasTargetManager returns true if the config has a target manager func (c *Config) HasTargetManager() bool { - return c.TargetManager != TargetManagerConfig{} + return c.TargetManager != targets.TargetManagerConfig{} } diff --git a/pkg/sparrow/run_test.go b/pkg/sparrow/run_test.go index 57cbdea4..e417efdc 100644 --- a/pkg/sparrow/run_test.go +++ b/pkg/sparrow/run_test.go @@ -24,6 +24,8 @@ import ( "testing" "time" + "github.com/caas-team/sparrow/pkg/sparrow/targets" + "github.com/caas-team/sparrow/pkg/api" "github.com/caas-team/sparrow/pkg/checks/dns" @@ -193,11 +195,13 @@ func TestSparrow_Run_FullComponentStart(t *testing.T) { File: config.FileLoaderConfig{Path: "../config/testdata/config.yaml"}, Interval: time.Second * 1, }, - TargetManager: config.TargetManagerConfig{ - CheckInterval: time.Second * 1, - RegistrationInterval: time.Second * 1, - UnhealthyThreshold: time.Second * 1, - Gitlab: config.GitlabTargetManagerConfig{ + TargetManager: targets.TargetManagerConfig{ + Config: targets.Config{ + CheckInterval: time.Second * 1, + RegistrationInterval: time.Second * 1, + UnhealthyThreshold: time.Second * 1, + }, + Gitlab: targets.GitlabTargetManagerConfig{ BaseURL: "https://gitlab.com", Token: "my-cool-token", ProjectID: 42, diff --git a/pkg/sparrow/targets/errors.go b/pkg/sparrow/targets/errors.go new file mode 100644 index 00000000..cfdaefd2 --- /dev/null +++ b/pkg/sparrow/targets/errors.go @@ -0,0 +1,30 @@ +// sparrow +// (C) 2023, Deutsche Telekom IT GmbH +// +// Deutsche Telekom IT GmbH and all other contributors / +// copyright owners license this file to you under the Apache +// License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package targets + +import "errors" + +// ErrInvalidCheckInterval is returned when the check interval is invalid +var ErrInvalidCheckInterval = errors.New("invalid check interval") + +// ErrInvalidRegistrationInterval is returned when the registration interval is invalid +var ErrInvalidRegistrationInterval = errors.New("invalid registration interval") + +// ErrInvalidUnhealthyThreshold is returned when the unhealthy threshold is invalid +var ErrInvalidUnhealthyThreshold = errors.New("invalid unhealthy threshold") diff --git a/pkg/sparrow/targets/gitlab.go b/pkg/sparrow/targets/gitlab.go index 00657d03..c81ec3c8 100644 --- a/pkg/sparrow/targets/gitlab.go +++ b/pkg/sparrow/targets/gitlab.go @@ -27,8 +27,6 @@ import ( "github.com/caas-team/sparrow/pkg/checks" - "github.com/caas-team/sparrow/pkg/config" - "github.com/caas-team/sparrow/pkg/sparrow/gitlab" "github.com/caas-team/sparrow/internal/logger" @@ -43,31 +41,29 @@ type gitlabTargetManager struct { targets []checks.GlobalTarget mu sync.RWMutex // channel to signal the "reconcile" routine to stop - done chan struct{} - gitlab gitlab.Gitlab + done chan struct{} // the DNS name used for self-registration name string - // the interval for the target reconciliation process - checkInterval time.Duration - // the amount of time a target can be - // unhealthy before it is removed from the global target list - unhealthyThreshold time.Duration - // how often the instance should register itself as a global target - registrationInterval time.Duration // whether the instance has already registered itself as a global target registered bool + cfg Config + gitlab gitlab.Gitlab +} + +type GitlabTargetManagerConfig struct { + BaseURL string `yaml:"baseUrl" mapstructure:"baseUrl"` + Token string `yaml:"token" mapstructure:"token"` + ProjectID int `yaml:"projectId" mapstructure:"projectId"` } // NewGitlabManager creates a new gitlabTargetManager -func NewGitlabManager(name string, gtmConfig config.TargetManagerConfig) *gitlabTargetManager { +func NewGitlabManager(name string, gtmConfig TargetManagerConfig) *gitlabTargetManager { return &gitlabTargetManager{ - gitlab: gitlab.New(gtmConfig.Gitlab.BaseURL, gtmConfig.Gitlab.Token, gtmConfig.Gitlab.ProjectID), - name: name, - checkInterval: gtmConfig.CheckInterval, - registrationInterval: gtmConfig.RegistrationInterval, - unhealthyThreshold: gtmConfig.UnhealthyThreshold, - mu: sync.RWMutex{}, - done: make(chan struct{}, 1), + gitlab: gitlab.New(gtmConfig.Gitlab.BaseURL, gtmConfig.Gitlab.Token, gtmConfig.Gitlab.ProjectID), + name: name, + cfg: gtmConfig.Config, + mu: sync.RWMutex{}, + done: make(chan struct{}, 1), } } @@ -80,8 +76,8 @@ func (t *gitlabTargetManager) Reconcile(ctx context.Context) error { log := logger.FromContext(ctx) log.Info("Starting global gitlabTargetManager reconciler") - checkTimer := time.NewTimer(t.checkInterval) - registrationTimer := time.NewTimer(t.registrationInterval) + checkTimer := time.NewTimer(t.cfg.CheckInterval) + registrationTimer := time.NewTimer(t.cfg.RegistrationInterval) defer checkTimer.Stop() defer registrationTimer.Stop() @@ -99,13 +95,13 @@ func (t *gitlabTargetManager) Reconcile(ctx context.Context) error { if err != nil { log.Warn("Failed to get global targets", "error", err) } - checkTimer.Reset(t.checkInterval) + checkTimer.Reset(t.cfg.CheckInterval) case <-registrationTimer.C: err := t.updateRegistration(ctx) if err != nil { log.Warn("Failed to register self as global target", "error", err) } - registrationTimer.Reset(t.registrationInterval) + registrationTimer.Reset(t.cfg.RegistrationInterval) } } } @@ -211,7 +207,7 @@ func (t *gitlabTargetManager) refreshTargets(ctx context.Context) error { log.Debug("Found self as global target", "lastSeenMin", time.Since(target.LastSeen).Minutes()) t.registered = true } - if time.Now().Add(-t.unhealthyThreshold).After(target.LastSeen) { + if time.Now().Add(-t.cfg.UnhealthyThreshold).After(target.LastSeen) { log.Debug("Skipping unhealthy target", "target", target) continue } diff --git a/pkg/sparrow/targets/gitlab_test.go b/pkg/sparrow/targets/gitlab_test.go index 8131ecd5..1f1ca14b 100644 --- a/pkg/sparrow/targets/gitlab_test.go +++ b/pkg/sparrow/targets/gitlab_test.go @@ -107,10 +107,10 @@ func Test_gitlabTargetManager_refreshTargets(t *testing.T) { gitlab.SetFetchFilesErr(tt.wantErr) } gtm := &gitlabTargetManager{ - targets: nil, - gitlab: gitlab, - name: "test", - unhealthyThreshold: time.Hour, + targets: nil, + gitlab: gitlab, + name: "test", + cfg: Config{UnhealthyThreshold: time.Hour}, } if err := gtm.refreshTargets(context.Background()); (err != nil) != (tt.wantErr != nil) { t.Fatalf("refreshTargets() error = %v, wantErr %v", err, tt.wantErr) @@ -509,14 +509,16 @@ func Test_gitlabTargetManager_Reconcile_Shutdown_Fail_Unregister(t *testing.T) { func mockGitlabTargetManager(g *gitlabmock.MockClient, name string) *gitlabTargetManager { //nolint: unparam // irrelevant return &gitlabTargetManager{ - targets: nil, - mu: sync.RWMutex{}, - done: make(chan struct{}, 1), - gitlab: g, - name: name, - checkInterval: 100 * time.Millisecond, - unhealthyThreshold: 1 * time.Second, - registrationInterval: 150 * time.Millisecond, - registered: false, + targets: nil, + mu: sync.RWMutex{}, + done: make(chan struct{}, 1), + gitlab: g, + name: name, + cfg: Config{ + CheckInterval: 100 * time.Millisecond, + UnhealthyThreshold: 1 * time.Second, + RegistrationInterval: 150 * time.Millisecond, + }, + registered: false, } } diff --git a/pkg/sparrow/targets/targetmanager.go b/pkg/sparrow/targets/targetmanager.go index 691e7d44..9c823003 100644 --- a/pkg/sparrow/targets/targetmanager.go +++ b/pkg/sparrow/targets/targetmanager.go @@ -20,6 +20,7 @@ package targets import ( "context" + "time" "github.com/caas-team/sparrow/pkg/checks" ) @@ -36,3 +37,34 @@ type TargetManager interface { // and unregisters the instance as a global target Shutdown(ctx context.Context) error } + +// Config is the general configuration of the target manager +type Config struct { + // The interval for the target reconciliation process + CheckInterval time.Duration `yaml:"checkInterval" mapstructure:"checkInterval"` + // How often the instance should register itself as a global target. + // A duration of 0 means no registration. + RegistrationInterval time.Duration `yaml:"registrationInterval" mapstructure:"registrationInterval"` + // The amount of time a target can be unhealthy + // before it is removed from the global target list. + // A duration of 0 means no removal. + UnhealthyThreshold time.Duration `yaml:"unhealthyThreshold" mapstructure:"unhealthyThreshold"` +} + +type TargetManagerConfig struct { + Config + Gitlab GitlabTargetManagerConfig `yaml:"gitlab" mapstructure:"gitlab"` +} + +func (tmc *Config) Validate() error { + if tmc.CheckInterval <= 0 { + return ErrInvalidCheckInterval + } + if tmc.RegistrationInterval < 0 { + return ErrInvalidRegistrationInterval + } + if tmc.UnhealthyThreshold < 0 { + return ErrInvalidUnhealthyThreshold + } + return nil +} diff --git a/pkg/sparrow/targets/targetmanager_test.go b/pkg/sparrow/targets/targetmanager_test.go new file mode 100644 index 00000000..fa2b5f67 --- /dev/null +++ b/pkg/sparrow/targets/targetmanager_test.go @@ -0,0 +1,78 @@ +// sparrow +// (C) 2023, Deutsche Telekom IT GmbH +// +// Deutsche Telekom IT GmbH and all other contributors / +// copyright owners license this file to you under the Apache +// License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package targets + +import ( + "testing" + "time" +) + +func TestTargetManagerConfig_Validate(t *testing.T) { + tests := []struct { + name string + cfg Config + wantErr bool + }{ + { + name: "empty config", + wantErr: true, + }, + { + name: "valid config - non-zero values", + cfg: Config{ + UnhealthyThreshold: 1 * time.Second, + CheckInterval: 1 * time.Second, + RegistrationInterval: 1 * time.Second, + }, + }, + { + name: "valid config - zero values", + cfg: Config{ + UnhealthyThreshold: 0, + CheckInterval: 1 * time.Second, + RegistrationInterval: 0, + }, + }, + { + name: "invalid config - zero check interval", + cfg: Config{ + UnhealthyThreshold: 1 * time.Second, + CheckInterval: 0, + RegistrationInterval: 1 * time.Second, + }, + wantErr: true, + }, + { + name: "invalid config - negative values", + cfg: Config{ + UnhealthyThreshold: -1 * time.Second, + CheckInterval: 1 * time.Second, + RegistrationInterval: 1 * time.Second, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.cfg.Validate(); (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}