hashicorp / hashicorp/consul-template
Non-deterministic failures due to file-loading behavior and nil inputs in once mode
- Dominant language
- Go
- Stars
- 4.8k
- Forks
- 801
- Avg merge
- 4h 5m
- Merged PRs (30d)
- 6
Description
Assume a template such as:
```
{{ $a := file "fileA.json" | parseJSON }}
{{ $b := file "fileB.json" | parseJSON }}
{{ if $a.enabled }}
{{ if $b.foo | regexMatch "test*" }}
{{ end }}
{{ end }}
```
w/ fileA.json containing `{ "enabled": true }` and fileB.json containing `{ "foo": "test" }`
When repeatedly run with `consul-template -once ...`, this will non-deterministically succeed or fail:
- Over thousands of runs it appears to succeed ~83% of the time on my machine.
- If the two file-loading lines are reversed, the success rate drops to 36%.
My guess is that there are two things happening:
1. `regexMatch` (and other functions) do not support nil inputs. This is clearly reproduced w/ e.g. `{{ if ("{}" | parseJSON).foo | regexMatch "test*" }}{{ end }}`. This failure does NOT occur with `eq` or other operators that tolerate nil inputs.
2. Even in Once mode, multiple rendering passes are occurring. That is, is isn't waiting for both fileA and fileB to load. The failures occur when a rendering pass occurs where fileA was loaded but fileB was not yet loaded.
I do see the following note at https://github.com/hashicorp/consul-template/blob/main/docs/templating-language.md#parsejson:
> Note: Consul Template evaluates the template multiple times, and on the first evaluation the value of the key will be empty (because no data has been loaded yet). This means that templates must guard against empty responses.
That would explain the behavior here... but at https://github.com/hashicorp/consul-template/blob/main/docs/modes.md#once-mode it also says:
> In Once mode, Consul Template will wait for all dependencies to be rendered.
...although perhaps a file isn't a dependency for this context.
In any case, this behavior feels extremely surprising at least for Once mode and IMO if not a bug still probably deserves a bit more than a small comment in parseJSON.
Possible suggestions:
1. Change `regexMatch` and other functions to tolerance nil inputs.
2. Make file-loading behavior deterministic in once mode.
3. If existing behavior is correct, consider further describing the behavior in `parseJSON` and also functions that may be impacted by it.
Contributor guide
Research direction
Start by reproducing the once-mode template with the file-loading and nil-input cases described in the issue, then read docs/templating-language.md#parsejson and docs/modes.md#once-mode. Determine whether the intended fix is deterministic dependency handling, nil tolerance, or documentation; the issue does not define a single acceptance criterion.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100