From a611c528a80b37431e827dee326f47576fb01a33 Mon Sep 17 00:00:00 2001 From: Niklas Treml Date: Thu, 30 Nov 2023 16:21:30 +0100 Subject: [PATCH 1/3] Feat/latency check (#19) * chore: formatting * fix: decode mapstructure * feat: track error by domain * refactor: adhere to naming conventions * feat: only collect total duration * feat: add port to config * chore: go mod tidy * fix: channel sends could potentially block gouroutines * feat: unit tests * feat: retry * refactor: rename mutex * chore: gen docs * docs(latency): document latency check * chore: remove dead code --- .vscode/launch.json | 4 +- README.md | 29 ++++ cmd/run.go | 8 +- docs/sparrow.md | 2 +- docs/sparrow_completion.md | 2 +- docs/sparrow_completion_bash.md | 2 +- docs/sparrow_completion_fish.md | 2 +- docs/sparrow_completion_powershell.md | 2 +- docs/sparrow_completion_zsh.md | 2 +- docs/sparrow_gen-docs.md | 2 +- docs/sparrow_run.md | 22 +-- go.mod | 1 + go.sum | 2 + pkg/checks/checks.go | 3 +- pkg/checks/latency.go | 179 +++++++++++++++++++ pkg/checks/latency_test.go | 237 ++++++++++++++++++++++++++ pkg/config/config.go | 2 +- pkg/config/file_test.go | 4 +- pkg/config/flags.go | 2 +- pkg/config/http_test.go | 2 +- pkg/config/loader.go | 4 - pkg/sparrow/api.go | 5 +- pkg/sparrow/run.go | 6 +- pkg/sparrow/run_test.go | 12 +- 24 files changed, 492 insertions(+), 44 deletions(-) create mode 100644 pkg/checks/latency.go create mode 100644 pkg/checks/latency_test.go diff --git a/.vscode/launch.json b/.vscode/launch.json index 7d8cb0b1..751c7cb4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,9 @@ "args": [ "run", "--config", - ".vscode/config/local.config.yaml" + "config.yaml", + "--apiAddress", + ":9090" ] }, { diff --git a/README.md b/README.md index 1600e362..fe613035 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,35 @@ checks: healthEndpoint: false ``` +### Check: Latency + +Available configuration options: + +- `checks` + - `latency` + - `enabled` (boolean): Currently not used. + - `interval` (integer): Interval in seconds to perform the latency check. + - `timeout` (integer): Timeout in seconds for the latency check. + - `retry` + - `count` (integer): Number of retries for the latency check. + - `delay` (integer): Delay in seconds between retries for the latency check. + - `targets` (list of strings): List of targets to send latency probe. Needs to be a valid url. Can be another `sparrow` instance. Use latency endpoint, e.g. `https://sparrow-dns.telekom.de/checks/latency`. The remote `sparrow` instance needs the `latencyEndpoint` enabled. + - `latencyEndpoint` (boolean): Needs to be activated when the `sparrow` should expose its own latency endpoint. Mandatory if another `sparrow` instance wants perform a latency check. +Example configuration: + +```yaml +checks: + latency: + enabled: true + interval: 1 + timeout: 3 + retry: + count: 3 + delay: 1 + targets: + - https://example.com/ + - https://google.com/ +``` ### API diff --git a/cmd/run.go b/cmd/run.go index 704cb82b..e3c8958b 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -32,7 +32,7 @@ import ( // NewCmdRun creates a new run command func NewCmdRun() *cobra.Command { flagMapping := config.RunFlagsNameMapping{ - ApiListeningAddress: "apiListeningAddress", + ApiAddress: "apiAddress", LoaderType: "loaderType", LoaderInterval: "loaderInterval", LoaderHttpUrl: "loaderHttpUrl", @@ -50,7 +50,7 @@ func NewCmdRun() *cobra.Command { Run: run(&flagMapping), } - cmd.PersistentFlags().String(flagMapping.ApiListeningAddress, ":8080", "api: The address the server is listening on") + cmd.PersistentFlags().String(flagMapping.ApiAddress, ":8080", "api: The address the server is listening on") cmd.PersistentFlags().StringP(flagMapping.LoaderType, "l", "http", "defines the loader type that will load the checks configuration during the runtime. The fallback is the fileLoader") @@ -62,7 +62,7 @@ func NewCmdRun() *cobra.Command { cmd.PersistentFlags().Int(flagMapping.LoaderHttpRetryDelay, 1, "http loader: The initial delay between retries in seconds") cmd.PersistentFlags().String(flagMapping.LoaderFilePath, "config.yaml", "file loader: The path to the file to read the runtime config from") - viper.BindPFlag(flagMapping.ApiListeningAddress, cmd.PersistentFlags().Lookup(flagMapping.ApiListeningAddress)) + viper.BindPFlag(flagMapping.ApiAddress, cmd.PersistentFlags().Lookup(flagMapping.ApiAddress)) viper.BindPFlag(flagMapping.LoaderType, cmd.PersistentFlags().Lookup(flagMapping.LoaderType)) viper.BindPFlag(flagMapping.LoaderInterval, cmd.PersistentFlags().Lookup(flagMapping.LoaderInterval)) @@ -84,7 +84,7 @@ func run(fm *config.RunFlagsNameMapping) func(cmd *cobra.Command, args []string) cfg := config.NewConfig() - cfg.SetApiListeningAddress(viper.GetString(fm.ApiListeningAddress)) + cfg.SetApiAddress(viper.GetString(fm.ApiAddress)) cfg.SetLoaderType(viper.GetString(fm.LoaderType)) cfg.SetLoaderInterval(viper.GetInt(fm.LoaderInterval)) diff --git a/docs/sparrow.md b/docs/sparrow.md index c37922b5..f433887b 100644 --- a/docs/sparrow.md +++ b/docs/sparrow.md @@ -20,4 +20,4 @@ The check results are exposed via an API. * [sparrow gen-docs](sparrow_gen-docs.md) - Generate markdown documentation * [sparrow run](sparrow_run.md) - Run sparrow -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_completion.md b/docs/sparrow_completion.md index fc13b561..97d7cdb8 100644 --- a/docs/sparrow_completion.md +++ b/docs/sparrow_completion.md @@ -28,4 +28,4 @@ See each sub-command's help for details on how to use the generated script. * [sparrow completion powershell](sparrow_completion_powershell.md) - Generate the autocompletion script for powershell * [sparrow completion zsh](sparrow_completion_zsh.md) - Generate the autocompletion script for zsh -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_completion_bash.md b/docs/sparrow_completion_bash.md index 250d0890..2f979571 100644 --- a/docs/sparrow_completion_bash.md +++ b/docs/sparrow_completion_bash.md @@ -47,4 +47,4 @@ sparrow completion bash * [sparrow completion](sparrow_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_completion_fish.md b/docs/sparrow_completion_fish.md index 9768a0df..1e3e5c40 100644 --- a/docs/sparrow_completion_fish.md +++ b/docs/sparrow_completion_fish.md @@ -38,4 +38,4 @@ sparrow completion fish [flags] * [sparrow completion](sparrow_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_completion_powershell.md b/docs/sparrow_completion_powershell.md index e9fa323f..493d4db8 100644 --- a/docs/sparrow_completion_powershell.md +++ b/docs/sparrow_completion_powershell.md @@ -35,4 +35,4 @@ sparrow completion powershell [flags] * [sparrow completion](sparrow_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_completion_zsh.md b/docs/sparrow_completion_zsh.md index efff49fd..e98c55a2 100644 --- a/docs/sparrow_completion_zsh.md +++ b/docs/sparrow_completion_zsh.md @@ -49,4 +49,4 @@ sparrow completion zsh [flags] * [sparrow completion](sparrow_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_gen-docs.md b/docs/sparrow_gen-docs.md index d009341d..14d0dd0f 100644 --- a/docs/sparrow_gen-docs.md +++ b/docs/sparrow_gen-docs.md @@ -27,4 +27,4 @@ sparrow gen-docs [flags] * [sparrow](sparrow.md) - Sparrow, the infrastructure monitoring agent -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/docs/sparrow_run.md b/docs/sparrow_run.md index 268750d4..1f4f879b 100644 --- a/docs/sparrow_run.md +++ b/docs/sparrow_run.md @@ -13,16 +13,16 @@ sparrow run [flags] ### Options ``` - --apiListeningAddress string api: The address the server is listening on (default ":8080") - -h, --help help for run - --loaderFilePath string file loader: The path to the file to read the runtime config from (default "config.yaml") - --loaderHttpRetryCount int http loader: Amount of retries trying to load the configuration (default 3) - --loaderHttpRetryDelay int http loader: The initial delay between retries in seconds (default 1) - --loaderHttpTimeout int http loader: The timeout for the http request in seconds (default 30) - --loaderHttpToken string http loader: Bearer token to authenticate the http endpoint - --loaderHttpUrl string http loader: The url where to get the remote configuration - --loaderInterval int defines the interval the loader reloads the configuration in seconds (default 300) - -l, --loaderType string defines the loader type that will load the checks configuration during the runtime. The fallback is the fileLoader (default "http") + --apiAddress string api: The address the server is listening on (default ":8080") + -h, --help help for run + --loaderFilePath string file loader: The path to the file to read the runtime config from (default "config.yaml") + --loaderHttpRetryCount int http loader: Amount of retries trying to load the configuration (default 3) + --loaderHttpRetryDelay int http loader: The initial delay between retries in seconds (default 1) + --loaderHttpTimeout int http loader: The timeout for the http request in seconds (default 30) + --loaderHttpToken string http loader: Bearer token to authenticate the http endpoint + --loaderHttpUrl string http loader: The url where to get the remote configuration + --loaderInterval int defines the interval the loader reloads the configuration in seconds (default 300) + -l, --loaderType string defines the loader type that will load the checks configuration during the runtime. The fallback is the fileLoader (default "http") ``` ### Options inherited from parent commands @@ -35,4 +35,4 @@ sparrow run [flags] * [sparrow](sparrow.md) - Sparrow, the infrastructure monitoring agent -###### Auto generated by spf13/cobra on 27-Nov-2023 +###### Auto generated by spf13/cobra on 30-Nov-2023 diff --git a/go.mod b/go.mod index 38bc5df6..5a666714 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 + golang.org/x/sync v0.3.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 7f66ce71..7b78dc7e 100644 --- a/go.sum +++ b/go.sum @@ -316,6 +316,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/checks/checks.go b/pkg/checks/checks.go index 942eefe0..e8f05fb5 100644 --- a/pkg/checks/checks.go +++ b/pkg/checks/checks.go @@ -31,7 +31,8 @@ import ( // The key is the name of the Check // The name needs to map the configuration item key var RegisteredChecks = map[string]func() Check{ - "health": NewHealthCheck, + "health": NewHealthCheck, + "latency": NewLatencyCheck, } //go:generate moq -out checks_moq.go . Check diff --git a/pkg/checks/latency.go b/pkg/checks/latency.go new file mode 100644 index 00000000..5c222539 --- /dev/null +++ b/pkg/checks/latency.go @@ -0,0 +1,179 @@ +package checks + +import ( + "context" + "net/http" + "sync" + "time" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/mitchellh/mapstructure" + "golang.org/x/sync/errgroup" + + "github.com/caas-team/sparrow/internal/helper" + "github.com/caas-team/sparrow/internal/logger" + "github.com/caas-team/sparrow/pkg/api" +) + +var _ Check = (*Latency)(nil) + +func NewLatencyCheck() Check { + return &Latency{ + mu: sync.Mutex{}, + cfg: LatencyConfig{}, + c: nil, + done: make(chan bool, 1), + } + +} + +type Latency struct { + cfg LatencyConfig + mu sync.Mutex + c chan<- Result + done chan bool +} + +type LatencyConfig struct { + Targets []string + Interval time.Duration + Timeout time.Duration + Retry helper.RetryConfig +} + +type LatencyResult struct { + Code int `json:"code"` + Error *string `json:"error"` + Total int64 `json:"total"` +} + +func (l *Latency) Run(ctx context.Context) error { + log := logger.FromContext(ctx).WithGroup("Latency") + log.Info(l.cfg.Interval.String()) + for { + select { + case <-ctx.Done(): + log.Error("context canceled", "err", ctx.Err()) + return ctx.Err() + case <-l.done: + return nil + case <-time.After(l.cfg.Interval): + results, err := l.check(ctx) + errval := "" + if err != nil { + errval = err.Error() + } + checkResult := Result{ + Data: results, + Err: errval, + Timestamp: time.Now(), + } + + l.c <- checkResult + } + } +} + +func (l *Latency) Startup(ctx context.Context, cResult chan<- Result) error { + log := logger.FromContext(ctx).WithGroup("latency") + log.Debug("Starting latency check") + + l.c = cResult + return nil +} + +func (l *Latency) Shutdown(ctx context.Context) error { + l.done <- true + close(l.done) + + return nil +} + +func (l *Latency) SetConfig(ctx context.Context, config any) error { + var c LatencyConfig + err := mapstructure.Decode(config, &c) + if err != nil { + return ErrInvalidConfig + } + c.Interval = time.Second * c.Interval + c.Retry.Delay = time.Second * c.Retry.Delay + l.mu.Lock() + defer l.mu.Unlock() + l.cfg = c + + return nil +} + +func (l *Latency) Schema() (*openapi3.SchemaRef, error) { + return OpenapiFromPerfData(make(map[string]LatencyResult)) +} + +func (l *Latency) RegisterHandler(ctx context.Context, router *api.RoutingTree) { + router.Add(http.MethodGet, "v1alpha1/latency", l.Handler) +} + +func (l *Latency) DeregisterHandler(ctx context.Context, router *api.RoutingTree) { + router.Remove(http.MethodGet, "v1alpha1/latency") +} + +func (l *Latency) Handler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func (l *Latency) check(ctx context.Context) (map[string]LatencyResult, error) { + log := logger.FromContext(ctx).WithGroup("check") + log.Debug("Checking latency") + + var resultMutex sync.Mutex + results := map[string]LatencyResult{} + + wg, ctx := errgroup.WithContext(ctx) + for _, e := range l.cfg.Targets { + wg.Go(func(ctx context.Context, e string) func() error { + return func() error { + cl := http.Client{ + Timeout: l.cfg.Timeout * time.Second, + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, e, nil) + if err != nil { + log.Error("Error while creating request", "error", err) + return err + } + + var latencyresult LatencyResult + + req = req.WithContext(ctx) + + helper.Retry(func(ctx context.Context) error { + start := time.Now() + response, err := cl.Do(req) + if err != nil { + errval := err.Error() + latencyresult.Error = &errval + log.Error("Error while checking latency", "error", err) + + } else { + latencyresult.Code = response.StatusCode + } + end := time.Now() + + latencyresult.Total = end.Sub(start).Milliseconds() + + resultMutex.Lock() + defer resultMutex.Unlock() + results[e] = latencyresult + + return err + }, l.cfg.Retry)(ctx) // ignore return value, since we set it in the closure + return nil + } + }(ctx, e)) + } + + if err := wg.Wait(); err != nil { + log.Error("Error while checking latency", "error", err) + return nil, err + } + + return results, nil +} diff --git a/pkg/checks/latency_test.go b/pkg/checks/latency_test.go new file mode 100644 index 00000000..245b83d4 --- /dev/null +++ b/pkg/checks/latency_test.go @@ -0,0 +1,237 @@ +package checks + +import ( + "context" + "net/http" + "net/http/httptest" + "reflect" + "sync" + "testing" + "time" + + "github.com/caas-team/sparrow/pkg/api" + "github.com/jarcoal/httpmock" +) + +func stringPointer(s string) *string { + return &s +} +func TestLatency_check(t *testing.T) { + httpmock.Activate() + defer httpmock.Deactivate() + + httpmock.RegisterResponder(http.MethodGet, "http://success.com", httpmock.NewStringResponder(200, "ok")) + httpmock.RegisterResponder(http.MethodGet, "http://fail.com", httpmock.NewStringResponder(500, "fail")) + httpmock.RegisterResponder(http.MethodGet, "http://timeout.com", httpmock.NewErrorResponder(context.DeadlineExceeded)) + + cResult := make(chan Result, 1) + c := Latency{ + cfg: LatencyConfig{}, + mu: sync.Mutex{}, + c: cResult, + done: make(chan bool, 1), + } + results := make(chan Result, 1) + c.Startup(context.Background(), results) + + c.SetConfig(context.Background(), LatencyConfig{ + Targets: []string{"http://success.com", "http://fail.com", "http://timeout.com"}, + Interval: time.Second * 120, + Timeout: time.Second * 1, + }) + defer c.Shutdown(context.Background()) + + data, err := c.check(context.Background()) + + if err != nil { + t.Errorf("Latency.check() error = %v", err) + } + + wantData := map[string]LatencyResult{ + "http://success.com": { + Code: 200, + Error: nil, + Total: 0, + }, + "http://fail.com": { + Code: 500, + Error: nil, + Total: 0, + }, + "http://timeout.com": { + Code: 0, + Error: stringPointer("Get \"http://timeout.com\": context deadline exceeded"), + Total: 0, + }, + } + + for k, v := range wantData { + if v.Code != data[k].Code { + t.Errorf("Latency.Run() = %v, want %v", data[k].Code, v.Code) + } + if v.Total != data[k].Total { + t.Errorf("Latency.Run() = %v, want %v", data[k].Total, v.Total) + } + if v.Error != nil && data[k].Error != nil { + if *v.Error != *data[k].Error { + t.Errorf("Latency.Run() = %v, want %v", *data[k].Error, *v.Error) + } + } + } + +} + +func TestLatency_Run(t *testing.T) { + httpmock.Activate() + defer httpmock.Deactivate() + + httpmock.RegisterResponder(http.MethodGet, "http://success.com", httpmock.NewStringResponder(200, "ok")) + httpmock.RegisterResponder(http.MethodGet, "http://fail.com", httpmock.NewStringResponder(500, "fail")) + httpmock.RegisterResponder(http.MethodGet, "http://timeout.com", httpmock.NewErrorResponder(context.DeadlineExceeded)) + + c := NewLatencyCheck() + results := make(chan Result, 1) + c.Startup(context.Background(), results) + + c.SetConfig(context.Background(), LatencyConfig{ + Targets: []string{"http://success.com", "http://fail.com", "http://timeout.com"}, + Interval: time.Second * 120, + Timeout: time.Second * 1, + }) + go c.Run(context.Background()) + defer c.Shutdown(context.Background()) + + result := <-results + wantResult := Result{ + Timestamp: result.Timestamp, + Err: "", + Data: map[string]LatencyResult{ + "http://success.com": { + Code: 200, + Error: nil, + Total: 0, + }, + "http://fail.com": { + Code: 500, + Error: nil, + Total: 0, + }, + "http://timeout.com": { + Code: 0, + Error: stringPointer("Get \"http://timeout.com\": context deadline exceeded"), + Total: 0, + }, + }, + } + + if wantResult.Timestamp != result.Timestamp { + t.Errorf("Latency.Run() = %v, want %v", result.Timestamp, wantResult.Timestamp) + } + if wantResult.Err != result.Err { + t.Errorf("Latency.Run() = %v, want %v", result.Err, wantResult.Err) + } + wantData := wantResult.Data.(map[string]LatencyResult) + data := result.Data.(map[string]LatencyResult) + + for k, v := range wantData { + if v.Code != data[k].Code { + t.Errorf("Latency.Run() = %v, want %v", data[k].Code, v.Code) + } + if v.Total != data[k].Total { + t.Errorf("Latency.Run() = %v, want %v", data[k].Total, v.Total) + } + if v.Error != nil && data[k].Error != nil { + if *v.Error != *data[k].Error { + t.Errorf("Latency.Run() = %v, want %v", *data[k].Error, *v.Error) + } + } + } +} + +func TestLatency_Startup(t *testing.T) { + c := Latency{} + + if err := c.Startup(context.Background(), make(chan<- Result, 1)); err != nil { + t.Errorf("Startup() error = %v", err) + } +} + +func TestLatency_Shutdown(t *testing.T) { + cDone := make(chan bool, 1) + c := Latency{ + done: cDone, + } + err := c.Shutdown(context.Background()) + + if err != nil { + t.Errorf("Shutdown() error = %v", err) + } + + if !<-cDone { + t.Error("Shutdown() should be ok") + } + +} + +func TestLatency_SetConfig(t *testing.T) { + c := Latency{} + wantCfg := LatencyConfig{ + Targets: []string{"http://localhost:9090"}, + } + + err := c.SetConfig(context.Background(), wantCfg) + + if err != nil { + t.Errorf("SetConfig() error = %v", err) + } + if !reflect.DeepEqual(c.cfg, wantCfg) { + t.Errorf("SetConfig() = %v, want %v", c.cfg, wantCfg) + } +} + +func TestLatency_RegisterHandler(t *testing.T) { + c := Latency{} + + rt := api.NewRoutingTree() + c.RegisterHandler(context.Background(), &rt) + + h, ok := rt.Get("GET", "v1alpha1/latency") + + if !ok { + t.Error("RegisterHandler() should be ok") + } + if h == nil { + t.Error("RegisterHandler() should not be nil") + } + c.DeregisterHandler(context.Background(), &rt) + h, ok = rt.Get("GET", "v1alpha1/latency") + + if ok { + t.Error("DeregisterHandler() should not be ok") + } + + if h != nil { + t.Error("DeregisterHandler() should be nil") + } + +} + +func TestLatency_Handler(t *testing.T) { + c := Latency{} + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/v1alpha1/latency", nil) + + c.Handler(rec, req) + + if rec.Code != http.StatusOK { + t.Errorf("Handler() should be ok, got %d", rec.Code) + } + +} + +func TestNewLatencyCheck(t *testing.T) { + c := NewLatencyCheck() + if c == nil { + t.Error("NewLatencyCheck() should not be nil") + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go index a4616073..50f9fb15 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -63,7 +63,7 @@ func NewConfig() *Config { } } -func (c *Config) SetApiListeningAddress(address string) { +func (c *Config) SetApiAddress(address string) { c.Api.ListeningAddress = address } diff --git a/pkg/config/file_test.go b/pkg/config/file_test.go index 74282532..bee4f547 100644 --- a/pkg/config/file_test.go +++ b/pkg/config/file_test.go @@ -25,7 +25,7 @@ import ( ) func TestNewFileLoader(t *testing.T) { - l := NewFileLoader(&Config{Loader: LoaderConfig{file: FileLoaderConfig{path: "config.yaml"}}}, make(chan<- map[string]any)) + l := NewFileLoader(&Config{Loader: LoaderConfig{file: FileLoaderConfig{path: "config.yaml"}}}, make(chan<- map[string]any, 1)) if l.path != "config.yaml" { t.Errorf("Expected path to be config.yaml, got %s", l.path) @@ -53,7 +53,7 @@ func TestFileLoader_Run(t *testing.T) { args args want want }{ - {name: "Loads config from file", fields: fields{path: "testdata/config.yaml", c: make(chan map[string]any)}, args: func() args { + {name: "Loads config from file", fields: fields{path: "testdata/config.yaml", c: make(chan map[string]any, 1)}, args: func() args { ctx, cancel := context.WithCancel(context.Background()) return args{ctx: &ctx, cancel: &cancel} }(), want: want{cfg: map[string]any{"testCheck1": map[string]any{"enabled": true}}}}, diff --git a/pkg/config/flags.go b/pkg/config/flags.go index f7b1031f..45eea6ac 100644 --- a/pkg/config/flags.go +++ b/pkg/config/flags.go @@ -19,7 +19,7 @@ package config type RunFlagsNameMapping struct { - ApiListeningAddress string + ApiAddress string LoaderType string LoaderInterval string diff --git a/pkg/config/http_test.go b/pkg/config/http_test.go index 2933aa9c..ffa9b761 100644 --- a/pkg/config/http_test.go +++ b/pkg/config/http_test.go @@ -143,7 +143,7 @@ func TestHttpLoader_GetRuntimeConfig(t *testing.T) { gl := &HttpLoader{ cfg: tt.cfg, - cCfgChecks: make(chan<- map[string]any), + cCfgChecks: make(chan<- map[string]any, 1), } gl.cfg.Loader.http.url = endpoint diff --git a/pkg/config/loader.go b/pkg/config/loader.go index 11d241fd..c3fe48d9 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -22,10 +22,6 @@ import ( "context" ) -const ( - gitlabLoader = "GITLAB" - localLoader = "LOCAL" -) type Loader interface { Run(context.Context) diff --git a/pkg/sparrow/api.go b/pkg/sparrow/api.go index 989df634..18b9d4d5 100644 --- a/pkg/sparrow/api.go +++ b/pkg/sparrow/api.go @@ -58,14 +58,15 @@ func (s *Sparrow) register(ctx context.Context) { // // Blocks until context is done func (s *Sparrow) api(ctx context.Context) error { - log := logger.FromContext(ctx) - cErr := make(chan error) + log := logger.FromContext(ctx).WithGroup("api") + cErr := make(chan error, 1) s.register(ctx) server := http.Server{Addr: s.cfg.Api.ListeningAddress, Handler: s.router} // run http server in goroutine go func(cErr chan error) { defer close(cErr) + log.Info("serving api", "addr", s.cfg.Api.ListeningAddress) if err := server.ListenAndServe(); err != nil { log.Error("failed to serve api", "error", err) cErr <- err diff --git a/pkg/sparrow/run.go b/pkg/sparrow/run.go index 36cfa81a..05e115e0 100644 --- a/pkg/sparrow/run.go +++ b/pkg/sparrow/run.go @@ -53,10 +53,10 @@ func New(cfg *config.Config) *Sparrow { router: chi.NewRouter(), routingTree: api.NewRoutingTree(), checks: make(map[string]checks.Check), - cResult: make(chan checks.ResultDTO), + cResult: make(chan checks.ResultDTO, 1), resultFanIn: make(map[string]chan checks.Result), cfg: cfg, - cCfgChecks: make(chan map[string]any), + cCfgChecks: make(chan map[string]any, 1), db: db.NewInMemory(), } @@ -116,7 +116,7 @@ func (s *Sparrow) ReconcileChecks(ctx context.Context) { s.checks[name] = check // Create a fan in channel for the check - checkChan := make(chan checks.Result) + checkChan := make(chan checks.Result, 1) s.resultFanIn[name] = checkChan err := check.SetConfig(ctx, checkCfg) diff --git a/pkg/sparrow/run_test.go b/pkg/sparrow/run_test.go index 53f19ae9..72e98091 100644 --- a/pkg/sparrow/run_test.go +++ b/pkg/sparrow/run_test.go @@ -86,7 +86,7 @@ func TestSparrow_ReconcileChecks(t *testing.T) { fields: fields{ checks: map[string]checks.Check{}, cfg: &config.Config{}, - cCfgChecks: make(chan map[string]any), + cCfgChecks: make(chan map[string]any, 1), resultFanIn: make(map[string]chan checks.Result), }, newChecksConfig: map[string]any{ @@ -100,7 +100,7 @@ func TestSparrow_ReconcileChecks(t *testing.T) { "alpha": checks.RegisteredChecks["alpha"](), }, cfg: &config.Config{}, - cCfgChecks: make(chan map[string]any), + cCfgChecks: make(chan map[string]any, 1), resultFanIn: make(map[string]chan checks.Result), }, newChecksConfig: map[string]any{ @@ -115,7 +115,7 @@ func TestSparrow_ReconcileChecks(t *testing.T) { "alpha": checks.RegisteredChecks["alpha"](), }, cfg: &config.Config{}, - cCfgChecks: make(chan map[string]any), + cCfgChecks: make(chan map[string]any, 1), resultFanIn: make(map[string]chan checks.Result), }, newChecksConfig: map[string]any{}, @@ -128,7 +128,7 @@ func TestSparrow_ReconcileChecks(t *testing.T) { "gamma": checks.RegisteredChecks["alpha"](), }, cfg: &config.Config{}, - cCfgChecks: make(chan map[string]any), + cCfgChecks: make(chan map[string]any, 1), resultFanIn: make(map[string]chan checks.Result), }, newChecksConfig: map[string]any{ @@ -163,8 +163,8 @@ func TestSparrow_ReconcileChecks(t *testing.T) { } func Test_fanInResults(t *testing.T) { - checkChan := make(chan checks.Result) - cResult := make(chan checks.ResultDTO) + checkChan := make(chan checks.Result, 1) + cResult := make(chan checks.ResultDTO, 1) name := "check" go fanInResults(checkChan, cResult, name) From 6477ffcedbdbb9faee263752fcd9a6b730d5c8b7 Mon Sep 17 00:00:00 2001 From: Maximilian Schubert Date: Fri, 1 Dec 2023 11:40:31 +0100 Subject: [PATCH 2/3] Ci/action (#25) * ci: add ci and release pipeline --- .github/workflows/ci.yml | 64 +++++++++++++++++++ .github/workflows/release.yml | 38 +++++++++++ .github/workflows/test_sast.yml | 23 +++++++ .github/workflows/{test.yml => test_unit.yml} | 15 +++-- .goreleaser-ci.yaml | 24 +++++++ .goreleaser.yaml | 37 +++++++++++ Dockerfile | 19 ++++++ README.md | 34 +++++++++- 8 files changed, 245 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test_sast.yml rename .github/workflows/{test.yml => test_unit.yml} (57%) create mode 100644 .goreleaser-ci.yaml create mode 100644 .goreleaser.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..2a7ee6ba --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: Contiuous Integration + +on: + push: + pull_request: + +permissions: + contents: write + packages: write + security-events: write + +jobs: + rel: + name: Build, scan & push Snapshot + runs-on: ubuntu-latest + + permissions: + contents: write + packages: write + security-events: write + + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + + - name: Build snapshot artifacts + uses: goreleaser/goreleaser-action@v5 + with: + version: latest + args: release --snapshot --clean --config .goreleaser-ci.yaml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get Version + id: version + run: jq -r '.version' ./dist/metadata.json | { read value; echo "value=$value"; } >> "$GITHUB_OUTPUT" + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: "ghcr.io/caas-team/sparrow:v${{ steps.version.outputs.value }}" + format: "sarif" + output: "trivy-results.sarif" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results.sarif" + + - name: Registry login + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push snapshot container image + run: docker push ghcr.io/caas-team/sparrow:v${{ steps.version.outputs.value }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..a667d5d1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release + +on: + push: + tags: + - "v[012].[0-9]+.[0-9]+" + +permissions: + contents: write + packages: write + +jobs: + rel: + name: Release Sparrow + runs-on: ubuntu-latest + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build, push & release + uses: goreleaser/goreleaser-action@v5 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test_sast.yml b/.github/workflows/test_sast.yml new file mode 100644 index 00000000..dacd396b --- /dev/null +++ b/.github/workflows/test_sast.yml @@ -0,0 +1,23 @@ +name: Test - SAST + +on: + pull_request: + +permissions: + contents: read + +jobs: + tests: + runs-on: ubuntu-latest + + env: + GO111MODULE: on + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + args: ./... \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test_unit.yml similarity index 57% rename from .github/workflows/test.yml rename to .github/workflows/test_unit.yml index d7966bbe..3bbdc3d1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test_unit.yml @@ -1,25 +1,28 @@ -name: test +name: Test - Unit on: + push: pull_request: - branches: - - main + +permissions: + contents: read jobs: test_go: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: - go-version-file: 'go.mod' + go-version-file: go.mod - name: Test run: | go mod download go install github.com/matryer/moq@v0.3.3 go generate ./... - go test --race --coverprofile cover.out -v ./... + go test --race --coverprofile cover.out -v ./... \ No newline at end of file diff --git a/.goreleaser-ci.yaml b/.goreleaser-ci.yaml new file mode 100644 index 00000000..5680351c --- /dev/null +++ b/.goreleaser-ci.yaml @@ -0,0 +1,24 @@ +project_name: sparrow +builds: + - env: [CGO_ENABLED=0] + ldflags: + - -s -w -X main.version=v{{ .Version }} + - -extldflags "-static" + goos: + - linux + goarch: + - amd64 + - arm64 +dockers: + - image_templates: + - "ghcr.io/caas-team/sparrow:v{{ .Version }}" + dockerfile: Dockerfile + build_flag_templates: + - --label=org.opencontainers.image.title={{ .ProjectName }} + - --label=org.opencontainers.image.description="This is a pre-release version. Do not use this in production!" + - --label=org.opencontainers.image.url=https://caas.telekom.de + - --label=org.opencontainers.image.source=https://github.com/caas-team/sparrow + - --label=org.opencontainers.image.version={{ .Version }} + - --label=org.opencontainers.image.created={{ .Timestamp }} + - --label=org.opencontainers.image.revision={{ .FullCommit }} + - --label=org.opencontainers.image.licenses="Apache 2.0" diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 00000000..934ae813 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,37 @@ +project_name: sparrow +builds: + - env: [CGO_ENABLED=0] + ldflags: + - -s -w -X main.version={{ .Tag }} + - -extldflags "-static" + goos: + - linux + goarch: + - amd64 + - arm64 +dockers: + - image_templates: + - "ghcr.io/caas-team/sparrow:latest" + - "ghcr.io/caas-team/sparrow:{{ .Tag }}" + - "ghcr.io/caas-team/sparrow:v{{ .Major }}.{{ .Minor }}" + - "ghcr.io/caas-team/sparrow:v{{ .Major }}" + dockerfile: Dockerfile + build_flag_templates: + - --label=org.opencontainers.image.title={{ .ProjectName }} + - --label=org.opencontainers.image.description={{ .ProjectName }} + - --label=org.opencontainers.image.url=https://caas.telekom.de + - --label=org.opencontainers.image.source=https://github.com/caas-team/sparrow + - --label=org.opencontainers.image.version={{ .Version }} + - --label=org.opencontainers.image.created={{ .Timestamp }} + - --label=org.opencontainers.image.revision={{ .FullCommit }} + - --label=org.opencontainers.image.licenses="Apache 2.0" +nfpms: + - maintainer: CaaS + description: |- + Monitoring tool to gather infrastructure network information + homepage: https://github.com/caas-team + license: Apache 2.0 + formats: + - deb + - rpm + - apk diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..4a590d19 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM alpine:3.18 as prep + +RUN apk add --no-cache ca-certificates +RUN adduser \ + --disabled-password \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid 65532 \ + sparrow + + +FROM scratch +COPY --from=prep /etc/passwd /etc/passwd +COPY --from=prep /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY sparrow ./ + +USER sparrow + +ENTRYPOINT ["/sparrow", "run"] \ No newline at end of file diff --git a/README.md b/README.md index fe613035..76da0dcc 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,13 @@ - [Container Image](#container-image) - [Helm](#helm) - [Usage](#usage) + - [Container Image](#container-image-1) - [Configuration](#configuration) - [Startup](#startup) - [Loader](#loader) - [Runtime](#runtime) - [Check: Health](#check-health) + - [Check: Latency](#check-latency) - [API](#api) - [Code of Conduct](#code-of-conduct) - [Working Language](#working-language) @@ -39,13 +41,31 @@ The `sparrow` performs several checks to monitor the health of the infrastructur The `sparrow` is provided as an small binary & a container image. +Please see the [release notes](https://github.com/caas-team/sparrow/releases) for to get the latest version. + ### Binary -tbd +The binary is available for several distributions. Currently the binary needs to be installed from a provided bundle or source. + +```sh +curl https://github.com/caas-team/sparrow/releases/download/v${RELEASE_VERSION}/sparrow_${RELEASE_VERSION}_linux_amd64.tar.gz -Lo sparrow.tar.gz +curl https://github.com/caas-team/sparrow/releases/download/v${RELEASE_VERSION}/sparrow_${RELEASE_VERSION}_checksums.txt -Lo checksums.txt +``` + +For example release `v0.0.1`: +```sh +curl https://github.com/caas-team/sparrow/releases/download/v0.0.1/sparrow_0.0.1_linux_amd64.tar.gz -Lo sparrow.tar.gz +curl https://github.com/caas-team/sparrow/releases/download/v0.0.1/sparrow_0.0.1_checksums.txt -Lo checksums.txt +``` + +Extract the binary: +```sh +tar -xf sparrow.tar.gz +``` ### Container Image -tbd +The [sparrow container images](https://github.com/caas-team/sparrow/pkgs/container/sparrow) for dedicated [release](https://github.com/caas-team/sparrow/releases) can be found in the GitHub registry. ### Helm @@ -53,7 +73,15 @@ tbd ## Usage -Use `sparrow run` to execute the instance. +Use `sparrow run` to execute the instance using the binary. + +### Container Image + +Run a `sparrow` container by using e.g. `docker run ghcr.io/caas-team/sparrow`. + +Pass the available configuration arguments to the container e.g. `docker run ghcr.io/caas-team/sparrow --help`. + +Start the instance using a mounted startup configuration file e.g. `docker run -v /config:/config ghcr.io/caas-team/sparrow --config /config/config.yaml`. ## Configuration From faab23e94e58cc2c014a31bd03894299c4b94744 Mon Sep 17 00:00:00 2001 From: Frank Kloeker Date: Tue, 5 Dec 2023 13:11:14 +0100 Subject: [PATCH 3/3] [Feature] Helm chart (#26) * create Helm chart * add end2end test * generate helm-docs for chart * feat: update readme, codeowners --------- Signed-off-by: Frank Kloeker Co-authored-by: maximilian.schubert@telekom.de --- .github/workflows/ci.yml | 6 +- .github/workflows/end2end.yml | 84 ++++++++++++++++++ .github/workflows/release.yml | 22 ++++- .goreleaser-ci.yaml | 6 +- .pre-commit-config.yaml | 7 ++ CODEOWNERS | 2 +- NOTICE | 3 +- README.md | 29 +++++- chart/Chart.yaml | 18 ++++ chart/README.md | 62 +++++++++++++ chart/templates/_helpers.tpl | 68 ++++++++++++++ chart/templates/configmap.yaml | 35 ++++++++ chart/templates/deployment.yaml | 100 +++++++++++++++++++++ chart/templates/ingress.yaml | 61 +++++++++++++ chart/templates/networkpolicy.yaml | 19 ++++ chart/templates/secret.yaml | 11 +++ chart/templates/service.yaml | 15 ++++ chart/templates/serviceaccount.yaml | 13 +++ chart/values.yaml | 133 ++++++++++++++++++++++++++++ docs/img/sparrow.png | Bin 0 -> 101088 bytes 20 files changed, 681 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/end2end.yml create mode 100644 .pre-commit-config.yaml create mode 100644 chart/Chart.yaml create mode 100644 chart/README.md create mode 100644 chart/templates/_helpers.tpl create mode 100644 chart/templates/configmap.yaml create mode 100644 chart/templates/deployment.yaml create mode 100644 chart/templates/ingress.yaml create mode 100644 chart/templates/networkpolicy.yaml create mode 100644 chart/templates/secret.yaml create mode 100644 chart/templates/service.yaml create mode 100644 chart/templates/serviceaccount.yaml create mode 100644 chart/values.yaml create mode 100644 docs/img/sparrow.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a7ee6ba..62d232b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,12 +39,12 @@ jobs: - name: Get Version id: version - run: jq -r '.version' ./dist/metadata.json | { read value; echo "value=$value"; } >> "$GITHUB_OUTPUT" + run: echo "value=commit-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: - image-ref: "ghcr.io/caas-team/sparrow:v${{ steps.version.outputs.value }}" + image-ref: "ghcr.io/caas-team/sparrow:${{ steps.version.outputs.value }}" format: "sarif" output: "trivy-results.sarif" @@ -61,4 +61,4 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Push snapshot container image - run: docker push ghcr.io/caas-team/sparrow:v${{ steps.version.outputs.value }} \ No newline at end of file + run: docker push ghcr.io/caas-team/sparrow:${{ steps.version.outputs.value }} \ No newline at end of file diff --git a/.github/workflows/end2end.yml b/.github/workflows/end2end.yml new file mode 100644 index 00000000..e8a565e9 --- /dev/null +++ b/.github/workflows/end2end.yml @@ -0,0 +1,84 @@ +# This workflow installs 1 instance of sparrow and +# verify the API output + +name: End2End Testing +on: + push: + paths: + - 'chart/**' + +jobs: + end2end: + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + - name: Set up K3S + uses: debianmaster/actions-k3s@master + id: k3s + with: + version: 'v1.26.9-k3s1' + - name: Check Cluster + run: | + kubectl get nodes + - name: Check Coredns Deployment + run: | + kubectl -n kube-system rollout status deployment/coredns --timeout=60s + STATUS=$(kubectl -n kube-system get deployment coredns -o jsonpath={.status.readyReplicas}) + if [[ $STATUS -ne 1 ]] + then + echo "Deployment coredns not ready" + kubectl -n kube-system get events + exit 1 + else + echo "Deployment coredns OK" + fi + - name: Check Metricsserver Deployment + run: | + kubectl -n kube-system rollout status deployment/metrics-server --timeout=60s + STATUS=$(kubectl -n kube-system get deployment metrics-server -o jsonpath={.status.readyReplicas}) + if [[ $STATUS -ne 1 ]] + then + echo "Deployment metrics-server not ready" + kubectl -n kube-system get events + exit 1 + else + echo "Deployment metrics-server OK" + fi + - name: Setup Helm + run: | + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash + helm version + - name: Get Image Tag + id: version + run: echo "value=commit-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + - name: Install Sparrow + run: | + helm upgrade -i sparrow \ + --atomic \ + --timeout 300s \ + --set extraArgs.loaderType=file \ + --set extraArgs.loaderFilePath=/runconfig/checks.yaml \ + --set image.tag=${{ steps.version.outputs.value }} \ + chart + - name: Check Pods + run: | + kubectl get pods + - name: Wait for Sparrow + run: | + sleep 60 + - name: Healthcheck + run: | + kubectl create job curl --image=quay.io/curl/curl:latest -- curl -f -v -H 'Content-Type: application/json' http://sparrow:8080/v1/metrics/health + kubectl wait --for=condition=complete job/curl + STATUS=$(kubectl get job curl -o jsonpath={.status.succeeded}) + if [[ $STATUS -ne 1 ]] + then + echo "Job failed" + kubectl logs -ljob-name=curl + kubectl delete job curl + exit 1 + else + echo "Job OK" + kubectl delete job curl + fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a667d5d1..118ed7b4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ permissions: packages: write jobs: - rel: + main: name: Release Sparrow runs-on: ubuntu-latest steps: @@ -35,4 +35,22 @@ jobs: version: latest args: release --clean env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + helm: + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Registry login + run: helm registry login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} + + - name: Helm lint + run: helm lint ./chart + + - name: Helm package + run: helm package ./chart -d ./chart + + - name: Push helm package + run: helm push $(ls ./chart/*.tgz| head -1) oci://ghcr.io/${{ github.repository_owner }}/charts \ No newline at end of file diff --git a/.goreleaser-ci.yaml b/.goreleaser-ci.yaml index 5680351c..6eeaa854 100644 --- a/.goreleaser-ci.yaml +++ b/.goreleaser-ci.yaml @@ -1,8 +1,10 @@ project_name: sparrow +snapshot: + name_template: "commit-{{ .ShortCommit }}" builds: - env: [CGO_ENABLED=0] ldflags: - - -s -w -X main.version=v{{ .Version }} + - -s -w -X main.version={{ .Version }} - -extldflags "-static" goos: - linux @@ -11,7 +13,7 @@ builds: - arm64 dockers: - image_templates: - - "ghcr.io/caas-team/sparrow:v{{ .Version }}" + - "ghcr.io/caas-team/sparrow:{{ .Version }}" dockerfile: Dockerfile build_flag_templates: - --label=org.opencontainers.image.title={{ .ProjectName }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..501eac9d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/norwoodj/helm-docs + rev: "v1.11.3" + hooks: + - id: helm-docs + args: + - --chart-search-root=chart diff --git a/CODEOWNERS b/CODEOWNERS index fcb6b7c5..73bf39fa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @y-eight @NiklasTreml @puffitos @nico151999 @lvlcn-t \ No newline at end of file +* @y-eight @NiklasTreml @puffitos @nico151999 @lvlcn-t @eumel8 \ No newline at end of file diff --git a/NOTICE b/NOTICE index 22703024..b2be5917 100644 --- a/NOTICE +++ b/NOTICE @@ -10,4 +10,5 @@ Maximilian Schubert [y-eight], Deutsche Telekom IT GmbH Niklas Treml [niklastreml], Deutsche Telekom IT GmbH Bruno Bressi [puffitos], Deutsche Telekom IT GmbH Nico Feulner [nico151999], Deutsche Telekom IT GmbH -Tom Vendolsky [lvlcn-t], Deutsche Telekom IT GmbH \ No newline at end of file +Tom Vendolsky [lvlcn-t], Deutsche Telekom IT GmbH +Frank Kloeker [eumel8], Deutsche Telekom IT GmbH \ No newline at end of file diff --git a/README.md b/README.md index 76da0dcc..cbad09e4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# `sparrow` aka Check Sparrow +# Sparrow - Infrastructure Monitoring

@@ -35,7 +35,7 @@ The `sparrow` performs several checks to monitor the health of the infrastructur 1. Health check - `health`: The `sparrow` is able perform an http-based (HTTP/1.1) health check to provided endpoints. The `sparrow` will expose its own health check endpoint as well. -2. Latency check - `rtt`: The `sparrow` is able to communicate with other `sparrow` instances to calculate the time a request takes to the target and back. The check is http (HTTP/1.1) based as well. +2. Latency check - `latency`: The `sparrow` is able to communicate with other `sparrow` instances to calculate the time a request takes to the target and back. The check is http (HTTP/1.1) based as well. ## Installation @@ -69,7 +69,28 @@ The [sparrow container images](https://github.com/caas-team/sparrow/pkgs/contain ### Helm -tbd +Sparrow can be install via Helm Chart. The chart is provided in the GitHub registry: + +```sh +helm -n sparrow upgrade -i sparrow oci://ghcr.io/caas-team/charts/sparrow --version 1.0.0 --create-namespace +``` + +The default settings are fine for a local running configuration. With the default Helm values the sparrow loader uses a runtime configuration that is provided in a ConfigMap. The ConfigMap can be set by defining the `runtimeConfig` section. + +To be able to load the configuration during the runtime dynamically, the sparrow loader needs to be set to type `http`. + +Use the following configuration values to use a runtime configuration by the `http` loader: + +```yaml +startupConfig: + loaderType: http + loaderHttpUrl: https://url-to-runtime-config.de/api/config%2Eyaml + +runtimeConfig: {} +``` +For all available value options see [Chart README](./chart/README.md). + +Additionally check out the sparrow [configuration](#configuration) variants. ## Usage @@ -212,4 +233,4 @@ Licensed under the **Apache License, Version 2.0** (the "License"); you may not You may obtain a copy of the License at . -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](./LICENSE) for the specific language governing permissions and limitations under the License. \ No newline at end of file +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](./LICENSE) for the specific language governing permissions and limitations under the License. diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 00000000..369c54ae --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: sparrow +description: A Helm chart to install Sparrow +type: application +keywords: + - monitoring +version: 0.0.2 +appVersion: "v0.1.0" +icon: https://github.com/caas-team/sparrow/blob/main/docs/img/sparrow.png +sources: + - https://github.com/caas-team/sparrow +maintainers: + - name: eumel8 + email: f.kloeker@telekom.de + url: https://www.telekom.com + - name: y-eight + email: maximilian.schubert@telekom.de + url: https://www.telekom.com diff --git a/chart/README.md b/chart/README.md new file mode 100644 index 00000000..96f25450 --- /dev/null +++ b/chart/README.md @@ -0,0 +1,62 @@ +# sparrow + +![Version: 0.0.2](https://img.shields.io/badge/Version-0.0.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v0.1.0](https://img.shields.io/badge/AppVersion-v0.1.0-informational?style=flat-square) + +A Helm chart to install Sparrow + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| eumel8 | | | +| y-eight | | | + +## Source Code + +* + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | | +| env | object | `{}` | | +| extraArgs | object | `{"loaderFilePath":"/runconfig/checks.yaml","loaderType":"file"}` | extra command line start parameters see: https://github.com/caas-team/sparrow/blob/main/docs/sparrow_run.md | +| fullnameOverride | string | `""` | | +| image.pullPolicy | string | `"IfNotPresent"` | | +| image.repository | string | `"ghcr.io/caas-team/sparrow"` | | +| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | +| imagePullSecrets | list | `[]` | | +| ingress.annotations | object | `{}` | | +| ingress.className | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.hosts[0].host | string | `"chart-example.local"` | | +| ingress.hosts[0].paths[0].path | string | `"/"` | | +| ingress.hosts[0].paths[0].pathType | string | `"ImplementationSpecific"` | | +| ingress.tls | list | `[]` | | +| nameOverride | string | `""` | | +| networkPolicies | object | `{"proxy":{"enabled":false}}` | define a network policy that will open egress traffic to a proxy | +| nodeSelector | object | `{}` | | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| podSecurityContext.fsGroup | int | `1000` | | +| podSecurityContext.supplementalGroups[0] | int | `1000` | | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| runtimeConfig | object | `{"health":{"enabled":true,"healthEndpoint":false,"targets":["https://www.example.com/","https://www.google.com/"]},"latency":{"enabled":true,"interval":1,"retry":{"count":3,"delay":1},"targets":["https://example.com/","https://google.com/"],"timeout":3}}` | runtime configuration of the Sparrow see: https://github.com/caas-team/sparrow#runtime | +| securityContext.allowPrivilegeEscalation | bool | `false` | | +| securityContext.capabilities.drop[0] | string | `"ALL"` | | +| securityContext.privileged | bool | `false` | | +| securityContext.readOnlyRootFilesystem | bool | `true` | | +| securityContext.runAsGroup | int | `1000` | | +| securityContext.runAsUser | int | `1000` | | +| service.port | int | `8080` | | +| service.type | string | `"ClusterIP"` | | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.automount | bool | `true` | Automatically mount a ServiceAccount's API credentials? | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| tolerations | list | `[]` | | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.11.3](https://github.com/norwoodj/helm-docs/releases/v1.11.3) diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 00000000..7cd32abb --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,68 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "sparrow.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "sparrow.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "sparrow.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "sparrow.labels" -}} +helm.sh/chart: {{ include "sparrow.chart" . }} +{{ include "sparrow.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "sparrow.selectorLabels" -}} +app.kubernetes.io/name: {{ include "sparrow.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "sparrow.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "sparrow.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{- define "startupConfig" -}} +{{- range $key, $value := .Values.startupConfig }} +{{ $key }}: {{ $value }} +{{- end }} +{{- end }} diff --git a/chart/templates/configmap.yaml b/chart/templates/configmap.yaml new file mode 100644 index 00000000..ff03e768 --- /dev/null +++ b/chart/templates/configmap.yaml @@ -0,0 +1,35 @@ +{{- if .Values.runtimeConfig}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "sparrow.fullname" . }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} +data: + checks.yaml: | + apiVersion: 0.0.1 + kind: Config + checks: + {{- if .Values.runtimeConfig.health}} + health: + enabled: {{ .Values.runtimeConfig.health.enabled }} + targets: + {{- with .Values.runtimeConfig.health.targets }} + {{- toYaml . | nindent 10 }} + {{- end }} + healthEndpoint: {{ .Values.runtimeConfig.health.healthEndpoint }} + {{- end }} + {{- if .Values.runtimeConfig.latency }} + latency: + enabled: true + interval: {{ .Values.runtimeConfig.latency.interval | default 1 }} + timeout: {{ .Values.runtimeConfig.latency.timeout | default 3 }} + retry: + count: {{ .Values.runtimeConfig.latency.retry.count | default 3 }} + delay: {{ .Values.runtimeConfig.latency.retry.delay | default 1 }} + targets: + {{- with .Values.runtimeConfig.latency.targets }} + {{- toYaml . | nindent 10 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 00000000..f5aa3b20 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "sparrow.fullname" . }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "sparrow.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "sparrow.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "sparrow.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + {{- if or .Values.extraArgs .Values.startupConfig}} + - args: + {{- end }} + {{- if .Values.startupConfig}} + - --config + - /startupconfig/.sparrow.yaml + {{- else if .Values.extraArgs }} + {{- range $key, $value := .Values.extraArgs }} + - --{{ $key }} + - {{ $value }} + {{- end }} + {{- end }} + {{- if .Values.env }} + env: + {{- range $key, $val := .Values.env }} + - name: {{ $key }} + value: {{ $val | quote }} + {{- end }} + {{- end }} + name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port | default 8080 }} + protocol: TCP + {{- if .Values.resources }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if or .Values.runtimeConfig .Values.startupConfig}} + volumeMounts: + {{- end }} + {{- if .Values.startupConfig}} + - name: startup + mountPath: /startupconfig + {{- end }} + {{- if .Values.runtimeConfig}} + - name: runtime + mountPath: /runconfig + {{- end }} + {{- if or .Values.runtimeConfig .Values.startupConfig}} + volumes: + {{- end }} + {{- if .Values.startupConfig}} + - name: startup + secret: + secretName: {{ include "sparrow.fullname" . }} + {{- end }} + {{- if .Values.runtimeConfig}} + - name: runtime + configMap: + name: {{ include "sparrow.fullname" . }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 00000000..1303ce40 --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "sparrow.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/chart/templates/networkpolicy.yaml b/chart/templates/networkpolicy.yaml new file mode 100644 index 00000000..a587008e --- /dev/null +++ b/chart/templates/networkpolicy.yaml @@ -0,0 +1,19 @@ +{{- if .Values.networkPolicies.proxy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "sparrow.fullname" . }}-proxy-np + labels: + {{- include "sparrow.labels" . | nindent 4 }} +spec: + egress: + - ports: + - port: {{ .Values.networkPolicies.proxy.port }} + protocol: TCP + to: + - ipBlock: + cidr: {{ .Values.networkPolicies.proxy.ip }}/32 + podSelector: {} + policyTypes: + - Egress +{{- end }} \ No newline at end of file diff --git a/chart/templates/secret.yaml b/chart/templates/secret.yaml new file mode 100644 index 00000000..335577af --- /dev/null +++ b/chart/templates/secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.startupConfig}} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "sparrow.fullname" . }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} +data: + .sparrow.yaml: {{ include "startupConfig" . | b64enc }} +{{- end }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 00000000..9b87cb19 --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "sparrow.fullname" . }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "sparrow.selectorLabels" . | nindent 4 }} diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml new file mode 100644 index 00000000..7ac8c31c --- /dev/null +++ b/chart/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "sparrow.serviceAccountName" . }} + labels: + {{- include "sparrow.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 00000000..002fb26a --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,133 @@ +# Default values for sparrow. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: ghcr.io/caas-team/sparrow + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # -- Specifies whether a service account should be created + create: true + # -- Automatically mount a ServiceAccount's API credentials? + automount: true + # -- Annotations to add to the service account + annotations: {} + # -- The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: + fsGroup: 1000 + supplementalGroups: + - 1000 + +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsUser: 1000 + runAsGroup: 1000 + +service: + type: ClusterIP + port: 8080 + +ingress: + enabled: false + className: "" + annotations: + {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +env: + {} + # HTTP_PROXY: + # HTTPS_PROXY: + # NO_PROXY: + +# -- define a network policy that will +# open egress traffic to a proxy +networkPolicies: + proxy: + enabled: false + # ip: 1.2.3.4 + # port: 8080 + +resources: {} +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# requests: +# cpu: 100m +# memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# -- extra command line start parameters +# see: https://github.com/caas-team/sparrow/blob/main/docs/sparrow_run.md +extraArgs: + loaderFilePath: /runconfig/checks.yaml + loaderType: file + +# -- startup configuration of the Sparrow +# see: https://github.com/caas-team/sparrow/blob/main/docs/sparrow_run.md +# startupConfig: +# apiAddress: +# loaderFilePath: /runconfig/checks.yaml +# loaderHttpRetryCount: +# loaderHttpRetryDelay: +# loaderHttpTimeout: +# loaderHttpToken: +# loaderHttpUrl: +# loaderInterval: +# loaderType: http | file + +# -- runtime configuration of the Sparrow +# see: https://github.com/caas-team/sparrow#runtime +runtimeConfig: + health: + enabled: true + targets: + - "https://www.example.com/" + - "https://www.google.com/" + healthEndpoint: false + latency: + enabled: true + interval: 1 + timeout: 3 + retry: + count: 3 + delay: 1 + targets: + - https://example.com/ + - https://google.com/ diff --git a/docs/img/sparrow.png b/docs/img/sparrow.png new file mode 100644 index 0000000000000000000000000000000000000000..7e720bbf6723d9d3f3fa5c3a696fb7c5e500b8c1 GIT binary patch literal 101088 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

Hd!wxNKGSaLa4B^P3t>4F1fRRO1gQC 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 HM*jZ;<;=S& literal 0 HcmV?d00001