diff options
| author | Dave Henderson <dhenderson@gmail.com> | 2024-01-25 21:14:06 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-25 21:14:06 -0500 |
| commit | 2cc314740e4ce29739c667f0887448d6ee592542 (patch) | |
| tree | 93e6768da61b137e95a1079e775117c238146a24 /internal | |
| parent | 562bcd95dfb6fb14af7e0b5829beccff3636d2f1 (diff) | |
Revert "Remove support for deprecated key/value array form of template config" (#1979)
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/config/configfile.go | 7 | ||||
| -rw-r--r-- | internal/config/configfile_test.go | 14 | ||||
| -rw-r--r-- | internal/config/types.go | 78 | ||||
| -rw-r--r-- | internal/config/types_test.go | 29 | ||||
| -rw-r--r-- | internal/tests/integration/config_test.go | 14 |
5 files changed, 128 insertions, 14 deletions
diff --git a/internal/config/configfile.go b/internal/config/configfile.go index f22d9a1b..1fcccc20 100644 --- a/internal/config/configfile.go +++ b/internal/config/configfile.go @@ -40,7 +40,7 @@ type Config struct { DataSources map[string]DataSource `yaml:"datasources,omitempty"` Context map[string]DataSource `yaml:"context,omitempty"` Plugins map[string]PluginConfig `yaml:"plugins,omitempty"` - Templates map[string]DataSource `yaml:"templates,omitempty"` + Templates Templates `yaml:"templates,omitempty"` // Extra HTTP headers not attached to pre-defined datsources. Potentially // used by datasources defined in the template. @@ -107,7 +107,7 @@ type DataSource struct { // well supported, and anyway we need to do some extra parsing func (d *DataSource) UnmarshalYAML(value *yaml.Node) error { type raw struct { - Header http.Header `yaml:",omitempty"` + Header http.Header URL string } r := raw{} @@ -130,10 +130,9 @@ func (d *DataSource) UnmarshalYAML(value *yaml.Node) error { // well supported, and anyway we need to do some extra parsing func (d DataSource) MarshalYAML() (interface{}, error) { type raw struct { - Header http.Header `yaml:",omitempty"` + Header http.Header URL string } - r := raw{ URL: d.URL.String(), Header: d.Header, diff --git a/internal/config/configfile_test.go b/internal/config/configfile_test.go index d2759a1e..449c379b 100644 --- a/internal/config/configfile_test.go +++ b/internal/config/configfile_test.go @@ -74,7 +74,7 @@ pluginTimeout: 2s Plugins: map[string]PluginConfig{ "foo": {Cmd: "echo", Pipe: true}, }, - Templates: map[string]DataSource{"foo": {URL: mustURL("file:///tmp/foo.t")}}, + Templates: Templates{"foo": DataSource{URL: mustURL("file:///tmp/foo.t")}}, PluginTimeout: 2 * time.Second, } @@ -386,7 +386,7 @@ func TestMergeFrom(t *testing.T) { cfg = &Config{ InputDir: "indir/", ExcludeGlob: []string{"*.txt"}, - Templates: map[string]DataSource{ + Templates: Templates{ "foo": { URL: mustURL("file:///foo.yaml"), }, @@ -402,7 +402,7 @@ func TestMergeFrom(t *testing.T) { OutMode: "600", LDelim: "${", RDelim: "}", - Templates: map[string]DataSource{ + Templates: Templates{ "foo": {URL: mustURL("https://example.com/foo.yaml")}, "baz": {URL: mustURL("vault:///baz")}, }, @@ -414,7 +414,7 @@ func TestMergeFrom(t *testing.T) { OutMode: "600", LDelim: "${", RDelim: "}", - Templates: map[string]DataSource{ + Templates: Templates{ "foo": {URL: mustURL("https://example.com/foo.yaml")}, "bar": { URL: mustURL("stdin:///"), @@ -503,7 +503,7 @@ func TestParseDataSourceFlags(t *testing.T) { ) require.NoError(t, err) assert.EqualValues(t, &Config{ - Templates: map[string]DataSource{ + Templates: Templates{ "foo": { URL: mustURL("http://example.com"), Header: http.Header{"Accept": {"application/json"}}, @@ -551,7 +551,7 @@ pluginTimeout: 5s RDelim: "R", Input: "foo", OutputFiles: []string{"-"}, - Templates: map[string]DataSource{ + Templates: Templates{ "foo": {URL: mustURL("https://www.example.com/foo.tmpl")}, "bar": {URL: mustURL("file:///tmp/bar.t")}, }, @@ -576,7 +576,7 @@ templates: RDelim: "R", Input: "long input that should be truncated", OutputFiles: []string{"-"}, - Templates: map[string]DataSource{ + Templates: Templates{ "foo": {URL: mustURL("https://www.example.com/foo.tmpl")}, "bar": {URL: mustURL("file:///tmp/bar.t")}, }, diff --git a/internal/config/types.go b/internal/config/types.go index 25414105..648ad2b9 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -1,11 +1,89 @@ package config import ( + "fmt" + "net/http" "strings" "github.com/hairyhenderson/gomplate/v4/internal/urlhelpers" + "github.com/hairyhenderson/yaml" ) +// Templates - a map of templates. We can't just use map[string]DataSource, +// because we need to be able to marshal both the old (array of '[k=]v' strings) +// and the new (proper map) formats. +// +// Note that templates use the DataSource type, since they have the exact same +// shape. +// TODO: get rid of this and just use map[string]DataSource once the legacy +// [k=]v array format is no longer supported +type Templates map[string]DataSource + +// UnmarshalYAML - satisfy the yaml.Umarshaler interface +func (t *Templates) UnmarshalYAML(value *yaml.Node) error { + // first attempt to unmarshal as a map[string]DataSource + m := map[string]DataSource{} + err := value.Decode(m) + if err == nil { + *t = m + return nil + } + + // if that fails, try to unmarshal as an array of '[k=]v' strings + err = t.unmarshalYAMLArray(value) + if err != nil { + return fmt.Errorf("could not unmarshal templates as map or array: %w", err) + } + + return nil +} + +func (t *Templates) unmarshalYAMLArray(value *yaml.Node) error { + a := []string{} + err := value.Decode(&a) + if err != nil { + return fmt.Errorf("could not unmarshal templates as array: %w", err) + } + + ts := Templates{} + for _, s := range a { + alias, pth, _ := strings.Cut(s, "=") + if pth == "" { + // when alias is omitted, the path and alias are identical + pth = alias + } + + u, err := urlhelpers.ParseSourceURL(pth) + if err != nil { + return fmt.Errorf("could not parse template URL %q: %w", pth, err) + } + + ts[alias] = DataSource{ + URL: u, + } + } + + *t = ts + + return nil +} + +func (t Templates) MarshalYAML() (interface{}, error) { + type rawTemplate struct { + Header http.Header `yaml:"header,omitempty,flow"` + URL string `yaml:"url"` + } + + m := map[string]rawTemplate{} + for k, v := range t { + m[k] = rawTemplate{ + Header: v.Header, + URL: v.URL.String(), + } + } + return m, nil +} + func parseTemplateArg(value string) (alias string, ds DataSource, err error) { alias, u, _ := strings.Cut(value, "=") if u == "" { diff --git a/internal/config/types_test.go b/internal/config/types_test.go index f4f66fc9..47942c2f 100644 --- a/internal/config/types_test.go +++ b/internal/config/types_test.go @@ -22,10 +22,10 @@ remote: url: https://example.com/foo/bar/helloworld.tmpl header: Accept: [text/plain, text/template]` - out := map[string]DataSource{} + out := Templates{} err := yaml.Unmarshal([]byte(in), &out) require.NoError(t, err) - assert.EqualValues(t, map[string]DataSource{ + assert.EqualValues(t, Templates{ "t": {URL: mustURL("foo/bar/helloworld.tmpl")}, "templatedir": {URL: mustURL("templatedir/")}, "dir": {URL: mustURL("foo/bar/")}, @@ -36,9 +36,32 @@ remote: }, }, out) + // legacy array format + in = `- t=foo/bar/helloworld.tmpl +- templatedir/ +- dir=foo/bar/ +- mytemplate.t +- remote=https://example.com/foo/bar/helloworld.tmpl` + out = Templates{} + err = yaml.Unmarshal([]byte(in), &out) + require.NoError(t, err) + assert.EqualValues(t, Templates{ + "t": {URL: mustURL("foo/bar/helloworld.tmpl")}, + "templatedir/": {URL: mustURL("templatedir/")}, + "dir": {URL: mustURL("foo/bar/")}, + "mytemplate.t": {URL: mustURL("mytemplate.t")}, + "remote": {URL: mustURL("https://example.com/foo/bar/helloworld.tmpl")}, + }, out) + // invalid format in = `"neither an array nor a map"` - out = map[string]DataSource{} + out = Templates{} + err = yaml.Unmarshal([]byte(in), &out) + assert.Error(t, err) + + // invalid URL + in = `- t="not a:valid url"` + out = Templates{} err = yaml.Unmarshal([]byte(in), &out) assert.Error(t, err) } diff --git a/internal/tests/integration/config_test.go b/internal/tests/integration/config_test.go index 5c414bff..00ab4f89 100644 --- a/internal/tests/integration/config_test.go +++ b/internal/tests/integration/config_test.go @@ -262,6 +262,20 @@ templates: assertSuccess(t, o, e, err, "12345") } +func TestConfig_ConfigTemplatesSupportsArray(t *testing.T) { + tmpDir := setupConfigTest(t) + + // TODO: remove this test once the array format is no longer supported + writeConfig(t, tmpDir, `in: '{{ template "t1" (dict "testValue" "12345") }}' +templates: + - t1=t1.tmpl +`) + writeFile(t, tmpDir, "t1.tmpl", `{{ .testValue }}`) + + o, e, err := cmd(t).withDir(tmpDir.Path()).run() + assertSuccess(t, o, e, err, "12345") +} + func TestConfig_MissingKeyDefault(t *testing.T) { tmpDir := setupConfigTest(t) writeConfig(t, tmpDir, `inputFiles: [in] |
