Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ require (
github.com/google/uuid v1.1.1 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/joho/godotenv v1.3.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/kelseyhightower/envconfig v1.4.0
Expand All @@ -26,7 +25,6 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/launchdarkly/eventsource v1.6.1 // indirect
github.com/launchdarkly/go-test-helpers v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.1.2
github.com/nats-io/nats-streaming-server v0.15.1 // indirect
github.com/nats-io/nats.go v1.8.1
github.com/nats-io/stan.go v0.5.0
Expand All @@ -39,14 +37,16 @@ require (
github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35
github.com/shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d
github.com/sirupsen/logrus v1.6.0
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.4-0.20190321000552-67fc4837d267
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.0
github.com/tidwall/pretty v1.0.1 // indirect
github.com/xdg/stringprep v1.0.0 // indirect
go.mongodb.org/mongo-driver v1.4.1
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.26.0
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ghodss/yaml.v1 v1.0.0 // indirect
Expand Down
205 changes: 205 additions & 0 deletions go.sum

Large diffs are not rendered by default.

48 changes: 21 additions & 27 deletions nconf/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,30 @@ type RootArgs struct {
ConfigFile string
}

type RootConfig struct {
Log LoggingConfig
BugSnag BugSnagConfig `mapstructure:",squash"`
Metrics metriks.Config `mapstructure:",squash"`
Tracing tracing.Config `mapstructure:",squash"`
FeatureFlag featureflag.Config `mapstructure:",squash"`
}

func (args *RootArgs) Setup(config interface{}, serviceName, version string) (logrus.FieldLogger, error) {
rootConfig := RootConfig{
Log: DefaultLoggingConfig,
// first load the logger and BugSnag config
rootConfig := &struct {
Log *LoggingConfig
BugSnag *BugSnagConfig
Metrics metriks.Config
Tracing tracing.Config
FeatureFlag featureflag.Config
}{}

loader := func(cfg interface{}) error {
return LoadFromEnv(args.Prefix, args.ConfigFile, cfg)
}
if !strings.HasSuffix(args.ConfigFile, ".env") {
loader = func(cfg interface{}) error {
return LoadFromFile(args.ConfigFile, cfg)
}
}

if err := args.load(&rootConfig); err != nil {
return nil, errors.Wrap(err, "Failed to load the root configuration")
if err := loader(rootConfig); err != nil {
return nil, errors.Wrap(err, "Failed to load the logging configuration")
}

log, err := ConfigureLogging(&rootConfig.Log)
log, err := ConfigureLogging(rootConfig.Log)
if err != nil {
return nil, errors.Wrap(err, "Failed to create the logger")
}
Expand All @@ -44,7 +50,7 @@ func (args *RootArgs) Setup(config interface{}, serviceName, version string) (lo
}
log = log.WithField("version", version)

if err := SetupBugSnag(&rootConfig.BugSnag, version); err != nil {
if err := SetupBugSnag(rootConfig.BugSnag, version); err != nil {
return nil, errors.Wrap(err, "Failed to configure bugsnag")
}

Expand All @@ -65,26 +71,14 @@ func (args *RootArgs) Setup(config interface{}, serviceName, version string) (lo

if config != nil {
// second load the config for this project
if err := args.load(config); err != nil {
if err := loader(config); err != nil {
return log, errors.Wrap(err, "Failed to load the config object")
}
log.Debug("Loaded configuration")
}
return log, nil
}

func (args *RootArgs) load(cfg interface{}) error {
loader := func(cfg interface{}) error {
return LoadFromEnv(args.Prefix, args.ConfigFile, cfg)
}
if !strings.HasSuffix(args.ConfigFile, ".env") {
loader = func(cfg interface{}) error {
return LoadFromFile(args.ConfigFile, cfg)
}
}
return loader(cfg)
}

func (args *RootArgs) MustSetup(config interface{}, serviceName, version string) logrus.FieldLogger {
logger, err := args.Setup(config, serviceName, version)
if err != nil {
Expand Down
43 changes: 0 additions & 43 deletions nconf/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package nconf

import (
"io/ioutil"
"os"
"testing"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -68,45 +67,3 @@ func TestArgsAddToCmd(t *testing.T) {
require.NoError(t, cmd.Execute())
assert.Equal(t, 1, called)
}

func TestArgsLoadFromYAML(t *testing.T) {
f, err := ioutil.TempFile("", "test-config-*.yaml")
require.NoError(t, err)
defer os.Remove(f.Name())

args := RootArgs{
ConfigFile: f.Name(),
}

t.Run("empty-file", func(t *testing.T) {
cfg := RootConfig{
Log: DefaultLoggingConfig,
}
require.NoError(t, args.load(&cfg))

assert.True(t, cfg.Log.QuoteEmptyFields)
assert.Equal(t, DefaultLoggingConfig, cfg.Log)
assert.Empty(t, cfg.BugSnag.APIKey)
})

_, err = f.WriteString(`
log:
log_level: debug
fields:
string: value
int: 4
`)
require.NoError(t, err)

t.Run("set log field", func(t *testing.T) {
cfg := RootConfig{Log: DefaultLoggingConfig}
require.NoError(t, args.load(&cfg))

// retains original value
assert.True(t, cfg.Log.QuoteEmptyFields)
assert.Equal(t, "debug", cfg.Log.Level)
require.Len(t, cfg.Log.Fields, 2)
assert.Equal(t, "value", cfg.Log.Fields["string"])
assert.Equal(t, 4, cfg.Log.Fields["int"])
})
}
54 changes: 17 additions & 37 deletions nconf/configuration.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,40 @@
package nconf

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
"github.com/mitchellh/mapstructure"
"gopkg.in/yaml.v3"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

// ErrUnknownConfigFormat indicates the extension of the config file is not supported as a config source
type ErrUnknownConfigFormat struct {
ext string
}

func (e *ErrUnknownConfigFormat) Error() string {
return fmt.Sprintf("Unknown config format: %s", e.ext)
}

// LoadFromFile will load the configuration from the specified file based on the file type
// There is only support for .json and .yml now
func LoadFromFile(configFile string, input interface{}) error {
if configFile == "" {
return nil
}

// read in all the bytes
data, err := ioutil.ReadFile(configFile)
if err != nil {
return err
switch {
case strings.HasSuffix(configFile, ".json"):
viper.SetConfigType("json")
case strings.HasSuffix(configFile, ".yaml"):
fallthrough
case strings.HasSuffix(configFile, ".yml"):
viper.SetConfigType("yaml")
}
viper.SetConfigFile(configFile)

config := make(map[string]interface{})

configExt := filepath.Ext(configFile)

switch configExt {
case ".json":
err = json.Unmarshal(data, &config)
case ".yaml", ".yml":
err = yaml.Unmarshal(data, &config)
default:
err = &ErrUnknownConfigFormat{configExt}
}
if err != nil {
return fmt.Errorf("failed to read config: %w", err)
if err := viper.ReadInConfig(); err != nil && !os.IsNotExist(err) {
_, ok := err.(viper.ConfigFileNotFoundError)
if !ok {
return errors.Wrap(err, "reading configuration from files")
}
}

if err := mapstructure.Decode(&config, input); err != nil {
return fmt.Errorf("failed to map data: %w", err)
}
return nil
return viper.Unmarshal(input)
}

func LoadFromEnv(prefix, filename string, face interface{}) error {
Expand Down
46 changes: 10 additions & 36 deletions nconf/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import (
)

type testConfig struct {
Hero string
Villian string
Matchups map[string]string
Cities []string

ShootingLocation string `mapstructure:"shooting_location" split_words:"true"`
Hero string `json:",omitempty"`
Villian string `json:",omitempty"`
Matchups map[string]string `json:",omitempty"`
Cities []string `json:",omitempty"`
}

func exampleConfig() testConfig {
Expand All @@ -29,8 +27,6 @@ func exampleConfig() testConfig {
"superman": "luther",
},
Cities: []string{"gotham", "central", "star"},

ShootingLocation: "LA",
}
}

Expand All @@ -48,7 +44,6 @@ func TestEnvLoadingNoFile(t *testing.T) {
os.Setenv("TEST_HERO", "batman")
os.Setenv("TEST_MATCHUPS", "batman:superman,superman:luther")
os.Setenv("TEST_CITIES", "gotham,central,star")
os.Setenv("TEST_SHOOTING_LOCATION", "LA")

var results testConfig
assert.NoError(t, LoadFromEnv("test", "", &results))
Expand All @@ -67,7 +62,6 @@ TEST_VILLIAN=joker
TEST_HERO=batman
TEST_MATCHUPS=batman:superman,superman:luther
TEST_CITIES=gotham,central,star
TEST_SHOOTING_LOCATION=LA
`
filename := writeTestFile(t, "env", []byte(data))
defer os.Remove(filename)
Expand All @@ -90,18 +84,9 @@ func TestFileLoadingNoFile(t *testing.T) {

func TestFileLoadJSON(t *testing.T) {
expected := exampleConfig()

input := `{
"hero": "batman",
"villian": "joker",
"matchups": {
"batman": "superman",
"superman": "luther"
},
"cities": ["gotham","central","star"],
"shooting_location": "LA"
}`
filename := writeTestFile(t, "json", []byte(input))
bytes, err := json.Marshal(&expected)
require.NoError(t, err)
filename := writeTestFile(t, "json", bytes)
defer os.Remove(filename)

var results testConfig
Expand All @@ -111,19 +96,9 @@ func TestFileLoadJSON(t *testing.T) {

func TestFileLoadYAML(t *testing.T) {
expected := exampleConfig()

input := `---
hero: batman
villian: joker
matchups:
batman: superman
superman: luther
cities:
- gotham
- central
- star
shooting_location: LA`
filename := writeTestFile(t, "yaml", []byte(input))
bytes, err := json.Marshal(&expected)
require.NoError(t, err)
filename := writeTestFile(t, "yaml", bytes)
defer os.Remove(filename)

var results testConfig
Expand Down Expand Up @@ -199,5 +174,4 @@ func validateConfig(t *testing.T, expected testConfig, results testConfig) {
for k, v := range expected.Matchups {
assert.Equal(t, v, results.Matchups[k])
}
assert.Equal(t, results.ShootingLocation, expected.ShootingLocation)
}
10 changes: 0 additions & 10 deletions nconf/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,9 @@ type LoggingConfig struct {
UseNewLogger bool `mapstructure:"use_new_logger",split_words:"true"`
}

var DefaultLoggingConfig = LoggingConfig{
Level: "info",
QuoteEmptyFields: true,
Fields: make(map[string]interface{}),
}

func ConfigureLogging(config *LoggingConfig) (*logrus.Entry, error) {
logger := logrus.New()

if config == nil {
config = &DefaultLoggingConfig
}

tsFormat := time.RFC3339Nano
if config.TSFormat != "" {
tsFormat = config.TSFormat
Expand Down