Skip to content

Commit 68b9d8d

Browse files
authored
Merge pull request #59 from fabiante/feat/refactor-env-config
2 parents e255c90 + 4903911 commit 68b9d8d

File tree

11 files changed

+565
-63
lines changed

11 files changed

+565
-63
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# devtools
12
.idea
3+
4+
# config files
25
/.env
6+
/app.*
7+
8+
# other
39
*.sqlite

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
Application to manage and resolve [PURL](https://en.wikipedia.org/wiki/Persistent_uniform_resource_locator) links.
44

5+
## Usage
6+
7+
### Configuration
8+
9+
The application can be configured using:
10+
11+
- env variables, prefixed with `PERSURL_` (example: `PERSURL_DB_DSN`)
12+
- config files (example: `app.yml`)
13+
14+
Have a look at `example.config.yml` for an example configuration.
15+
516
## Documentation
617

718
Until the documentation becomes large enough, this README will be used to
@@ -32,7 +43,7 @@ test specifications ensure correct behaviour. Test drivers execute test specific
3243

3344
#### Load Tests
3445

35-
Load tests can enabled via the env variable `TEST_LOAD=1`.
46+
Load tests can enabled via the env variable `TEST_LOAD=1` or setting `test_load` to `1` in your config file.
3647

3748
These run the application and generate load by running multiple agents simulating user behaviour.
3849
The motivation of these tests is to ensure that the application can be used for a large user base which

config/env.go

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,16 @@ package config
22

33
import (
44
"errors"
5-
"fmt"
6-
"log"
7-
"os"
8-
"strconv"
95

10-
"github.com/joho/godotenv"
6+
"github.com/spf13/viper"
117
)
128

13-
func LoadEnv() {
14-
path := ".env"
15-
16-
// check if .env file exists - if not, exit early.
17-
_, err := os.Stat(path)
18-
if errors.Is(err, os.ErrNotExist) {
19-
return
20-
}
21-
22-
err = godotenv.Load(path)
23-
if err != nil {
24-
panic(fmt.Errorf("loading env failed: %w", err))
25-
}
26-
}
27-
28-
func DbDSN() string {
29-
dsn := os.Getenv("PERSURL_DB_DSN")
30-
if dsn == "" {
31-
dsn = os.Getenv("DATABASE_URL")
32-
}
33-
if dsn == "" {
34-
log.Fatalf("persurl db dsn may not be empty")
35-
}
36-
return dsn
37-
}
38-
39-
func DbMaxConnections() int {
40-
val := os.Getenv("PERSURL_DB_MAX_CONNECTIONS")
41-
if val == "" {
42-
val = "10"
43-
}
44-
45-
maxCon, err := strconv.ParseInt(val, 10, 32)
46-
if err != nil {
47-
log.Fatalf("invalid db max connection parameter %s", val)
9+
func setupEnv(v *viper.Viper) error {
10+
errs := []error{
11+
v.BindEnv("test_load", "TEST_LOAD"),
12+
v.BindEnv("db.dsn", "PERSURL_DB_DSN", "DATABASE_URL"),
13+
v.BindEnv("db.max_connections", "PERSURL_DB_MAX_CONNECTIONS"),
4814
}
4915

50-
return int(maxCon)
16+
return errors.Join(errs...)
5117
}

config/values.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
)
6+
7+
func DbDSN() string {
8+
dsn := vip.GetString("db.dsn")
9+
10+
if dsn == "" {
11+
panic(errors.New("db dsn may not be empty"))
12+
}
13+
14+
return dsn
15+
}
16+
17+
func DbMaxConnections() int {
18+
return vip.GetInt("db.max_connections")
19+
}
20+
21+
func TestLoad() bool {
22+
return vip.IsSet("test_load")
23+
}

config/viper.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
6+
"github.com/spf13/viper"
7+
)
8+
9+
var vip *viper.Viper
10+
11+
func init() {
12+
vip = setupViper()
13+
}
14+
15+
func setupViper() *viper.Viper {
16+
v := viper.New()
17+
18+
// loading
19+
v.AddConfigPath(".")
20+
v.SetConfigName("app")
21+
22+
// helper to panic on any error
23+
check := func(e error) {
24+
if e != nil {
25+
panic(e)
26+
}
27+
}
28+
29+
// defaults
30+
v.SetDefault("db.max_connections", 10)
31+
32+
// env binding
33+
check(setupEnv(v))
34+
35+
// trigger config parsing - optional
36+
if err := v.ReadInConfig(); err != nil {
37+
var configFileNotFoundError viper.ConfigFileNotFoundError
38+
if !errors.As(err, &configFileNotFoundError) {
39+
check(err)
40+
}
41+
}
42+
43+
return v
44+
}

example.config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Example configuration - copy as app.yml and modify
2+
3+
# Enable this if you want to run load tests
4+
# test_load: true
5+
6+
db:
7+
dsn: postgresql://persurl:persurl@localhost:5432/persurl?sslmode=disable
8+
max_connections: 10

example.env

Lines changed: 0 additions & 8 deletions
This file was deleted.

go.mod

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,41 @@ go 1.21
55
require (
66
github.com/doug-martin/goqu/v9 v9.18.0
77
github.com/gin-gonic/gin v1.9.1
8-
github.com/joho/godotenv v1.5.1
98
github.com/lib/pq v1.10.1
109
github.com/lopezator/migrator v0.3.1
1110
github.com/spf13/cobra v1.7.0
11+
github.com/spf13/viper v1.16.0
1212
github.com/stretchr/testify v1.8.3
1313
)
1414

1515
require (
1616
github.com/bytedance/sonic v1.9.1 // indirect
1717
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
1818
github.com/davecgh/go-spew v1.1.1 // indirect
19+
github.com/fsnotify/fsnotify v1.6.0 // indirect
1920
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
2021
github.com/gin-contrib/sse v0.1.0 // indirect
2122
github.com/go-playground/locales v0.14.1 // indirect
2223
github.com/go-playground/universal-translator v0.18.1 // indirect
2324
github.com/go-playground/validator/v10 v10.14.0 // indirect
2425
github.com/goccy/go-json v0.10.2 // indirect
25-
github.com/google/go-cmp v0.5.9 // indirect
26+
github.com/hashicorp/hcl v1.0.0 // indirect
2627
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2728
github.com/json-iterator/go v1.1.12 // indirect
2829
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
2930
github.com/leodido/go-urn v1.2.4 // indirect
31+
github.com/magiconair/properties v1.8.7 // indirect
3032
github.com/mattn/go-isatty v0.0.19 // indirect
33+
github.com/mitchellh/mapstructure v1.5.0 // indirect
3134
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
3235
github.com/modern-go/reflect2 v1.0.2 // indirect
3336
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
3437
github.com/pmezard/go-difflib v1.0.0 // indirect
38+
github.com/spf13/afero v1.9.5 // indirect
39+
github.com/spf13/cast v1.5.1 // indirect
40+
github.com/spf13/jwalterweatherman v1.1.0 // indirect
3541
github.com/spf13/pflag v1.0.5 // indirect
42+
github.com/subosito/gotenv v1.4.2 // indirect
3643
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
3744
github.com/ugorji/go/codec v1.2.11 // indirect
3845
golang.org/x/arch v0.3.0 // indirect
@@ -41,5 +48,6 @@ require (
4148
golang.org/x/sys v0.8.0 // indirect
4249
golang.org/x/text v0.9.0 // indirect
4350
google.golang.org/protobuf v1.30.0 // indirect
51+
gopkg.in/ini.v1 v1.67.0 // indirect
4452
gopkg.in/yaml.v3 v3.0.1 // indirect
4553
)

0 commit comments

Comments
 (0)