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

Refactor: Config and CLI #63

Merged
merged 31 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9968dbe
refactor: simplify config and flag parsing
niklastreml Jan 9, 2024
55e27a0
fix: parsing for StringP and Int flags
niklastreml Jan 9, 2024
2a23548
fix: some values not being parsed from config correctly due to access…
niklastreml Jan 9, 2024
af17771
fix: compile error in unit tests
niklastreml Jan 9, 2024
cdf97b7
fix: add yaml tags to target manager config
niklastreml Jan 9, 2024
3f462b8
refactor: remove setters from config
niklastreml Jan 9, 2024
c7e3b99
refactor: update helm values
niklastreml Jan 10, 2024
b93db3b
fix: remove dead import
niklastreml Jan 10, 2024
a719378
fix: handle viper errors
niklastreml Jan 10, 2024
877ea28
fix: remove dead import
niklastreml Jan 10, 2024
04ea1d7
fix: use t.log
niklastreml Jan 11, 2024
f4dde72
refactor: log retryCount
niklastreml Jan 11, 2024
9993b15
feat: yaml struct tags
niklastreml Jan 11, 2024
6caacb4
docs: config example
niklastreml Jan 11, 2024
4e51ed4
chore: rename CLI
niklastreml Jan 11, 2024
9f7a574
chore: remove BindFN
niklastreml Jan 11, 2024
e290ea0
docs: flag package
niklastreml Jan 11, 2024
b684ae0
docs: document config file
niklastreml Jan 11, 2024
052bdb6
fix: incorrect cli param in end2end tests
niklastreml Jan 11, 2024
d647bc4
Merge branch 'main' into refactor/config-and-cli
niklastreml Jan 11, 2024
1f1f3e1
docs: remove bearer
niklastreml Jan 11, 2024
64d47fc
refactor: remove unnecessary yaml tags
niklastreml Jan 11, 2024
f63942b
chore: removed dedicated targetManager config
puffitos Jan 11, 2024
3586e75
chore: bump helm-docs v1.12.0
lvlcn-t Jan 11, 2024
69e98be
chore: fixes after review:
puffitos Jan 11, 2024
938e8bd
chore: removed footer from docs
puffitos Jan 11, 2024
9db255e
Merge pull request #68 from caas-team/refactor/remove-tm-config
puffitos Jan 12, 2024
af43c9a
fix: add duration flags
lvlcn-t Jan 12, 2024
82fc716
feat: embed flag struct into flag type structs
lvlcn-t Jan 12, 2024
f04453b
Merge branch 'main' into refactor/config-and-cli
niklastreml Jan 12, 2024
ad83c5f
fix: bad merge from main
niklastreml Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ gen

