Skip to content

Commit f6caa7b

Browse files
author
Jason Yellick
committed
Add string slice config support
When overriding a configuration option via the env which is supposed to be a collection collection (slice) of options, the override is instead interpretted as a string. This patch modifies the config decode hook to handle this special case and appropriately decode the env override to a []string instead of a plain string. There is no JIRA issue associated with this, but is being done to support the Kafka epic effort, visible here: https://jira.hyperledger.org/browse/FAB-32 Reclaim commit after a little Gerrit accident Change-Id: Iae800b5237549232b90c7d7cf91590a3d8caf178 Signed-off-by: Jason Yellick <[email protected]>
1 parent 60e4e45 commit f6caa7b

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

orderer/config/config_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ limitations under the License.
1717
package config
1818

1919
import (
20+
"bytes"
2021
"fmt"
2122
"os"
23+
"reflect"
24+
"strings"
2225
"testing"
2326

2427
"github.com/spf13/viper"
@@ -50,6 +53,45 @@ func TestBadConfig(t *testing.T) {
5053
}
5154
}
5255

56+
type testSlice struct {
57+
Inner struct {
58+
Slice []string
59+
}
60+
}
61+
62+
func TestEnvSlice(t *testing.T) {
63+
envVar := "ORDERER_INNER_SLICE"
64+
envVal := "[a, b, c]"
65+
os.Setenv(envVar, envVal)
66+
defer os.Unsetenv(envVar)
67+
config := viper.New()
68+
config.SetEnvPrefix(Prefix)
69+
config.AutomaticEnv()
70+
replacer := strings.NewReplacer(".", "_")
71+
config.SetEnvKeyReplacer(replacer)
72+
config.SetConfigType("yaml")
73+
74+
data := "---\nInner:\n Slice: [d,e,f]"
75+
76+
err := config.ReadConfig(bytes.NewReader([]byte(data)))
77+
78+
if err != nil {
79+
t.Fatalf("Error reading %s plugin config: %s", Prefix, err)
80+
}
81+
82+
var uconf testSlice
83+
84+
err = ExactWithDateUnmarshal(config, &uconf)
85+
if err != nil {
86+
t.Fatalf("Failed to unmarshal with: %s", err)
87+
}
88+
89+
expected := []string{"a", "b", "c"}
90+
if !reflect.DeepEqual(uconf.Inner.Slice, expected) {
91+
t.Fatalf("Did not get back the right slice, expeced: %v got %v", expected, uconf.Inner.Slice)
92+
}
93+
}
94+
5395
// TestEnvInnerVar verifies that with the Unmarshal function that
5496
// the environmental overrides still work on internal vars. This was
5597
// a bug in the original viper implementation that is worked around in

orderer/config/config_util.go

+36-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ limitations under the License.
1717
package config
1818

1919
import (
20+
"reflect"
21+
"strings"
22+
"time"
23+
2024
"github.com/mitchellh/mapstructure"
2125
"github.com/spf13/viper"
2226
)
@@ -45,6 +49,37 @@ func getKeysRecursively(base string, v *viper.Viper, nodeKeys map[string]interfa
4549
return result
4650
}
4751

52+
// customDecodeHook adds the additional functions of parsing durations from strings
53+
// as well as parsing strings of the format "[thing1, thing2, thing3]" into string slices
54+
// Note that whitespace around slice elements is removed
55+
func customDecodeHook() mapstructure.DecodeHookFunc {
56+
durationHook := mapstructure.StringToTimeDurationHookFunc()
57+
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
58+
dur, err := mapstructure.DecodeHookExec(durationHook, f, t, data)
59+
if err == nil {
60+
if _, ok := dur.(time.Duration); ok {
61+
return dur, nil
62+
}
63+
}
64+
65+
if f.Kind() != reflect.String {
66+
return data, nil
67+
}
68+
69+
raw := data.(string)
70+
l := len(raw)
71+
if raw[0] == '[' && raw[l-1] == ']' {
72+
slice := strings.Split(raw[1:l-1], ",")
73+
for i, v := range slice {
74+
slice[i] = strings.TrimSpace(v)
75+
}
76+
return slice, nil
77+
}
78+
79+
return data, nil
80+
}
81+
}
82+
4883
// ExactWithDateUnmarshal is intended to unmarshal a config file into a structure
4984
// producing error when extraneous variables are introduced and supporting
5085
// the time.Duration type
@@ -58,7 +93,7 @@ func ExactWithDateUnmarshal(v *viper.Viper, output interface{}) error {
5893
Metadata: nil,
5994
Result: output,
6095
WeaklyTypedInput: true,
61-
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
96+
DecodeHook: customDecodeHook(),
6297
}
6398

6499
decoder, err := mapstructure.NewDecoder(config)

0 commit comments

Comments
 (0)