/
loading.go
153 lines (131 loc) · 5.33 KB
/
loading.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
package config
import (
"crypto/rsa"
"errors"
"flag"
aulogging "github.com/StephanHCB/go-autumn-logging"
"github.com/eurofurence/reg-attendee-service/internal/repository/system"
"gopkg.in/yaml.v2"
"net/url"
"os"
"sort"
"sync"
)
var (
configurationData *Application
configurationLock *sync.RWMutex
configurationFilename string
dbMigrate bool
ecsLogging bool
generateCount uint
parallel uint
baseUrl string
cookieDomain string
idToken string
accessToken string
parsedKeySet []*rsa.PublicKey
)
var (
ErrorConfigArgumentMissing = errors.New("configuration file argument missing. Please specify using -config argument. Aborting")
ErrorConfigFile = errors.New("failed to read or parse configuration file. Aborting")
)
func init() {
configurationData = &Application{Logging: LoggingConfig{Severity: "DEBUG"}}
configurationLock = &sync.RWMutex{}
flag.StringVar(&configurationFilename, "config", "", "config file path")
flag.BoolVar(&dbMigrate, "migrate-database", false, "migrate database on startup")
flag.BoolVar(&ecsLogging, "ecs-json-logging", false, "switch to structured json logging")
}
func AdditionalGeneratorCommandLineFlags() {
flag.UintVar(&generateCount, "generate-count", 0, "total number of fake registrations to generate (separate generator/loadtest binaries only)")
flag.UintVar(¶llel, "parallel", 0, "number of parallel goroutines to use (separate generator/loadtest binaries only)")
flag.StringVar(&baseUrl, "base-url", "", "base url of target attendee service (separate generator/loadtest binaries only)")
flag.StringVar(&cookieDomain, "cookie-domain", "", "domain for cookies (separate generator/loadtest binaries only)")
flag.StringVar(&idToken, "id-token", "", "id token to use (separate generator/loadtest binaries only)")
flag.StringVar(&accessToken, "access-token", "", "access token to use (separate generator/loadtest binaries only)")
}
// ParseCommandLineFlags is exposed separately so you can skip it for tests
func ParseCommandLineFlags() {
flag.Parse()
}
func parseAndOverwriteConfig(yamlFile []byte) error {
newConfigurationData := &Application{}
err := yaml.UnmarshalStrict(yamlFile, newConfigurationData)
if err != nil {
// cannot use logging package here as this would create a circular dependency (logging needs config)
aulogging.Logger.NoCtx().Error().Printf("failed to parse configuration file '%s': %v", configurationFilename, err)
return err
}
setConfigurationDefaults(newConfigurationData)
applyEnvVarOverrides(newConfigurationData)
errs := url.Values{}
validateServerConfiguration(errs, newConfigurationData.Server)
validateServiceConfiguration(errs, newConfigurationData.Service)
validateLoggingConfiguration(errs, newConfigurationData.Logging)
validateSecurityConfiguration(errs, newConfigurationData.Security)
validateDatabaseConfiguration(errs, newConfigurationData.Database)
validateFlagsConfiguration(errs, newConfigurationData.Choices.Flags)
validatePackagesConfiguration(errs, newConfigurationData.Choices.Packages)
validateOptionsConfiguration(errs, newConfigurationData.Choices.Options)
validateBirthdayConfiguration(errs, newConfigurationData.Birthday)
validateRegistrationStartTime(errs, newConfigurationData.GoLive, newConfigurationData.Security)
validateDuesConfiguration(errs, newConfigurationData.Dues)
if len(errs) != 0 {
var keys []string
for key := range errs {
keys = append(keys, key)
}
sort.Strings(keys)
for _, k := range keys {
key := k
val := errs[k]
aulogging.Logger.NoCtx().Error().Printf("configuration error: %s: %s", key, val[0])
}
return errors.New("configuration validation error")
}
configurationLock.Lock()
defer configurationLock.Unlock()
configurationData = newConfigurationData
return nil
}
func loadConfiguration() error {
yamlFile, err := os.ReadFile(configurationFilename)
if err != nil {
// cannot use logging package here as this would create a circular dependency (logging needs config)
aulogging.Logger.NoCtx().Error().Printf("failed to load configuration file '%s': %v", configurationFilename, err)
return err
}
err = parseAndOverwriteConfig(yamlFile)
return err
}
// LoadTestingConfigurationFromPathOrAbort is for tests to set a hardcoded yaml configuration
func LoadTestingConfigurationFromPathOrAbort(configFilenameForTests string) {
configurationFilename = configFilenameForTests
if err := StartupLoadConfiguration(); err != nil {
system.Exit(1)
}
}
// EnableTestingMigrateDatabase is for tests
func EnableTestingMigrateDatabase() {
dbMigrate = true
}
func StartupLoadConfiguration() error {
aulogging.Logger.NoCtx().Info().Print("Reading configuration...")
if configurationFilename == "" {
// cannot use logging package here as this would create a circular dependency (logging needs config)
aulogging.Logger.NoCtx().Error().Print("Configuration file argument missing. Please specify using -config argument. Aborting.")
return ErrorConfigArgumentMissing
}
err := loadConfiguration()
if err != nil {
// cannot use logging package here as this would create a circular dependency (logging needs config)
aulogging.Logger.NoCtx().Error().Print("Error reading or parsing configuration file. Aborting.")
return ErrorConfigFile
}
return nil
}
func Configuration() *Application {
configurationLock.RLock()
defer configurationLock.RUnlock()
return configurationData
}