# Temporary directory
.tmp/*
config.yaml
85 changes: 79 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,79 @@ Priority of configuration (high to low):
3. Defined configuration file
4. Default configuration file

Every value in the config file can be set through environment variables.

You can set a token for the http loader:

```bash
export SPARROW_LOADER_HTTP_TOKEN="Bearer xxxxxx"
```

Or for any other config attribute:
```bash
export SPARROW_ANY_OTHER_OPTION="Some value"
```

Just write out the path to the attribute, delimited by `_`.


Example configuration:
```yaml
# DNS sparrow is exposed on
name: sparrow.example.com
# Selects and configures a loader for continuosly fetching the configuration at runtime
loader:
# defines which loader to use. Options: "file | http"
type: http
# the interval in which sparrow tries to fetch a new configuration
interval: 30s
# config specific to the http loader
http:
# The url where the config is located
url: https://myconfig.example.com/config.yaml
# This token is passed in the Authorization header, when refreshing the config
token: Bearer xxxxxxx
niklastreml marked this conversation as resolved.
Show resolved Hide resolved
# A timeout for the config refresh
timeout: 30s
retry:
# How long to wait in between retries
delay: 10s
# How many times to retry
count: 3

# config specific to the file loader
# The file loader is not intended for production use and does
# not refresh the config after reading it the first time
file:
# where to read the runtime config from
path: ./config.yaml

# Configures the api
api:
# Which address to expose sparrows rest api on
address: :8080

# Configures the targetmanager
targetmanager:
# time between checking for new targets
checkInterval: 1m
# how often the instance should register itself as a global target
registrationInterval: 1m
# the amount of time a target can be
# unhealthy before it is removed from the global target list
unhealthyThreshold: 3m
# Configuration options for the gitlab target manager
gitlab:
# The url of your gitlab host
baseUrl: https://gitlab.com
# Your gitlab api token
# you can also set this value through the
# SPARROW_TARGETMANAGER_GITLAB_TOKEN environment variable
token: "glpat-xxxxxxxx"
# the id of your gitlab project. This is where sparrow will register itself
# and grab the list of other sparrows from
projectId: 18923
```
#### Loader

The loader component of the `sparrow` will load the [Runtime](#runtime) configuration dynamically.
Expand Down Expand Up @@ -232,11 +305,11 @@ Available configuration options:

- `checks`
- `latency`
- `interval` (integer): Interval in seconds to perform the latency check.
- `timeout` (integer): Timeout in seconds for the latency check.
- `interval` (duration): Interval to perform the latency check.
- `timeout` (duration): Timeout 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.
- `delay` (furation): Delay between retries for the latency check.
lvlcn-t marked this conversation as resolved.
Show resolved Hide resolved
- `targets` (list of strings): List of targets to send latency probe. Needs to be a valid url. Can be
another `sparrow` instance. Automatically used when the target manager is enabled otherwise
use latency endpoint, e.g. `https://sparrow-dns.telekom.de/checks/latency`.
Expand All @@ -247,11 +320,11 @@ Available configuration options:
```yaml
checks:
latency:
interval: 1
timeout: 3
interval: 1s
timeout: 3s
retry:
count: 3
delay: 1
delay: 1s
targets:
- https://example.com/
- https://google.com/
Expand Down
2 changes: 0 additions & 2 deletions chart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,3 @@ A Helm chart to install Sparrow
| targetManagerConfig | object | `{}` | target manager configuration of the Sparrow (part of the startup) |
| tolerations | list | `[]` | |

----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.11.3](https://github.com/norwoodj/helm-docs/releases/v1.11.3)
25 changes: 15 additions & 10 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,21 @@ extraArgs:
# -- 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
# sparrowName: the-sparrow.com
# api:
# address:
# loader:
# type: http | file
# interval:
# http:
# url:
# token:
# timeout:
# retryCount:
# retryDelay:
# file:
# path: /runconfig/checks.yaml
# name: the-sparrow.com



# -- target manager configuration of the Sparrow (part of the startup)
Expand Down
80 changes: 80 additions & 0 deletions cmd/flag.go
niklastreml marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package cmd

import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

type Flag struct {
Config string
Cli string
}

type StringFlag struct {
f *Flag
}

type IntFlag struct {
f *Flag
}

type StringPFlag struct {
f *Flag
sh string
}

// Bind registers the flag with the command and binds it to the config
func (f *StringFlag) Bind(cmd *cobra.Command, value, usage string) {
cmd.PersistentFlags().String(f.f.Cli, value, usage)
if err := viper.BindPFlag(f.f.Config, cmd.PersistentFlags().Lookup(f.f.Cli)); err != nil {
panic(err)
}
}

func (f *Flag) String() *StringFlag {
return &StringFlag{
f: f,
}
}

// Bind registers the flag with the command and binds it to the config
func (f *IntFlag) Bind(cmd *cobra.Command, value int, usage string) {
cmd.PersistentFlags().Int(f.f.Cli, value, usage)
if err := viper.BindPFlag(f.f.Config, cmd.PersistentFlags().Lookup(f.f.Cli)); err != nil {
panic(err)
}
}

func (f *Flag) Int() *IntFlag {
return &IntFlag{
f: f,
}
}

// Bind registers the flag with the command and binds it to the config
func (f *StringPFlag) Bind(cmd *cobra.Command, value, usage string) {
cmd.PersistentFlags().StringP(f.f.Cli, f.sh, value, usage)
if err := viper.BindPFlag(f.f.Config, cmd.PersistentFlags().Lookup(f.f.Cli)); err != nil {
panic(err)
}
}

func (f *Flag) StringP(shorthand string) *StringPFlag {
return &StringPFlag{
f: f,
sh: shorthand,
}
}

// NewFlag returns a flag builder
// It serves as a wrapper around cobra and viper, that allows creating and binding typed cli flags to config values
//
// Example:
//
// NewFlag("config", "c").String().Bind(cmd, "config.yaml", "config file")
func NewFlag(config, cli string) *Flag {
return &Flag{
Config: config,
Cli: cli,
}
}
4 changes: 4 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package cmd
import (
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -75,6 +76,9 @@ func initConfig(cfgFile string) {
viper.SetConfigName(".sparrow")
}

viper.SetEnvPrefix("sparrow")
dotreplacer := strings.NewReplacer(".", "_")
viper.EnvKeyReplacer(dotreplacer)
viper.AutomaticEnv()

if err := viper.ReadInConfig(); err == nil {
Expand Down
86 changes: 26 additions & 60 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ package cmd

import (
"context"
"os"
"fmt"

"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -39,88 +39,54 @@ const (

// NewCmdRun creates a new run command
func NewCmdRun() *cobra.Command {
flagMapping := config.RunFlagsNameMapping{
ApiAddress: "apiAddress",
SparrowName: "sparrowName",
LoaderType: "loaderType",
LoaderInterval: "loaderInterval",
LoaderHttpUrl: "loaderHttpUrl",
LoaderHttpToken: "loaderHttpToken",
LoaderHttpTimeout: "loaderHttpTimeout",
LoaderHttpRetryCount: "loaderHttpRetryCount",
LoaderHttpRetryDelay: "loaderHttpRetryDelay",
LoaderFilePath: "loaderFilePath",
TargetManagerConfig: "tmconfig",
}

cmd := &cobra.Command{
Use: "run",
Short: "Run sparrow",
Long: `Sparrow will be started with the provided configuration`,
Run: run(&flagMapping),
RunE: run(),
}

cmd.PersistentFlags().String(flagMapping.ApiAddress, ":8080", "api: The address the server is listening on")
cmd.PersistentFlags().String(flagMapping.SparrowName, "", "The DNS name of the sparrow")
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")
cmd.PersistentFlags().Int(flagMapping.LoaderInterval, defaultLoaderInterval, "defines the interval the loader reloads the configuration in seconds")
cmd.PersistentFlags().String(flagMapping.LoaderHttpUrl, "", "http loader: The url where to get the remote configuration")
cmd.PersistentFlags().String(flagMapping.LoaderHttpToken, "", "http loader: Bearer token to authenticate the http endpoint")
cmd.PersistentFlags().Int(flagMapping.LoaderHttpTimeout, defaultLoaderHttpTimeout, "http loader: The timeout for the http request in seconds")
cmd.PersistentFlags().Int(flagMapping.LoaderHttpRetryCount, defaultHttpRetryCount, "http loader: Amount of retries trying to load the configuration")
cmd.PersistentFlags().Int(flagMapping.LoaderHttpRetryDelay, defaultHttpRetryDelay, "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")
cmd.PersistentFlags().String(flagMapping.TargetManagerConfig, "", "target manager: The path to the file to read the target manager config from")

_ = viper.BindPFlag(flagMapping.ApiAddress, cmd.PersistentFlags().Lookup(flagMapping.ApiAddress))
_ = viper.BindPFlag(flagMapping.SparrowName, cmd.PersistentFlags().Lookup(flagMapping.SparrowName))
_ = viper.BindPFlag(flagMapping.LoaderType, cmd.PersistentFlags().Lookup(flagMapping.LoaderType))
_ = viper.BindPFlag(flagMapping.LoaderInterval, cmd.PersistentFlags().Lookup(flagMapping.LoaderInterval))
_ = viper.BindPFlag(flagMapping.LoaderHttpUrl, cmd.PersistentFlags().Lookup(flagMapping.LoaderHttpUrl))
_ = viper.BindPFlag(flagMapping.LoaderHttpToken, cmd.PersistentFlags().Lookup(flagMapping.LoaderHttpToken))
_ = viper.BindPFlag(flagMapping.LoaderHttpTimeout, cmd.PersistentFlags().Lookup(flagMapping.LoaderHttpTimeout))
_ = viper.BindPFlag(flagMapping.LoaderHttpRetryCount, cmd.PersistentFlags().Lookup(flagMapping.LoaderHttpRetryCount))
_ = viper.BindPFlag(flagMapping.LoaderHttpRetryDelay, cmd.PersistentFlags().Lookup(flagMapping.LoaderHttpRetryDelay))
_ = viper.BindPFlag(flagMapping.LoaderFilePath, cmd.PersistentFlags().Lookup(flagMapping.LoaderFilePath))
_ = viper.BindPFlag(flagMapping.TargetManagerConfig, cmd.PersistentFlags().Lookup(flagMapping.TargetManagerConfig))
NewFlag("api.address", "apiAddress").String().Bind(cmd, ":8080", "api: The address the server is listening on")
NewFlag("name", "sparrowName").String().Bind(cmd, "", "The DNS name of the sparrow")
NewFlag("loader.type", "loaderType").StringP("l").Bind(cmd, "http", "Defines the loader type that will load the checks configuration during the runtime. The fallback is the fileLoader")
NewFlag("loader.interval", "loaderInterval").Int().Bind(cmd, defaultLoaderInterval, "defines the interval the loader reloads the configuration in seconds")
NewFlag("loader.http.url", "loaderHttpUrl").String().Bind(cmd, "", "http loader: The url where to get the remote configuration")
NewFlag("loader.http.token", "loaderHttpToken").String().Bind(cmd, "", "http loader: Bearer token to authenticate the http endpoint")
NewFlag("loader.http.timeout", "loaderHttpTimeout").Int().Bind(cmd, defaultLoaderHttpTimeout, "http loader: The timeout for the http request in seconds")
NewFlag("loader.http.retry.count", "loaderHttpRetryCount").Int().Bind(cmd, defaultHttpRetryCount, "http loader: Amount of retries trying to load the configuration")
NewFlag("loader.http.retry.delay", "loaderHttpRetryDelay").Int().Bind(cmd, defaultHttpRetryDelay, "http loader: The initial delay between retries in seconds")
niklastreml marked this conversation as resolved.
Show resolved Hide resolved
NewFlag("loader.file.path", "loaderFilePath").String().Bind(cmd, "config.yaml", "file loader: The path to the file to read the runtime config from")
NewFlag("targetmanager.config", "tm-config").String().Bind(cmd, "", "target manager: The path to the file to read the target manager config from")

return cmd
}

// run is the entry point to start the sparrow
func run(fm *config.RunFlagsNameMapping) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
func run() func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
log := logger.NewLogger()
ctx := logger.IntoContext(context.Background(), log)

cfg := config.NewConfig()
cfg.SetTargetManagerConfig(config.NewTargetManagerConfig(viper.GetString(fm.TargetManagerConfig)))

cfg.SetApiAddress(viper.GetString(fm.ApiAddress))
cfg.SetSparrowName(viper.GetString(fm.SparrowName))

cfg.SetLoaderType(viper.GetString(fm.LoaderType))
cfg.SetLoaderInterval(viper.GetInt(fm.LoaderInterval))
cfg.SetLoaderHttpUrl(viper.GetString(fm.LoaderHttpUrl))
cfg.SetLoaderHttpToken(viper.GetString(fm.LoaderHttpToken))
cfg.SetLoaderHttpTimeout(viper.GetInt(fm.LoaderHttpTimeout))
cfg.SetLoaderHttpRetryCount(viper.GetInt(fm.LoaderHttpRetryCount))
cfg.SetLoaderHttpRetryDelay(viper.GetInt(fm.LoaderHttpRetryDelay))
cfg.SetLoaderFilePath(viper.GetString(fm.LoaderFilePath))
err := viper.Unmarshal(cfg)
if err != nil {
return fmt.Errorf("failed to parse config: %w", err)
}

if err := cfg.Validate(ctx, fm); err != nil {
log.Error("Error while validating the config", "error", err)
panic(err)
if err = cfg.Validate(ctx); err != nil {
return fmt.Errorf("error while validating the config: %w", err)
}

s := sparrow.New(cfg)
log.Info("Running sparrow")
if err := s.Run(ctx); err != nil {
log.Error("Error while running sparrow", "error", err)
if err = s.Run(ctx); err != nil {
err = fmt.Errorf("error while running sparrow: %w", err)
// by this time all shutdown routines should have been called
// so we can exit here
os.Exit(1)
return err
}

return nil
}
}
4 changes: 2 additions & 2 deletions docs/sparrow_run.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ sparrow run [flags]
--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")
-l, --loaderType string Defines the loader type that will load the checks configuration during the runtime. The fallback is the fileLoader (default "http")
--sparrowName string The DNS name of the sparrow
--tmconfig string target manager: The path to the file to read the target manager config from
--tm-config string target manager: The path to the file to read the target manager config from
```

### Options inherited from parent commands
Expand Down
4 changes: 2 additions & 2 deletions internal/helper/retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ import (
)

type RetryConfig struct {
Count int
Delay time.Duration
Count int `json:"count" yaml:"count"`
Delay time.Duration `json:"delay" yaml:"delay"`
}

// Effector will be the function that is called by the Retry function
Expand Down
Loading
Loading