From 72194feae77870190471d220898e2ccf1c7443a5 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Tue, 4 Apr 2023 08:42:23 -0400 Subject: [PATCH] New plugin args option Signed-off-by: Dave Henderson --- Dockerfile | 2 +- docs/content/config.md | 37 ++++++++++++++++++++-- internal/config/configfile.go | 6 ++-- internal/config/configfile_test.go | 7 ++-- internal/tests/integration/plugins_test.go | 14 ++++++++ plugins.go | 8 +++++ plugins_test.go | 24 ++++++++++++++ 7 files changed, 89 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71403790e..5183eb90b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ COPY --from=build /bin/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT} /gompl ENTRYPOINT [ "/gomplate" ] -FROM alpine:3.17 AS gomplate-alpine +FROM alpine:3.17.3 AS gomplate-alpine ARG VCS_REF ARG TARGETOS diff --git a/docs/content/config.md b/docs/content/config.md index 9e488e8a1..dbdc5ce0b 100644 --- a/docs/content/config.md +++ b/docs/content/config.md @@ -304,15 +304,19 @@ See [`--plugin`](../usage/#plugin). A map that configures custom functions for use in the templates. The key is the name of the function, and the value configures the plugin. The value is a map containing the command (`cmd`) and the options `pipe` (boolean) and `timeout` -(duration). +(duration). A list of optional arguments to always pass to the plugin can be set +with `args` (array of strings). -Alternatively, the value can be a string, which sets `cmd`. +Alternatively, the value can be a string, which sets only `cmd`. ```yaml -in: '{{ "hello world" | figlet | lolcat }}' +in: '{{ "world" | figlet | lolcat }}' plugins: figlet: cmd: /usr/local/bin/figlet + args: + - oh + - hello pipe: true timeout: 1s lolcat: /home/hairyhenderson/go/bin/lolcat @@ -322,6 +326,33 @@ plugins: The path to the plugin executable (or script) to run. +### `args` + +An array of optional arguments to always pass to the plugin. These arguments +will be passed _before_ any arguments provided in the template. + +For example: + +```yaml +plugins: + echo: + cmd: /bin/echo + args: + - foo + - bar +``` + +With this template: +``` +{{ echo "baz" }} +``` + +Will result the command being called like this: + +```console +$ /bin/echo foo bar baz +``` + ### `pipe` Whether to pipe the final argument of the template function to the plugin's diff --git a/internal/config/configfile.go b/internal/config/configfile.go index f85bdaebf..e9b17671b 100644 --- a/internal/config/configfile.go +++ b/internal/config/configfile.go @@ -153,8 +153,9 @@ func (d DataSource) mergeFrom(o DataSource) DataSource { type PluginConfig struct { Cmd string - Timeout time.Duration - Pipe bool + Args []string `yaml:"args,omitempty"` + Timeout time.Duration `yaml:"timeout,omitempty"` + Pipe bool `yaml:"pipe,omitempty"` } // UnmarshalYAML - satisfy the yaml.Umarshaler interface - plugin configs can @@ -178,6 +179,7 @@ func (p *PluginConfig) UnmarshalYAML(value *yaml.Node) error { type raw struct { Cmd string + Args []string Timeout time.Duration Pipe bool } diff --git a/internal/config/configfile_test.go b/internal/config/configfile_test.go index 5dfadbe2f..cacbed4b8 100644 --- a/internal/config/configfile_test.go +++ b/internal/config/configfile_test.go @@ -626,8 +626,9 @@ pluginTimeout: 500ms c = &Config{ Plugins: map[string]PluginConfig{ "foo": { - Cmd: "bar", - Pipe: true, + Cmd: "bar", + Timeout: 1 * time.Second, + Pipe: true, }, }, } @@ -635,7 +636,7 @@ pluginTimeout: 500ms plugins: foo: cmd: bar - timeout: 0s + timeout: 1s pipe: true ` diff --git a/internal/tests/integration/plugins_test.go b/internal/tests/integration/plugins_test.go index fbab35d8b..82d571ffa 100644 --- a/internal/tests/integration/plugins_test.go +++ b/internal/tests/integration/plugins_test.go @@ -111,3 +111,17 @@ plugins: assert.Equal(t, "", e) assert.Equal(t, "=hi=thZrZ", o) } + +func TestPlugins_Args(t *testing.T) { + tmpDir := setupPluginsTest(t) + + writeConfig(t, tmpDir, `in: '{{ echo "world" }}' +plugins: + echo: + cmd: echo + args: [ oh, hello ] +`) + + o, e, err := cmd(t).withDir(tmpDir.Path()).run() + assertSuccess(t, o, e, err, "oh hello world\n") +} diff --git a/plugins.go b/plugins.go index 518f0142d..50e34bf55 100644 --- a/plugins.go +++ b/plugins.go @@ -37,6 +37,7 @@ func bindPlugins(ctx context.Context, cfg *config.Config, funcMap template.FuncM Timeout: timeout, Pipe: v.Pipe, Stderr: cfg.Stderr, + Args: v.Args, }) } @@ -49,6 +50,10 @@ type PluginOpts struct { // Defaults to os.Stderr. Stderr io.Writer + // Args are additional arguments to pass to the plugin. These precede any + // arguments passed to the plugin function at runtime. + Args []string + // Timeout is the maximum amount of time to wait for the plugin to complete. // Defaults to 5 seconds. Timeout time.Duration @@ -74,6 +79,7 @@ func PluginFunc(ctx context.Context, cmd string, opts PluginOpts) func(...interf plugin := &plugin{ ctx: ctx, path: cmd, + args: opts.Args, timeout: timeout, pipe: opts.Pipe, stderr: stderr, @@ -87,6 +93,7 @@ type plugin struct { ctx context.Context stderr io.Writer path string + args []string timeout time.Duration pipe bool } @@ -122,6 +129,7 @@ func findPowershell() string { func (p *plugin) run(args ...interface{}) (interface{}, error) { a := conv.ToStrings(args...) + a = append(p.args, a...) name, a := p.buildCommand(a) diff --git a/plugins_test.go b/plugins_test.go index c38c57016..8a8eb18ab 100644 --- a/plugins_test.go +++ b/plugins_test.go @@ -73,6 +73,30 @@ func TestRun(t *testing.T) { require.NoError(t, err) assert.Equal(t, "", stderr.String()) assert.Equal(t, "foo", strings.TrimSpace(out.(string))) + + p = &plugin{ + ctx: ctx, + timeout: 500 * time.Millisecond, + stderr: stderr, + path: "echo", + args: []string{"foo", "bar"}, + } + out, err = p.run() + require.NoError(t, err) + assert.Equal(t, "", stderr.String()) + assert.Equal(t, "foo bar", strings.TrimSpace(out.(string))) + + p = &plugin{ + ctx: ctx, + timeout: 500 * time.Millisecond, + stderr: stderr, + path: "echo", + args: []string{"foo", "bar"}, + } + out, err = p.run("baz", "qux") + require.NoError(t, err) + assert.Equal(t, "", stderr.String()) + assert.Equal(t, "foo bar baz qux", strings.TrimSpace(out.(string))) } func ExamplePluginFunc() {