Evaluate template conditional guards before expanding other variables - #252
Merged
Merged
Conversation
A JobTemplate/TableTemplate guarded with {{var==value}} (or {{var!=value}})
should render only when the condition holds, and skip entirely otherwise. But
the renderer expanded placeholders in text order, so a guard placed after
another variable did not protect it: the earlier variable was resolved first.
This breaks the "two templates with mirroring guards" pattern. A Flink SQL job
template and a Flink Beam job template can both be installed, guarded on
li.flink.app.type==SQL and ==BEAM respectively. When deploying a Beam job, the
SQL template must be skipped — but because {{flinksql}} appears before its
{{li.flink.app.type==SQL}} guard, the SQL body was generated first and could
throw (e.g. failing type validation on a cast the Beam path doesn't care about),
breaking the deployment instead of cleanly skipping the template.
Parse the template once into an ordered list of literal/placeholder tokens, then
render in two passes over that list: evaluate every conditional guard first
(regardless of position) and skip the whole template if any fails, then expand
the remaining {{var}} / {{var:default}} values. Guards can now sit anywhere.
The token model also lets rendering append directly to a StringBuilder, dropping
the Matcher.appendReplacement/quoteReplacement escaping dance. Verified
byte-for-byte equivalent to the previous renderer on the real Flink SQL/Beam
templates and edge cases (multiline values, `$`/`\`, defaults, transforms,
adjacent placeholders); the only intended behavior change is the guard ordering.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jogrogan
enabled auto-merge (squash)
September 16, 2026 01:11
Code Coverage
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A
JobTemplate/TableTemplateguarded with{{var==value}}(or{{var!=value}}) is meant to render only when the condition holds and be skipped entirely otherwise. ButSimpleTemplate.render()expanded{{...}}placeholders in text order, so a guard placed after another variable did not protect it — the earlier variable was resolved first.This breaks the "two templates with mirroring guards" pattern. With a Flink SQL job template and a Flink Beam job template both installed (guarded on
li.flink.app.type==SQLand==BEAM), deploying a Beam job should skip the SQL template. But because{{flinksql}}appears before its{{li.flink.app.type==SQL}}guard, the SQL body was generated first and could throw (e.g. failing type validation on a cast the Beam path doesn't care about), breaking the deployment instead of cleanly skipping the template.Fix
Parse the template once into an ordered list of literal/placeholder tokens, then render in two passes over that list:
return null) before expanding anything else.{{var}}/{{var:default}}values.Guards can now sit anywhere in the template and reliably protect expensive or fail-prone variables.
The token model also lets rendering append directly to a
StringBuilder, dropping theMatcher.appendReplacement/quoteReplacementescaping.Validation
TemplateTest, +11): guard-before/after a throwing variable, multi-guard fail-fast, plus refactor-safety ($/\literal rendering, adjacent placeholders, empty template, guard-only template). All green.flink-template.yaml/flink-beam-template.yamlshapes (multiline bodies,$in SQL, guards passing/failing) and edge inputs — byte-for-byte identical. The only intended behavior change is the guard ordering.:hoptimator-util,:hoptimator-jdbc,:hoptimator-k8sunit suites green.Docs
docs/kubernetes/templates.md— the "guard can sit anywhere" note now states the real guarantee: guards are evaluated before any other variable, so a guard reliably protects a variable that appears earlier in the text.