Consul
Execute custom scripts with Consul Template plugins
For some use cases, it may be necessary to write a plugin that offloads work to another system. This is especially useful for things that may not fit in the "standard library" of Consul Template, but still need to be shared across multiple instances.
Consul Template plugins must have the following API:
$ NAME [INPUT...]
NAME
- the name of the plugin - this is also the name of the binary, either a full path or just the program name. It will be executed in a shell with the inheritedPATH
so e.g. the plugincat
will run the first executablecat
that is found on thePATH
.INPUT
- input from the template. There will be one INPUT for every argument passed to theplugin
function. If the arguments contain whitespace, that whitespace will be passed as if the argument were quoted by the shell.
Important notes
Plugins execute user-provided scripts and pass in potentially sensitive data from Consul or Vault. Nothing is validated or protected by Consul Template, so all necessary precautions and considerations should be made by template authors
Plugin output must be returned as a string on
stdout
. Onlystdout
will be parsed for output. Be sure to log all errors and debugging messages ontostderr
to avoid errors when Consul Template returns the value. Note that output tostderr
will only be output if the plugin returns a non-zero exit code.Always
exit 0
or Consul Template will assume the plugin failed to execute.Ensure the empty input case is handled correctly (see Multi-phase execution)
Data piped into the plugin is appended after any parameters given explicitly. For example, {{ "sample-data" | plugin "my-plugin" "some-parameter"}}
will call
my-plugin some-parameter sample-data`.
Examples
The following example plugin removes any JSON keys that start with an underscore and returns the JSON string:
func main() {
arg := []byte(os.Args[1])
var parsed map[string]interface{}
if err := json.Unmarshal(arg, &parsed); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("err: %s", err))
os.Exit(1)
}
for k, _ := range parsed {
if string(k[0]) == "_" {
delete(parsed, k)
}
}
result, err := json.Marshal(parsed)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("err: %s", err))
os.Exit(1)
}
fmt.Fprintln(os.Stdout, fmt.Sprintf("%s", result))
os.Exit(0)
}