From 972ef536641551737518d134c22d9193f32b6ac9 Mon Sep 17 00:00:00 2001 From: Luis Sanchez Date: Mon, 16 Jan 2017 14:34:09 -0500 Subject: [PATCH] [FAB-1653] Enable human-readable byte sizes in config Byte sizes can be specified with the following case insensitive suffixes: - 'k', 'kb', 'm', 'mb', 'g', 'gb' Change-Id: I36c3fd424ed52794562a6b50e7a3d400f053c52a Signed-off-by: Luis Sanchez --- orderer/localconfig/config_test.go | 71 ++++++++++++++++++++++++++++++ orderer/localconfig/config_util.go | 44 +++++++++++++++++- orderer/orderer.yaml | 4 +- 3 files changed, 116 insertions(+), 3 deletions(-) diff --git a/orderer/localconfig/config_test.go b/orderer/localconfig/config_test.go index 0ceccd05c4b..bd9d7cece1b 100644 --- a/orderer/localconfig/config_test.go +++ b/orderer/localconfig/config_test.go @@ -93,6 +93,77 @@ func TestEnvSlice(t *testing.T) { } } +type testByteSize struct { + Inner struct { + ByteSize uint32 + } +} + +func TestByteSize(t *testing.T) { + config := viper.New() + config.SetConfigType("yaml") + + testCases := []struct { + data string + expected uint32 + }{ + {"", 0}, + {"42", 42}, + {"42k", 42 * 1024}, + {"42kb", 42 * 1024}, + {"42K", 42 * 1024}, + {"42KB", 42 * 1024}, + {"42 K", 42 * 1024}, + {"42 KB", 42 * 1024}, + {"42m", 42 * 1024 * 1024}, + {"42mb", 42 * 1024 * 1024}, + {"42M", 42 * 1024 * 1024}, + {"42MB", 42 * 1024 * 1024}, + {"42 M", 42 * 1024 * 1024}, + {"42 MB", 42 * 1024 * 1024}, + {"3g", 3 * 1024 * 1024 * 1024}, + {"3gb", 3 * 1024 * 1024 * 1024}, + {"3G", 3 * 1024 * 1024 * 1024}, + {"3GB", 3 * 1024 * 1024 * 1024}, + {"3 G", 3 * 1024 * 1024 * 1024}, + {"3 GB", 3 * 1024 * 1024 * 1024}, + } + + for _, tc := range testCases { + t.Run(tc.data, func(t *testing.T) { + data := fmt.Sprintf("---\nInner:\n ByteSize: %s", tc.data) + err := config.ReadConfig(bytes.NewReader([]byte(data))) + if err != nil { + t.Fatalf("Error reading config: %s", err) + } + var uconf testByteSize + err = ExactWithDateUnmarshal(config, &uconf) + if err != nil { + t.Fatalf("Failed to unmarshal with: %s", err) + } + if uconf.Inner.ByteSize != tc.expected { + t.Fatalf("Did not get back the right byte size, expeced: %v got %v", tc.expected, uconf.Inner.ByteSize) + } + }) + } +} + +func TestByteSizeOverflow(t *testing.T) { + config := viper.New() + config.SetConfigType("yaml") + + data := "---\nInner:\n ByteSize: 4GB" + err := config.ReadConfig(bytes.NewReader([]byte(data))) + if err != nil { + t.Fatalf("Error reading config: %s", err) + } + var uconf testByteSize + err = ExactWithDateUnmarshal(config, &uconf) + if err == nil { + t.Fatalf("Should have failed to unmarshal") + } +} + // TestEnvInnerVar verifies that with the Unmarshal function that // the environmental overrides still work on internal vars. This was // a bug in the original viper implementation that is worked around in diff --git a/orderer/localconfig/config_util.go b/orderer/localconfig/config_util.go index 9707447e5d8..6b753c9c1f6 100644 --- a/orderer/localconfig/config_util.go +++ b/orderer/localconfig/config_util.go @@ -17,7 +17,11 @@ limitations under the License. package config import ( + "fmt" + "math" "reflect" + "regexp" + "strconv" "strings" "time" @@ -83,6 +87,41 @@ func customDecodeHook() mapstructure.DecodeHookFunc { } } +func byteSizeDecodeHook() mapstructure.DecodeHookFunc { + return func(f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) { + if f != reflect.String || t != reflect.Uint32 { + return data, nil + } + raw := data.(string) + if raw == "" { + return data, nil + } + var re = regexp.MustCompile(`^(?P[0-9]+)\s*(?i)(?P(k|m|g))b?$`) + if re.MatchString(raw) { + size, err := strconv.ParseUint(re.ReplaceAllString(raw, "${size}"), 0, 64) + if err != nil { + return data, nil + } + unit := re.ReplaceAllString(raw, "${unit}") + switch strings.ToLower(unit) { + case "g": + size = size << 10 + fallthrough + case "m": + size = size << 10 + fallthrough + case "k": + size = size << 10 + } + if size > math.MaxUint32 { + return size, fmt.Errorf("value '%s' overflows uint32", raw) + } + return size, nil + } + return data, nil + } +} + // ExactWithDateUnmarshal is intended to unmarshal a config file into a structure // producing error when extraneous variables are introduced and supporting // the time.Duration type @@ -96,7 +135,10 @@ func ExactWithDateUnmarshal(v *viper.Viper, output interface{}) error { Metadata: nil, Result: output, WeaklyTypedInput: true, - DecodeHook: customDecodeHook(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + customDecodeHook(), + byteSizeDecodeHook(), + ), } decoder, err := mapstructure.NewDecoder(config) diff --git a/orderer/orderer.yaml b/orderer/orderer.yaml index ef103081285..35adc8dfab4 100644 --- a/orderer/orderer.yaml +++ b/orderer/orderer.yaml @@ -124,9 +124,9 @@ Genesis: # Absolute Max Bytes: The absolute maximum number of bytes allowed for # the serialized messages in a batch. - AbsoluteMaxBytes: 100000000 + AbsoluteMaxBytes: 99 MB # Preferred Max Bytes: The preferred maximum number of bytes allowed for # the serialized messages in a batch. A message larger than the preferred # max bytes will result in a batch larger than preferred max bytes. - PreferredMaxBytes: 524288 + PreferredMaxBytes: 512 KB