-
Notifications
You must be signed in to change notification settings - Fork 37
/
loader.go
93 lines (80 loc) · 2.95 KB
/
loader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package config
import (
"embed"
"fmt"
"os"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
)
// Any yaml files in the default config directory will be embedded into the binary
//
//go:embed defaults/*.yaml defaults/**/*.yaml
var _baseConfig embed.FS
const _defaultBaseConfig = "defaults/0-base-config.yaml"
// LoadTenConfig reads the base config file and applying additional files provided as well as any env variables,
// returns a TenConfig struct
func LoadTenConfig(files ...string) (*TenConfig, error) {
configFiles := []string{_defaultBaseConfig}
configFiles = append(configFiles, files...)
return load(configFiles)
}
// load reads and applies the config files and environment variables, returning a TenConfig struct
// This method is not publicly available as we want callers to always use the base config file to avoid gotchas.
func load(filePaths []string) (*TenConfig, error) {
// parse yaml file with viper
v := viper.New()
// Bind environment variables to config keys to override yaml files
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
v.AutomaticEnv()
for i, filePath := range filePaths {
var content []byte
var err error
// Check if the file exists in the embedded FS
if content, err = _baseConfig.ReadFile(filePath); err == nil {
// File found in embedded FS
v.SetConfigType("yaml")
if i == 0 { // only first file is read, the rest are 'merged'
fmt.Println("reading embedded config file: ", filePath)
err = v.ReadConfig(strings.NewReader(string(content)))
} else {
fmt.Println("merging embedded config file: ", filePath)
err = v.MergeConfig(strings.NewReader(string(content)))
}
} else {
// Otherwise, check if it exists as a file in the filesystem
_, err = os.Stat(filePath)
if os.IsNotExist(err) {
fmt.Println("Config file not found: ", filePath)
return nil, err
}
v.SetConfigFile(filePath)
if i == 0 { // only first file is read, the rest are 'merged'
fmt.Println("reading config file: ", filePath)
err = v.ReadInConfig()
} else {
fmt.Println("merging config file: ", filePath)
err = v.MergeInConfig()
}
}
if err != nil {
fmt.Println("Error reading config file: ", filePath)
return nil, err
}
}
// todo (@matt) for enclave processes apply signed configuration file **after** even the env variable overrides
var tenCfg TenConfig
err := v.Unmarshal(&tenCfg, viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(), // handle string -> time.Duration
mapstructure.StringToSliceHookFunc(","), // handle string -> []string
mapstructure.TextUnmarshallerHookFunc(), // handle all types that implement encoding.TextUnmarshaler
bigIntHookFunc(), // handle int values -> big.Int fields
)))
if err != nil {
fmt.Println("Error unmarshalling config: ", err)
return nil, err
}
fmt.Println("Successfully loaded Ten config.")
tenCfg.PrettyPrint()
return &tenCfg, nil
}