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

Loki: Common Config #4347

Merged
merged 11 commits into from
Oct 1, 2021
2 changes: 1 addition & 1 deletion clients/cmd/promtail/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (c *Config) Clone() flagext.Registerer {
func main() {
// Load config, merging config file and CLI flags
var config Config
if err := cfg.Parse(&config); err != nil {
if err := cfg.DefaultUnmarshal(&config); err != nil {
fmt.Println("Unable to parse config:", err)
os.Exit(1)
}
Expand Down
45 changes: 7 additions & 38 deletions cmd/loki/main.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package main

import (
"flag"
"fmt"
"os"
"reflect"

"github.com/go-kit/kit/log/level"
"github.com/grafana/dskit/flagext"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/version"
"github.com/weaveworks/common/logging"
"github.com/weaveworks/common/tracing"

"github.com/grafana/loki/pkg/cfg"
"github.com/grafana/loki/pkg/loki"
logutil "github.com/grafana/loki/pkg/util"
_ "github.com/grafana/loki/pkg/util/build"
"github.com/grafana/loki/pkg/util/cfg"
"github.com/grafana/loki/pkg/validation"

util_log "github.com/cortexproject/cortex/pkg/util/log"
Expand All @@ -26,43 +24,14 @@ func init() {
prometheus.MustRegister(version.NewCollector("loki"))
}

type Config struct {
loki.Config `yaml:",inline"`
printVersion bool
verifyConfig bool
printConfig bool
logConfig bool
configFile string
configExpandEnv bool
}

func (c *Config) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&c.printVersion, "version", false, "Print this builds version information")
f.BoolVar(&c.verifyConfig, "verify-config", false, "Verify config file and exits")
f.BoolVar(&c.printConfig, "print-config-stderr", false, "Dump the entire Loki config object to stderr")
f.BoolVar(&c.logConfig, "log-config-reverse-order", false, "Dump the entire Loki config object at Info log "+
"level with the order reversed, reversing the order makes viewing the entries easier in Grafana.")
f.StringVar(&c.configFile, "config.file", "", "yaml file to load")
f.BoolVar(&c.configExpandEnv, "config.expand-env", false, "Expands ${var} in config according to the values of the environment variables.")
c.Config.RegisterFlags(f)
}

// Clone takes advantage of pass-by-value semantics to return a distinct *Config.
// This is primarily used to parse a different flag set without mutating the original *Config.
func (c *Config) Clone() flagext.Registerer {
return func(c Config) *Config {
return &c
}(*c)
}

func main() {
var config Config
var config cfg.ConfigWrapper

if err := cfg.Parse(&config); err != nil {
if err := cfg.Unmarshal(&config); err != nil {
fmt.Fprintf(os.Stderr, "failed parsing config: %v\n", err)
os.Exit(1)
}
if config.printVersion {
if config.PrintVersion {
fmt.Println(version.Print("loki"))
os.Exit(0)
}
Expand All @@ -87,19 +56,19 @@ func main() {
os.Exit(1)
}

if config.verifyConfig {
if config.VerifyConfig {
level.Info(util_log.Logger).Log("msg", "config is valid")
os.Exit(0)
}

if config.printConfig {
if config.PrintConfig {
err := logutil.PrintConfig(os.Stderr, &config)
if err != nil {
level.Error(util_log.Logger).Log("msg", "failed to print config to stderr", "err", err.Error())
}
}

if config.logConfig {
if config.LogConfig {
err := logutil.LogConfig(&config)
if err != nil {
level.Error(util_log.Logger).Log("msg", "failed to log config object", "err", err.Error())
Expand Down
2 changes: 1 addition & 1 deletion cmd/migrate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func main() {
flag.Parse()

// Create a set of defaults
if err := cfg.Unmarshal(&defaultsConfig, cfg.Defaults()); err != nil {
if err := cfg.Unmarshal(&defaultsConfig, cfg.Defaults(flag.CommandLine)); err != nil {
log.Println("Failed parsing defaults config:", err)
os.Exit(1)
}
Expand Down
85 changes: 85 additions & 0 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cfg

import (
"flag"
"os"

"github.com/grafana/dskit/flagext"
"github.com/pkg/errors"

"github.com/grafana/loki/pkg/loki"
"github.com/grafana/loki/pkg/util/cfg"
)

// ConfigWrapper is a struct containing the Loki config along with other values that can be set on the command line
// for interacting with the config file or the application directly.
type ConfigWrapper struct {
loki.Config `yaml:",inline"`
PrintVersion bool
VerifyConfig bool
PrintConfig bool
LogConfig bool
ConfigFile string
ConfigExpandEnv bool
}

func (c *ConfigWrapper) RegisterFlags(f *flag.FlagSet) {
f.BoolVar(&c.PrintVersion, "version", false, "Print this builds version information")
f.BoolVar(&c.VerifyConfig, "verify-config", false, "Verify config file and exits")
f.BoolVar(&c.PrintConfig, "print-config-stderr", false, "Dump the entire Loki config object to stderr")
f.BoolVar(&c.LogConfig, "log-config-reverse-order", false, "Dump the entire Loki config object at Info log "+
"level with the order reversed, reversing the order makes viewing the entries easier in Grafana.")
f.StringVar(&c.ConfigFile, "config.file", "", "yaml file to load")
f.BoolVar(&c.ConfigExpandEnv, "config.expand-env", false, "Expands ${var} in config according to the values of the environment variables.")
c.Config.RegisterFlags(f)
}

// Clone takes advantage of pass-by-value semantics to return a distinct *Config.
// This is primarily used to parse a different flag set without mutating the original *Config.
func (c *ConfigWrapper) Clone() flagext.Registerer {
return func(c ConfigWrapper) *ConfigWrapper {
return &c
}(*c)
}

// Unmarshal handles populating Loki's config based on the following precedence:
// 1. Defaults provided by the `RegisterFlags` interface
// 2. Sections populated by the `common` config section of the Loki config
// 3. Any config options specified directly in the loki config file
// 4. Any config options specified on the command line.
func Unmarshal(dst cfg.Cloneable) error {
return cfg.Unmarshal(dst,
// First populate the config with defaults including flags from the command line
cfg.Defaults(flag.CommandLine),
// Next populate the config from the config file, we do this to populate the `common`
// section of the config file by taking advantage of the code in YAMLFlag which will load
// and process the config file.
cfg.YAMLFlag(os.Args[1:], "config.file"),
// Apply our logic to use values from the common section to set values throughout the Loki config.
CommonConfig(),
// Load configs from the config file a second time, this will supersede anything set by the common
// config with values specified in the config file.
cfg.YAMLFlag(os.Args[1:], "config.file"),
// Load the flags again, this will supersede anything set from config file with flags from the command line.
cfg.Flags(),
)
}

// CommonConfig applies all rules for setting Loki config values from the common section of the Loki config file.
// This entire method's purpose is to simplify Loki's config in an opinionated way so that Loki can be run
// with the minimal amount of config options for most use cases. It also aims to reduce redundancy where
// some values are set multiple times through the Loki config.
func CommonConfig() cfg.Source {
return func(dst cfg.Cloneable) error {
r, ok := dst.(*ConfigWrapper)
if !ok {
return errors.New("dst is not a Loki ConfigWrapper")
}

// Apply all our custom logic here to set values in the Loki config from values in the common config
// FIXME this is just an example showing how we can use values from the common section to set values on the Loki config object
r.StorageConfig.BoltDBShipperConfig.SharedStoreType = r.Common.Store
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we can do anything needed to map common configs to the Loki configs


return nil
}
}
5 changes: 5 additions & 0 deletions pkg/cfg/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package common

type Config struct {
Store string // This is just an example, but here we should define all the 'common' config values used to set other Loki values.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we can define the common config struct

}
2 changes: 2 additions & 0 deletions pkg/loki/loki.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/weaveworks/common/signals"
"google.golang.org/grpc/health/grpc_health_v1"

"github.com/grafana/loki/pkg/cfg/common"
"github.com/grafana/loki/pkg/distributor"
"github.com/grafana/loki/pkg/ingester"
"github.com/grafana/loki/pkg/ingester/client"
Expand All @@ -52,6 +53,7 @@ type Config struct {
AuthEnabled bool `yaml:"auth_enabled,omitempty"`
HTTPPrefix string `yaml:"http_prefix"`

Common common.Config `yaml:"common,omitempty"`
Server server.Config `yaml:"server,omitempty"`
Distributor distributor.Config `yaml:"distributor,omitempty"`
Querier querier.Config `yaml:"querier,omitempty"`
Expand Down
24 changes: 4 additions & 20 deletions pkg/util/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,11 @@ func Unmarshal(dst Cloneable, sources ...Source) error {
return nil
}

// Parse is a higher level wrapper for Unmarshal that automatically parses flags and a .yaml file
func Parse(dst Cloneable) error {
return dParse(dst,
dDefaults(flag.CommandLine),
// DefaultUnmarshal is a higher level wrapper for Unmarshal that automatically parses flags and a .yaml file
func DefaultUnmarshal(dst Cloneable) error {
return Unmarshal(dst,
Defaults(flag.CommandLine),
YAMLFlag(os.Args[1:], "config.file"),
Flags(),
)
}

// dParse is the same as Parse, but with dependency injection for testing
func dParse(dst Cloneable, defaults, yaml, flags Source) error {
// check dst is a pointer
v := reflect.ValueOf(dst)
if v.Kind() != reflect.Ptr {
return ErrNotPointer
}

// unmarshal config
return Unmarshal(dst,
defaults,
yaml,
flags,
)
}
8 changes: 4 additions & 4 deletions pkg/util/cfg/cfg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ tls:
flagSource := dFlags(fs, []string{"-verbose", "-server.port=21"})

data := Data{}
err := dParse(&data,
dDefaults(fs),
err := Unmarshal(&data,
Defaults(fs),
yamlSource,
flagSource,
)
Expand Down Expand Up @@ -55,8 +55,8 @@ tls:
flagSource := dFlags(fs, []string{"-verbose", "-server.port=21"})

data := Data{}
err := dParse(&data,
dDefaults(fs),
err := Unmarshal(&data,
Defaults(fs),
yamlSource,
flagSource,
)
Expand Down
10 changes: 2 additions & 8 deletions pkg/util/cfg/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,8 @@ import (
"github.com/pkg/errors"
)

// Defaults registers flags to the command line using dst as the
// flagext.Registerer
func Defaults() Source {
return dDefaults(flag.CommandLine)
}

// dDefaults registers flags to the flagSet using dst as the flagext.Registerer
func dDefaults(fs *flag.FlagSet) Source {
// Defaults registers flags to the flagSet using dst as the flagext.Registerer
func Defaults(fs *flag.FlagSet) Source {
return func(dst Cloneable) error {
r, ok := dst.(flagext.Registerer)
if !ok {
Expand Down
4 changes: 2 additions & 2 deletions pkg/util/cfg/flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestDefaults(t *testing.T) {
fs := flag.NewFlagSet(t.Name(), flag.PanicOnError)

err := Unmarshal(&data,
dDefaults(fs),
Defaults(fs),
)

require.NoError(t, err)
Expand All @@ -39,7 +39,7 @@ func TestFlags(t *testing.T) {
data := Data{}
fs := flag.NewFlagSet(t.Name(), flag.PanicOnError)
err := Unmarshal(&data,
dDefaults(fs),
Defaults(fs),
dFlags(fs, []string{"-server.timeout=10h", "-verbose"}),
)
require.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/util/cfg/precedence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestYAMLOverDefaults(t *testing.T) {
data := Data{}
fs := flag.NewFlagSet(t.Name(), flag.PanicOnError)
err := Unmarshal(&data,
dDefaults(fs),
Defaults(fs),
dYAML([]byte(y)),
)

Expand All @@ -50,7 +50,7 @@ func TestFlagOverYAML(t *testing.T) {
fs := flag.NewFlagSet(t.Name(), flag.PanicOnError)

err := Unmarshal(&data,
dDefaults(fs),
Defaults(fs),
dYAML([]byte(y)),
dFlags(fs, []string{"-verbose=false", "-tls.cert=CLI"}),
)
Expand Down