Skip to content

Commit

Permalink
refactor the pkg/util/cfg package to simplify and make it accessable …
Browse files Browse the repository at this point in the history
…outside of this package.

create a pkg/cfg which handles building the Loki config, this is created separately from pkg/util (which is Apache 2 licensed) to maintain proper separations of AGPL and Apache 2 code
Update the main.go for several of the apps with the refactoring
  • Loading branch information
slim-bean committed Sep 17, 2021
1 parent ac0bb3c commit cb9c670
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 76 deletions.
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

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.
}
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

0 comments on commit cb9c670

Please sign in to comment.