gomplate: Gomplate produce output file when parsing fails

Subject of the issue

gomplate using an input template with reference to an environment variable that doesn’t exist, still writes an output file.

Your environment

  • gomplate version 3.5.0

Steps to reproduce

$ gomplate -i 'fail: {{ .Env.BOGUS }}' -o fail.yml
template: <arg>:1:13: executing "<arg>" at <.Env.BOGUS>: map has no entry for key "BOGUS"

Expected behaviour

$ stat -f "%N" fail.yml
stat: fail.yml: stat: No such file or directory
$ test -f fail.yml || echo $?
1

Actual behaviour

$ stat -f "%N" fail.yml
fail.yml
$ test -f fail.yml && echo $?
0

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Comments: 16 (11 by maintainers)

Most upvoted comments

I’m interested in this feature as well, for the same reasons others mentioned.

Buffering into memory is pretty common, even when piping. It all depends on the nature of the tool: jq will buffer in order to parse, sort/uniq/wc must do the same (for obvious reasons), etc. gomplate doing the same is fine, but I’m not so sure it should be the default behavior (an opt-in flag would be just as good, at least for me).

Regarding the multiple input/output issue, I think the result should be consistent with multiple independent invocations of gomplate with a single input/output, as long as final exit status correctly reflects any intermediate failures.

I was surprised by this behaviour. I’m using Gomplate to generate config files for services. If there is an error, Gomplate will clobber the (previously working) config file with an invalid one.

I expect instead - at least with a config option - that the content can be written to a temporary file and atomically renamed to the existing file to avoid such surprises.

I’m not sure about the flag name, but something like the following may be more clean and easy to reason about

$ gomplate -f template.tpl -stdin -- my_binary

where stdin stands for “pass as input to the command I’ll execute on success”

I’d thought about that when I wrote that feature, and opted to ignore it at that point - but I’m definitely open to it! Can you open a separate issue for that? 🙂

This is good from a Unix philosophy, don’t get me wrong, but this make quite difficult to use gomplate with pipes. […] EDIT: After some thinking, I realized that the fact that the gomplate “stream” could be incomplete may be not very adherent to Unix philosophy

Though I’m generally a fan of the Unix philosophy, I’ve never made any special effort to make gomplate with it… So any similarities are purely coincidental and unintentional 😉

What may be possible is a feature like the GOMPLATE_SUPPRESS_EMPTY one, where you could set an environment variable to change its behaviour to buffer rendering and not write to the output stream when errors are encountered. The challenge is what to do when outputting multiple files - I suppose it would have to output file-by-file…

Or maybe the behaviour could be changed in the next major version of gomplate (v4) 🤔…

I’m going to re-open this since I’m now thinking this may be a good change in default behaviour, though until the next major release it would be opt-in. The tradeoff (slower time to any data being written) is probably worth it!

Hi @valepert, thanks for logging this, and sorry for the slow response - I’ve been on vacation 😉

This is the expected behaviour. Because the template is parsed and interpreted simultaneously, gomplate creates the output file before it starts to render the template.

Also, this is a useful behaviour when troubleshooting template errors, especially with larger templates, since the content that successfully rendered will all be present in the output.

One way to accomplish what you’re looking for is to delete the file on failure:

$ gomplate -i 'fail: {{ .Env.BOGUS }}' -o fail.yml || rm -f fail.yml

This way, fail.yml will never be present when gomplate fails for any reason.

A slightly different approach, without requiring a shell, would be to try to resolve environment variables up front, and then use the GOMPLATE_SUPPRESS_EMPTY environment variable to cause the output file to not be written on failures:

$ GOMPLATE_SUPPRESS_EMPTY=true gomplate -i '{{ $bogus := .Env.BOGUS -}}
fail: {{ $bogus }}' -o fail.yml
template: <arg>:1:17: executing "<arg>" at <.Env.BOGUS>: map has no entry for key "BOGUS"
$ ls fail.yml
ls: cannot access 'fail.yml': No such file or directory

This works because the template fails before any output has been rendered.

I’m going to close this issue now, since this is working as designed. Feel free to re-open though, if you feel strongly about this, or if the suggested workarounds don’t work 😉