-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathconfig.go
More file actions
155 lines (126 loc) · 4.18 KB
/
Copy pathconfig.go
File metadata and controls
155 lines (126 loc) · 4.18 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package config
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"github.com/tokenized/logger"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/kelseyhightower/envconfig"
"github.com/pkg/errors"
)
const (
// EnvProduction is the expected environment variable for production.
//
// An empty ENV var also indicates production.
EnvProduction = "prod"
// masked is the string to use when masking config values.
masked = "******"
// EnvParamName is the the environment variable to use when loading
// config from the AWS ParamStore.
EnvParamName = "PARAM_NAME"
// EnvConfigFile is the the environment variable to use when loading
// JSON config from a file.
EnvConfigFile = "CONFIG_FILE"
)
// LoadConfig is the common way to load config.
//
// It will attempt to load from param store, then config file, falling back
// to the environment.
//
// To load from the ParamStore, the PARAM_STORE env var should be set with the
// name of the item to load.
//
// To load from a JSON config file, the CONFIG_FILE env var should have the name
// of the file to load.
//
// Fallback option is to load config from environment variables.
func LoadConfig(ctx context.Context, cfg interface{}) error {
// check the PARAM_NAME env var
paramName := os.Getenv(EnvParamName)
if len(paramName) > 0 {
// we have a parameter name, try to load it
logger.Info(ctx, "Loading config from param store : %s", paramName)
return LoadParamStore(paramName, cfg)
}
// check the CONFIG_FILE env var
filename := os.Getenv(EnvConfigFile)
if len(filename) > 0 {
logger.Info(ctx, "Loading config from file : %s", filename)
return LoadFromFile(filename, cfg)
}
logger.Info(ctx, "Loading config from environment")
return LoadEnvironment(cfg)
}
// LoadFromFile loads a JSON config from a file.
func LoadFromFile(filename string, cfg interface{}) error {
// Load default values from environment definitions.
if err := LoadEnvironment(cfg); err != nil {
return errors.Wrap(err, "load environment defaults")
}
b, err := ioutil.ReadFile(filename)
if err != nil {
return errors.Wrap(err, "read file")
}
if err := json.Unmarshal(b, cfg); err != nil {
return errors.Wrap(err, "json")
}
return nil
}
// LoadEnvironment attempts to hydrate a struct with environment variables.
func LoadEnvironment(cfg interface{}) error {
return envconfig.Process("", cfg)
}
// LoadParamStore returns unmarshals the an AWS ParamStore value into a
// struct.
//
// This function is intended to work for any struct that is to be used for
// service configuration.
//
// A difference between ParamStore is that this function will not flatten the
// JSON config that it loads, which is expected to fit the struct it is being
// marshalled into.
//
// It is intended to eventually replace the usage of ParamStore, which
// requires a specific type.
func LoadParamStore(keyName string, cfg interface{}) error {
// Load default values from environment definitions.
if err := LoadEnvironment(cfg); err != nil {
return errors.Wrap(err, "load environment defaults")
}
b, err := fetchFromParamStore(keyName)
if err != nil {
return errors.Wrap(err, "fetch param store")
}
// Unmarshal param value
return json.Unmarshal(b, cfg)
}
// fetchFromParamStore loads the data from the AWS ParamStore.
func fetchFromParamStore(keyName string) ([]byte, error) {
// Locate param value
sess, err := session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String(os.Getenv("AWS_REGION"))},
SharedConfigState: session.SharedConfigEnable,
})
if err != nil {
return nil, errors.Wrap(err, "new session")
}
ssmsvc := ssm.New(sess, aws.NewConfig().WithRegion(os.Getenv("AWS_REGION")))
withDecryption := true
param, err := ssmsvc.GetParameter(&ssm.GetParameterInput{
Name: &keyName,
WithDecryption: &withDecryption,
})
if err != nil {
return nil, errors.Wrap(err, "get parameters")
}
// Unmarshal param value
b := []byte(*param.Parameter.Value)
return b, nil
}
// DumpSafe logs a "safe" version of the config, with sensitive values masked.
func DumpSafe(ctx context.Context, cfg interface{}) {
logger.Info(ctx, "Config : %+v", Mask(cfg))
}