forked from zalando/skipper
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
config: add yaml flag (zalando#3069)
YAML flag parses (preferably flow-style) YAML value from the command line or unmarshals object yaml value from the yaml config file. Example value: ``` bin/skipper -foo-flag='{foo: hello, bar: [world, "1"], baz: 2, qux: {baz: 3}}' ``` and equivalent branch in config yaml: ```yaml foo-flag: foo: hello bar: - world - "1" baz: 2 qux: baz: 3 ``` This will be useful for zalando#2104 It is also a better alternative to manual parsing of micro-syntax like e.g. implemented in zalando#2888 Signed-off-by: Alexander Yastrebov <[email protected]>
- Loading branch information
1 parent
8cc6aab
commit a956aa9
Showing
2 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
|
||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
type yamlFlag[T any] struct { | ||
Ptr **T | ||
value string // only for Set | ||
} | ||
|
||
func newYamlFlag[T any](ptr **T) *yamlFlag[T] { | ||
return &yamlFlag[T]{Ptr: ptr} | ||
} | ||
|
||
func (yf *yamlFlag[T]) Set(value string) error { | ||
var opts T | ||
if err := yaml.Unmarshal([]byte(value), &opts); err != nil { | ||
return fmt.Errorf("failed to parse yaml: %w", err) | ||
} | ||
*yf.Ptr = &opts | ||
yf.value = value | ||
return nil | ||
} | ||
|
||
func (yf *yamlFlag[T]) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
var opts T | ||
if err := unmarshal(&opts); err != nil { | ||
return err | ||
} | ||
*yf.Ptr = &opts | ||
return nil | ||
} | ||
|
||
func (yf *yamlFlag[T]) String() string { | ||
if yf == nil { | ||
return "" | ||
} | ||
return yf.value | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package config | ||
|
||
import ( | ||
"testing" | ||
|
||
"gopkg.in/yaml.v2" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
type yamlFlagTestConfig struct { | ||
Foo string | ||
Bar []string | ||
Baz int | ||
Qux *yamlFlagTestConfig | ||
} | ||
|
||
func TestYamlFlag(t *testing.T) { | ||
t.Run("set", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
f := newYamlFlag(&cfg.Field) | ||
v := `{foo: hello, bar: [world, "1"], baz: 2, qux: {baz: 3}}` | ||
err := f.Set(v) | ||
|
||
require.NoError(t, err) | ||
assert.Equal(t, "hello", cfg.Field.Foo) | ||
assert.Equal(t, []string{"world", "1"}, cfg.Field.Bar) | ||
assert.Equal(t, 2, cfg.Field.Baz) | ||
assert.Equal(t, 3, cfg.Field.Qux.Baz) | ||
|
||
assert.Equal(t, v, f.String()) | ||
}) | ||
|
||
t.Run("set empty", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
f := newYamlFlag(&cfg.Field) | ||
err := f.Set("") | ||
|
||
require.NoError(t, err) | ||
assert.Equal(t, &yamlFlagTestConfig{}, cfg.Field) | ||
assert.Equal(t, "", f.String()) | ||
}) | ||
|
||
t.Run("set empty object", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
f := newYamlFlag(&cfg.Field) | ||
err := f.Set("{}") | ||
|
||
require.NoError(t, err) | ||
assert.Equal(t, &yamlFlagTestConfig{}, cfg.Field) | ||
assert.Equal(t, "{}", f.String()) | ||
}) | ||
|
||
t.Run("set invalid yaml", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
f := newYamlFlag(&cfg.Field) | ||
v := `This is not a valid YAML` | ||
err := f.Set(v) | ||
|
||
assert.Error(t, err) | ||
}) | ||
|
||
t.Run("unmarshal YAML", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
err := yaml.Unmarshal([]byte(` | ||
field: | ||
foo: hello | ||
bar: | ||
- world | ||
- "1" | ||
baz: 2 | ||
qux: | ||
baz: 3 | ||
`), &cfg) | ||
|
||
require.NoError(t, err) | ||
assert.Equal(t, "hello", cfg.Field.Foo) | ||
assert.Equal(t, []string{"world", "1"}, cfg.Field.Bar) | ||
assert.Equal(t, 2, cfg.Field.Baz) | ||
assert.Equal(t, 3, cfg.Field.Qux.Baz) | ||
}) | ||
|
||
t.Run("unmarshal invalid YAML", func(t *testing.T) { | ||
cfg := struct { | ||
Field *yamlFlagTestConfig | ||
}{} | ||
|
||
err := yaml.Unmarshal([]byte(`This is not a valid YAML`), &cfg) | ||
|
||
assert.Error(t, err) | ||
}) | ||
} |