From 916b94eb3aac698918e79a7b8592ac0ce5b74012 Mon Sep 17 00:00:00 2001 From: Andrii Soldatenko Date: Sun, 16 Jul 2023 21:00:18 +0200 Subject: [PATCH 1/2] Added support for configuring array values with environment variables #3511 Signed-off-by: Andrii Soldatenko --- configuration/parser.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/configuration/parser.go b/configuration/parser.go index 6d6451800..017d63a9b 100644 --- a/configuration/parser.go +++ b/configuration/parser.go @@ -166,6 +166,17 @@ func (p *Parser) overwriteFields(v reflect.Value, fullpath string, path []string return p.overwriteStruct(v, fullpath, path, payload) case reflect.Map: return p.overwriteMap(v, fullpath, path, payload) + case reflect.Slice: + idx, err := strconv.Atoi(path[0]) + if err != nil { + panic("non-numeric index: " + path[0]) + } + + if idx >= v.Len() { + panic("Undefined index: " + path[0]) + } + + return p.overwriteFields(v.Index(idx), fullpath, path[1:], payload) case reflect.Interface: if v.NumMethod() == 0 { if !v.IsNil() { From 0cf87b1fd17ab8d086559e75b7e1a828605bfaed Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Mon, 17 Jul 2023 22:40:32 +0100 Subject: [PATCH 2/2] Fix the code and update tests that verify the new code works Signed-off-by: Milos Gajdos --- configuration/parser.go | 12 +++++++++-- configuration/parser_test.go | 41 ++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/configuration/parser.go b/configuration/parser.go index 017d63a9b..1a9201986 100644 --- a/configuration/parser.go +++ b/configuration/parser.go @@ -172,10 +172,18 @@ func (p *Parser) overwriteFields(v reflect.Value, fullpath string, path []string panic("non-numeric index: " + path[0]) } - if idx >= v.Len() { - panic("Undefined index: " + path[0]) + if idx > v.Len() { + panic("undefined index: " + path[0]) } + // if there is no element or the current slice length + // is the same as the indexed variable create a new element, + // append it and then set it to the passed in env var value. + if v.Len() == 0 || idx == v.Len() { + typ := v.Type().Elem() + elem := reflect.New(typ).Elem() + v.Set(reflect.Append(v, elem)) + } return p.overwriteFields(v.Index(idx), fullpath, path[1:], payload) case reflect.Interface: if v.NumMethod() == 0 { diff --git a/configuration/parser_test.go b/configuration/parser_test.go index 7f352483b..b2cb60c9d 100644 --- a/configuration/parser_test.go +++ b/configuration/parser_test.go @@ -8,21 +8,39 @@ import ( ) type localConfiguration struct { - Version Version `yaml:"version"` - Log *Log `yaml:"log"` + Version Version `yaml:"version"` + Log *Log `yaml:"log"` + Notifications []Notif `yaml:"notifications,omitempty"` } type Log struct { Formatter string `yaml:"formatter,omitempty"` } +type Notif struct { + Name string `yaml:"name"` +} + var expectedConfig = localConfiguration{ Version: "0.1", Log: &Log{ Formatter: "json", }, + Notifications: []Notif{ + {Name: "foo"}, + {Name: "bar"}, + {Name: "car"}, + }, } +const testConfig = `version: "0.1" +log: + formatter: "text" +notifications: + - name: "foo" + - name: "bar" + - name: "car"` + type ParserSuite struct{} var _ = check.Suite(new(ParserSuite)) @@ -43,17 +61,32 @@ func (suite *ParserSuite) TestParserOverwriteIninitializedPoiner(c *check.C) { }, }) - err := p.Parse([]byte(`{version: "0.1", log: {formatter: "text"}}`), &config) + err := p.Parse([]byte(testConfig), &config) c.Assert(err, check.IsNil) c.Assert(config, check.DeepEquals, expectedConfig) } +const testConfig2 = `version: "0.1" +log: + formatter: "text" +notifications: + - name: "val1" + - name: "val2" + - name: "car"` + func (suite *ParserSuite) TestParseOverwriteUnininitializedPoiner(c *check.C) { config := localConfiguration{} os.Setenv("REGISTRY_LOG_FORMATTER", "json") defer os.Unsetenv("REGISTRY_LOG_FORMATTER") + // override only first two notificationsvalues + // in the tetConfig: leave the last value unchanged. + os.Setenv("REGISTRY_NOTIFICATIONS_0_NAME", "foo") + defer os.Unsetenv("REGISTRY_NOTIFICATIONS_0_NAME") + os.Setenv("REGISTRY_NOTIFICATIONS_1_NAME", "bar") + defer os.Unsetenv("REGISTRY_NOTIFICATIONS_1_NAME") + p := NewParser("registry", []VersionedParseInfo{ { Version: "0.1", @@ -64,7 +97,7 @@ func (suite *ParserSuite) TestParseOverwriteUnininitializedPoiner(c *check.C) { }, }) - err := p.Parse([]byte(`{version: "0.1"}`), &config) + err := p.Parse([]byte(testConfig2), &config) c.Assert(err, check.IsNil) c.Assert(config, check.DeepEquals, expectedConfig) }