summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2024-01-25 21:14:06 -0500
committerGitHub <noreply@github.com>2024-01-25 21:14:06 -0500
commit2cc314740e4ce29739c667f0887448d6ee592542 (patch)
tree93e6768da61b137e95a1079e775117c238146a24 /internal
parent562bcd95dfb6fb14af7e0b5829beccff3636d2f1 (diff)
Revert "Remove support for deprecated key/value array form of template config" (#1979)
Diffstat (limited to 'internal')
-rw-r--r--internal/config/configfile.go7
-rw-r--r--internal/config/configfile_test.go14
-rw-r--r--internal/config/types.go78
-rw-r--r--internal/config/types_test.go29
-rw-r--r--internal/tests/integration/config_test.go14
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]