Skip to content

Commit 6e45ef9

Browse files
authored
Merge pull request #1 from objenious/fix/master/allow_all_levels
allow all levels
2 parents dd1402a + 432f25e commit 6e45ef9

5 files changed

Lines changed: 291 additions & 14 deletions

File tree

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ go:
77
- 1.7.x
88
- 1.8.x
99
- 1.9.x
10-
- tip
10+
- 1.10.x
11+
- 1.11.x

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# envconfig
22

3-
[![Build Status](https://travis-ci.org/kelseyhightower/envconfig.svg)](https://travis-ci.org/kelseyhightower/envconfig)
3+
[![Build Status](https://travis-ci.org/envconfig/envconfig.svg)](https://travis-ci.org/envconfig/envconfig)
44

55
```Go
6-
import "github.com/kelseyhightower/envconfig"
6+
import "github.com/envconfig/envconfig"
77
```
88

99
## Documentation
1010

11-
See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig)
11+
See [godoc](http://godoc.org/github.com/envconfig/envconfig)
1212

1313
## Usage
1414

@@ -34,7 +34,7 @@ import (
3434
"log"
3535
"time"
3636

37-
"github.com/kelseyhightower/envconfig"
37+
"github.com/envconfig/envconfig"
3838
)
3939

4040
type Specification struct {

config_test.go

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package envconfig
2+
3+
import (
4+
"os"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
type HTTPConfig struct {
12+
LivenessCheckPath string
13+
ReadinessCheckPath string
14+
HTTPPort int
15+
EnablePProf bool
16+
}
17+
18+
type PubSubConfig struct {
19+
Topic string `envconfig:"topic"`
20+
MaxExtension time.Duration `default:"15m" envconfig:"max_extension"`
21+
}
22+
23+
type PubSubConfigWithFunnyTags struct {
24+
Topic string `envconfig:"toto"`
25+
MaxExtension time.Duration `default:"15m" envconfig:"patati_patata"`
26+
}
27+
28+
type Config struct {
29+
HTTPConfig
30+
PubSubA PubSubConfig
31+
PubSubB PubSubConfig
32+
}
33+
34+
type Config2 struct {
35+
Config Config
36+
Old Config
37+
Name string
38+
}
39+
40+
type Config3 struct {
41+
Config
42+
Old Config
43+
Name string
44+
}
45+
46+
type Env map[string]string
47+
48+
func (e Env) setEnv() {
49+
for k, v := range e {
50+
os.Setenv(k, v)
51+
}
52+
}
53+
54+
func (e Env) clearEnv() {
55+
/*for k := range e {
56+
os.Setenv(k, "")
57+
}*/
58+
os.Clearenv()
59+
}
60+
61+
var expected = &Config{
62+
HTTPConfig: HTTPConfig{
63+
HTTPPort: 8088,
64+
LivenessCheckPath: "/test/live",
65+
ReadinessCheckPath: "/test/ready",
66+
EnablePProf: true,
67+
},
68+
PubSubA: PubSubConfig{
69+
Topic: "topicA",
70+
MaxExtension: time.Duration(11 * time.Hour),
71+
},
72+
PubSubB: PubSubConfig{
73+
Topic: "topicB",
74+
MaxExtension: time.Duration(22 * time.Minute),
75+
},
76+
}
77+
78+
func TestConfigs(t *testing.T) {
79+
tests := []struct {
80+
prefix string
81+
env Env
82+
cfg interface{}
83+
expected interface{}
84+
}{
85+
{
86+
"test",
87+
Env{
88+
"HTTPPORT": "8088",
89+
"LIVENESSCHECKPATH": "/test/live",
90+
"READINESSCHECKPATH": "/test/ready",
91+
"ENABLEPPROF": "true",
92+
},
93+
&HTTPConfig{},
94+
&expected.HTTPConfig,
95+
},
96+
{
97+
"pubsubb",
98+
Env{
99+
"PUBSUBB_TOPIC": "topicB",
100+
"MAX_EXTENSION": "22m",
101+
},
102+
&PubSubConfig{},
103+
&expected.PubSubB,
104+
},
105+
{
106+
"pubsubb",
107+
Env{
108+
"PUBSUBB_TOTO": "topicB",
109+
"PATATI_PATATA": "22m",
110+
},
111+
&PubSubConfigWithFunnyTags{},
112+
&PubSubConfigWithFunnyTags{
113+
Topic: "topicB",
114+
MaxExtension: time.Duration(22 * time.Minute),
115+
},
116+
},
117+
{
118+
"test",
119+
Env{
120+
"HTTPPORT": "8088",
121+
"LIVENESSCHECKPATH": "/test/live",
122+
"READINESSCHECKPATH": "/test/ready",
123+
"ENABLEPPROF": "true",
124+
"TOPIC": "topicA",
125+
"PUBSUBA_MAX_EXTENSION": "11h",
126+
"PUBSUBB_TOPIC": "topicB",
127+
"MAX_EXTENSION": "22m",
128+
},
129+
&Config{},
130+
expected,
131+
},
132+
{
133+
"test",
134+
Env{
135+
"TEST_HTTPPORT": "8088",
136+
"LIVENESSCHECKPATH": "/test/live",
137+
"TEST_READINESSCHECKPATH": "/test/ready",
138+
"TEST_ENABLEPPROF": "true",
139+
"PUBSUBA_TOPIC": "topicA",
140+
"MAX_EXTENSION": "11h",
141+
"TOPIC": "topicB",
142+
"TEST_PUBSUBB_MAX_EXTENSION": "22m",
143+
},
144+
&Config{},
145+
expected,
146+
},
147+
{
148+
"test",
149+
Env{
150+
"TEST_CONFIG_HTTPPORT": "8080",
151+
"CONFIG_LIVENESSCHECKPATH": "/cfg/live",
152+
"TEST_CONFIG_READINESSCHECKPATH": "/cfg/ready",
153+
"TEST_CONFIG_ENABLEPPROF": "true",
154+
"CONFIG_PUBSUBA_TOPIC": "topicA-prod",
155+
"MAX_EXTENSION": "11h",
156+
"TOPIC": "topicB",
157+
"TEST_CONFIG_PUBSUBB_MAX_EXTENSION": "33m",
158+
"NAME": "test-multi",
159+
"TEST_OLD_HTTPPORT": "8088",
160+
"OLD_LIVENESSCHECKPATH": "/test/live",
161+
"TEST_OLD_READINESSCHECKPATH": "/test/ready",
162+
"TEST_OLD_ENABLEPPROF": "true",
163+
"OLD_PUBSUBA_TOPIC": "topicA",
164+
"TEST_OLD_PUBSUBB_MAX_EXTENSION": "22m",
165+
},
166+
&Config2{},
167+
&Config2{
168+
Config: Config{
169+
HTTPConfig: HTTPConfig{
170+
HTTPPort: 8080,
171+
LivenessCheckPath: "/cfg/live",
172+
ReadinessCheckPath: "/cfg/ready",
173+
EnablePProf: true,
174+
},
175+
PubSubA: PubSubConfig{
176+
Topic: "topicA-prod",
177+
MaxExtension: time.Duration(11 * time.Hour),
178+
},
179+
PubSubB: PubSubConfig{
180+
Topic: "topicB",
181+
MaxExtension: time.Duration(33 * time.Minute),
182+
},
183+
},
184+
Old: *expected,
185+
Name: "test-multi",
186+
},
187+
},
188+
{
189+
"test",
190+
Env{
191+
"TEST_HTTPPORT": "8080",
192+
"LIVENESSCHECKPATH": "/cfg/live",
193+
"TEST_READINESSCHECKPATH": "/cfg/ready",
194+
"TEST_ENABLEPPROF": "true",
195+
"PUBSUBA_TOPIC": "topicA-prod",
196+
"MAX_EXTENSION": "11h",
197+
"TOPIC": "topicB",
198+
"TEST_PUBSUBB_MAX_EXTENSION": "33m",
199+
"NAME": "test-multi",
200+
"TEST_OLD_HTTPPORT": "8088",
201+
"OLD_LIVENESSCHECKPATH": "/test/live",
202+
"TEST_OLD_READINESSCHECKPATH": "/test/ready",
203+
"TEST_OLD_ENABLEPPROF": "true",
204+
"OLD_PUBSUBA_TOPIC": "topicA",
205+
"TEST_OLD_PUBSUBB_MAX_EXTENSION": "22m",
206+
},
207+
&Config3{},
208+
&Config3{
209+
Config: Config{
210+
HTTPConfig: HTTPConfig{
211+
HTTPPort: 8080,
212+
LivenessCheckPath: "/cfg/live",
213+
ReadinessCheckPath: "/cfg/ready",
214+
EnablePProf: true,
215+
},
216+
PubSubA: PubSubConfig{
217+
Topic: "topicA-prod",
218+
MaxExtension: time.Duration(11 * time.Hour),
219+
},
220+
PubSubB: PubSubConfig{
221+
Topic: "topicB",
222+
MaxExtension: time.Duration(33 * time.Minute),
223+
},
224+
},
225+
Old: *expected,
226+
Name: "test-multi",
227+
},
228+
},
229+
}
230+
for i, test := range tests {
231+
t.Log("test envconfig with ", i, test)
232+
test.env.setEnv()
233+
if err := Process(test.prefix, test.cfg); err != nil {
234+
t.Error(err)
235+
}
236+
assert.Equal(t, test.expected, test.cfg)
237+
test.env.clearEnv()
238+
}
239+
}

envconfig.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (e *ParseError) Error() string {
5050
// varInfo maintains information about the configuration variable
5151
type varInfo struct {
5252
Name string
53-
Alt string
53+
Alt []string
5454
Key string
5555
Field reflect.Value
5656
Tags reflect.StructTag
@@ -95,7 +95,7 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
9595
Name: ftype.Name,
9696
Field: f,
9797
Tags: ftype.Tag,
98-
Alt: strings.ToUpper(ftype.Tag.Get("envconfig")),
98+
Alt: generateAlternatives(strings.ToUpper(ftype.Tag.Get("envconfig")), ftype.Name),
9999
}
100100

101101
// Default to the field name as the env var name (will be upcased)
@@ -113,18 +113,21 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
113113
info.Key = strings.Join(name, "_")
114114
}
115115
}
116-
if info.Alt != "" {
117-
info.Key = info.Alt
116+
if info.Alt[0] != "" {
117+
info.Key = info.Alt[0]
118+
} else {
119+
info.Alt = generateAlternatives(strings.ToUpper(info.Key), ftype.Name)
118120
}
119121
if prefix != "" {
120122
info.Key = fmt.Sprintf("%s_%s", prefix, info.Key)
123+
info.Alt = generateAlternatives(strings.ToUpper(info.Key), ftype.Name)
121124
}
122125
info.Key = strings.ToUpper(info.Key)
123126
infos = append(infos, info)
124127

125128
if f.Kind() == reflect.Struct {
126129
// honor Decode if present
127-
if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil {
130+
if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil {
128131
innerPrefix := prefix
129132
if !ftype.Anonymous {
130133
innerPrefix = info.Key
@@ -144,6 +147,19 @@ func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
144147
return infos, nil
145148
}
146149

150+
func generateAlternatives(matrice, name string) []string {
151+
alts := []string{matrice}
152+
split := strings.Split(matrice, "_")
153+
for i := 1; i < len(split); i++ {
154+
alt := strings.Join(split[i:], "_")
155+
if alt == name {
156+
break
157+
}
158+
alts = append(alts, alt)
159+
}
160+
return alts
161+
}
162+
147163
// CheckDisallowed checks that no environment variables with the prefix are set
148164
// that we don't know how or want to parse. This is likely only meaningful with
149165
// a non-empty prefix.
@@ -186,8 +202,13 @@ func Process(prefix string, spec interface{}) error {
186202
// but it is only available in go1.5 or newer. We're using Go build tags
187203
// here to use os.LookupEnv for >=go1.5
188204
value, ok := lookupEnv(info.Key)
189-
if !ok && info.Alt != "" {
190-
value, ok = lookupEnv(info.Alt)
205+
if !ok {
206+
for _, alt := range info.Alt {
207+
value, ok = lookupEnv(alt)
208+
if ok {
209+
break
210+
}
211+
}
191212
}
192213

193214
def := info.Tags.Get("default")

envconfig_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,8 @@ func TestUnsetVars(t *testing.T) {
308308

309309
// If the var is not defined the non-prefixed version should not be used
310310
// unless the struct tag says so
311-
if s.User != "" {
312-
t.Errorf("expected %q, got %q", "", s.User)
311+
if s.User != "foo" {
312+
t.Errorf("expected %q, got %q", "foo", s.User)
313313
}
314314
}
315315

@@ -774,6 +774,22 @@ func TestCheckDisallowedIgnored(t *testing.T) {
774774
}
775775
}
776776

777+
func TestPrefix(t *testing.T) {
778+
var s Specification
779+
os.Clearenv()
780+
os.Setenv("REQUIREDVAR", "foo")
781+
os.Setenv("DEBUG", "true")
782+
os.Setenv("PORT", "80")
783+
784+
err := Process("env_config", &s)
785+
if err != nil {
786+
t.Errorf("no error expected, got %v", err)
787+
}
788+
if s.Debug != true {
789+
t.Errorf("expected %v, got %v", true, s.Debug)
790+
}
791+
}
792+
777793
type bracketed string
778794

779795
func (b *bracketed) Set(value string) error {

0 commit comments

Comments
 (0)