/
config.go
157 lines (137 loc) · 4.3 KB
/
config.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
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
156
157
package config
import (
"context"
"errors"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/joeshaw/envdecode"
"github.com/joho/godotenv"
)
type (
Environment string
LogLevel string
Storage string
)
const (
EnvLocal Environment = "local"
EnvTest Environment = "test"
EnvDev Environment = "dev"
EnvStaging Environment = "staging"
EnvLT Environment = "lt"
EnvQA Environment = "qa"
EnvProd Environment = "prod"
StorageLocal Storage = "local"
StorageDynamoDB Storage = "dynamodb"
LvlDbg LogLevel = "debug"
LvlInfo LogLevel = "info"
LvlWarn LogLevel = "warn"
LvlErr LogLevel = "error"
)
// SwitchEnvironment sets the environment variable used to dictate which environment the application is
// currently running in. This must be called prior to loading the configuration in order for it
// to take effect.
func SwitchEnvironment(env Environment) {
if err := os.Setenv("APP_ENVIRONMENT", string(env)); err != nil {
panic(err)
}
}
type (
// Config is the aggregation of all necessary configurations.
Config struct {
App
HTTP
Metrics
AWS
DynamoDB
}
// App defines the configs needed for the application itself.
App struct {
Name string `env:"APP_NAME,default=backfill"`
Environment Environment `env:"APP_ENVIRONMENT,default=local"`
LogLevel LogLevel `env:"LOG_LEVEL,default=info"`
Timeout time.Duration `env:"APP_TIMEOUT,default=20s"`
Storage Storage `env:"APP_STORAGE,default=local"`
}
// HTTP stores the configuration for the HTTP server.
HTTP struct {
Hostname string `env:"HTTP_HOSTNAME,default=0.0.0.0"`
Port uint16 `env:"HTTP_PORT,default=8000"`
ReadTimeout time.Duration `env:"HTTP_READ_TIMEOUT,default=5s"`
WriteTimeout time.Duration `env:"HTTP_WRITE_TIMEOUT,default=10s"`
IdleTimeout time.Duration `env:"HTTP_IDLE_TIMEOUT,default=2m"`
TLS struct {
Enabled bool `env:"HTTP_TLS_ENABLED,default=false"`
Certificate string `env:"HTTP_TLS_CERTIFICATE"`
Key string `env:"HTTP_TLS_KEY"`
}
}
// Metrics defines the configs needed for collecting metrics.
Metrics struct {
Enabled bool `env:"METRICS_ENABLED,default=false"`
Addr string `env:"METRICS_ADDRESS"`
BufLen int `env:"METRICS_BUFFER,default=5"`
}
// AWS defines the configs related to AWS services.
AWS struct {
AccessKeyID string `env:"AWS_ACCESS_KEY_ID"`
Secret string `env:"AWS_SECRET_ACCESS_KEY"`
Region string `env:"AWS_REGION"`
Endpoint string `env:"AWS_ENDPOINT"`
AWSCfg aws.Config
}
// DynamoDB defines the configs related specfically to the DynamoDB service.
DynamoDB struct {
ItemTable string `env:"DYNAMODB_ITEM_TABLE"`
}
)
// New loads the configuration based on the environment variables.
func New() (Config, error) {
var cfg Config
err := godotenv.Load()
// If a .env file exists but was unable to be loaded
if err != nil && !os.IsNotExist(err) {
return Config{}, err
}
err = envdecode.StrictDecode(&cfg)
if err != nil {
return Config{}, err
}
cfg.AWS.AWSCfg, err = loadAWSCfg(context.Background(), cfg)
if err != nil {
return Config{}, err
}
return cfg, nil
}
func (c *Config) Validate() error {
// Ensure the appropriate values are set based on the storage
switch c.App.Storage {
case StorageDynamoDB:
if c.AWS.AccessKeyID == "" || c.AWS.Secret == "" || c.AWS.Region == "" {
return errors.New("missing required AWS configuration")
} else if c.DynamoDB.ItemTable == "" {
return errors.New("missing required DynamoDB configuration")
}
default:
}
return nil
}
func loadAWSCfg(ctx context.Context, cfg Config) (aws.Config, error) {
customResolver := aws.EndpointResolverWithOptions(
aws.EndpointResolverWithOptionsFunc(
func(service, region string, options ...interface{}) (aws.Endpoint, error) {
if cfg.AWS.Endpoint != "" {
return aws.Endpoint{
URL: cfg.AWS.Endpoint,
SigningRegion: region,
Source: aws.EndpointSourceCustom,
}, nil
}
// returning EndpointNotFoundError will allow the service to fallback to its default resolution
return aws.Endpoint{}, &aws.EndpointNotFoundError{}
},
),
)
return awsconfig.LoadDefaultConfig(ctx, awsconfig.WithEndpointResolverWithOptions(customResolver))
}