From ab1703c2e898bd14a2665af7739b95b77dff0c9f Mon Sep 17 00:00:00 2001 From: "A.Unger" Date: Tue, 2 Nov 2021 14:19:14 +0100 Subject: [PATCH] refactor around config parsing --- .../pkg/command/accounts_example_config.yaml | 7 -- accounts/pkg/command/root.go | 21 +---- ocis-pkg/config/helpers.go | 80 +++++++++++++++++++ proxy/pkg/command/proxy_example_config.yaml | 6 -- proxy/pkg/command/root.go | 19 +---- 5 files changed, 82 insertions(+), 51 deletions(-) delete mode 100644 accounts/pkg/command/accounts_example_config.yaml create mode 100644 ocis-pkg/config/helpers.go delete mode 100644 proxy/pkg/command/proxy_example_config.yaml diff --git a/accounts/pkg/command/accounts_example_config.yaml b/accounts/pkg/command/accounts_example_config.yaml deleted file mode 100644 index bb3c6019da..0000000000 --- a/accounts/pkg/command/accounts_example_config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -log: - level: info - color: true - pretty: true -repo: - cs3: - provider_addr: "localhost:9214" diff --git a/accounts/pkg/command/root.go b/accounts/pkg/command/root.go index 325b589bd4..3109ae54b1 100644 --- a/accounts/pkg/command/root.go +++ b/accounts/pkg/command/root.go @@ -4,9 +4,6 @@ import ( "context" "os" - gofig "github.com/gookit/config/v2" - gooyaml "github.com/gookit/config/v2/yaml" - "github.com/owncloud/ocis/accounts/pkg/config" ociscfg "github.com/owncloud/ocis/ocis-pkg/config" "github.com/owncloud/ocis/ocis-pkg/log" @@ -76,23 +73,7 @@ func NewLogger(cfg *config.Config) log.Logger { // ParseConfig loads accounts configuration from known paths. func ParseConfig(c *cli.Context, cfg *config.Config) error { - // create a new config and load files and env values onto it since this needs to be thread-safe. - cnf := gofig.NewWithOptions("accounts", gofig.ParseEnv) - - // TODO(refs) add ENV + toml + json - cnf.AddDriver(gooyaml.Driver) - - // TODO(refs) load from expected locations with the expected name - err := cnf.LoadFiles("/Users/aunger/code/owncloud/ocis/accounts/pkg/command/accounts_example_config.yaml") - if err != nil { - // we have to swallow the error, since it is not mission critical a - // config file is missing and default values are loaded instead. - //return err - } - - err = cnf.BindStruct("", cfg) - - return nil + return ociscfg.BindSourcesToStructs("accounts", cfg) } // SutureService allows for the accounts command to be embedded and supervised by a suture supervisor tree. diff --git a/ocis-pkg/config/helpers.go b/ocis-pkg/config/helpers.go new file mode 100644 index 0000000000..175c0b6a1e --- /dev/null +++ b/ocis-pkg/config/helpers.go @@ -0,0 +1,80 @@ +package config + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + + gofig "github.com/gookit/config/v2" + gooyaml "github.com/gookit/config/v2/yaml" +) + +var ( + defaultLocations = []string{ + filepath.Join(os.Getenv("HOME"), "/.ocis/config"), + "/etc/ocis", + ".config/", + } + + // supportedExtensions is determined by gookit/config. For the purposes of the PR MVP we will focus on yaml, looking + // into extending it to all supported drivers. + supportedExtensions = []string{ + "yaml", + "yml", + } +) + +// DefaultConfigSources returns a slice with matched expected config files. It sugars coat several aspects of config file +// management by assuming there are 3 default locations a config file could be. +// It uses globbing to match a config file by name, and retrieve any supported extension supported by our drivers. +// It sanitizes the output depending on the list of drivers provided. +func DefaultConfigSources(filename string, drivers []string) []string { + var sources []string + + for i := range defaultLocations { + _fs := os.DirFS(defaultLocations[i]) + pattern := filename + ".*" + matched, _ := fs.Glob(_fs, pattern) + if len(matched) > 0 { + // prepend path to results + for j := 0; j < len(matched); j++ { + matched[j] = filepath.Join(defaultLocations[i], matched[j]) + } + } + sources = append(sources, matched...) + } + + return sanitizeExtensions(sources, drivers, func(a, b string) bool { + return strings.HasSuffix(filepath.Base(a), b) + }) +} + +// sanitizeExtensions removes elements from "set" which extensions are not in "ext". +func sanitizeExtensions(set []string, ext []string, f func(a, b string) bool) []string { + var r []string + for i := 0; i < len(set); i++ { + for j := 0; j < len(ext); j++ { + if f(filepath.Base(set[i]), ext[j]) { + r = append(r, set[i]) + } + } + } + return r +} + +// BindSourcesToStructs assigns any config value from a config file / env variable to struct `dst`. Its only purpose +// is to solely modify `dst`, not dealing with the config structs; and do so in a thread safe manner. +func BindSourcesToStructs(extension string, dst interface{}) error { + sources := DefaultConfigSources(extension, supportedExtensions) + cnf := gofig.NewWithOptions("proxy", gofig.ParseEnv) + cnf.AddDriver(gooyaml.Driver) + _ = cnf.LoadFiles(sources...) + + err := cnf.BindStruct("", &dst) + if err != nil { + return err + } + + return nil +} diff --git a/proxy/pkg/command/proxy_example_config.yaml b/proxy/pkg/command/proxy_example_config.yaml deleted file mode 100644 index 0c2bff022d..0000000000 --- a/proxy/pkg/command/proxy_example_config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -log: - level: info - color: true - pretty: true -http: - addr: "${PROXY_HTTP_ADDR|localhost:2222}" diff --git a/proxy/pkg/command/root.go b/proxy/pkg/command/root.go index 54506cac8e..3603180d0d 100644 --- a/proxy/pkg/command/root.go +++ b/proxy/pkg/command/root.go @@ -4,8 +4,6 @@ import ( "context" "os" - gofig "github.com/gookit/config/v2" - gooyaml "github.com/gookit/config/v2/yaml" ociscfg "github.com/owncloud/ocis/ocis-pkg/config" "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/ocis-pkg/version" @@ -69,22 +67,7 @@ func NewLogger(cfg *config.Config) log.Logger { // ParseConfig loads proxy configuration from known paths. func ParseConfig(c *cli.Context, cfg *config.Config) error { - // create a new config and load files and env values onto it since this needs to be thread-safe. - cnf := gofig.NewWithOptions("proxy", gofig.ParseEnv) - - // TODO(refs) add ENV + toml + json - cnf.AddDriver(gooyaml.Driver) - - // TODO(refs) load from expected locations with the expected name - err := cnf.LoadFiles("/Users/aunger/code/owncloud/ocis/proxy/pkg/command/proxy_example_config.yaml") - if err != nil { - return err - } - - // bind all keys to cfg, as we expect an entire proxy.[yaml, toml...] to define all keys and not only sub values. - err = cnf.BindStruct("", cfg) - - return nil + return ociscfg.BindSourcesToStructs("proxy", cfg) } // SutureService allows for the proxy command to be embedded and supervised by a suture supervisor tree.