Skip to content

Commit

Permalink
Add ARM template escape sequence support
Browse files Browse the repository at this point in the history
  • Loading branch information
Craig Furman committed Jun 21, 2023
1 parent 340410d commit 7ff9c68
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 8 deletions.
7 changes: 6 additions & 1 deletion pkg/input/arm/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ func isARMTemplateExpression(input string) bool {
return strings.HasPrefix(input, "[") && strings.HasSuffix(input, "]")
}

// https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-expressions#escape-characters
func extractAndEscapeARMTemplate(input string) string {
// Remove brackets
input = input[1:(len(input) - 1)]
return input
return strings.ReplaceAll(
strings.ReplaceAll(
input, "[[", "[",
), "]]", "]",
)
}

type evaluationContext struct {
Expand Down
10 changes: 10 additions & 0 deletions pkg/input/arm/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ func TestEvalARMTemplateStrings(t *testing.T) {
input string
expected interface{}
}{
{
name: "returns inputs that are not ARM expressions",
input: "just a plain string, that is [not] and expression",
expected: "just a plain string, that is [not] and expression",
},
{
name: "replaces escape sequences inside ARM expressions",
input: "['[[string literal in brackets]], ''escaped single quotes''']",
expected: "[string literal in brackets], 'escaped single quotes'",
},
{
name: "concat string literals",
input: "[concat('foo', '-bar')]",
Expand Down
30 changes: 25 additions & 5 deletions pkg/input/arm/tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ func (t *tokenizer) next() token {
return dot{}
case '\'':
t.remaining = t.remaining[1:]
endOfStringLiteralIdx := strCharIndex(t.remaining, func(char rune) bool {
return char == '\''
})
tkn := stringLiteral{content: t.remaining[:endOfStringLiteralIdx]}
t.remaining = t.remaining[endOfStringLiteralIdx+1:]
tkn := stringLiteral{content: t.findEndOfStringLiteral()}
return tkn
}

Expand All @@ -79,6 +75,30 @@ func (t *tokenizer) next() token {
return identifier{name: raw}
}

// https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-expressions#escape-characters
// 2 single quotes are an escaped single quote
func (t *tokenizer) findEndOfStringLiteral() string {
ret := ""
for {
nextSingleQuoteIndex := strCharIndex(t.remaining, func(char rune) bool {
return char == '\''
})
ret += t.remaining[:nextSingleQuoteIndex]
t.remaining = t.remaining[nextSingleQuoteIndex:]
if len(t.remaining) < 2 || t.remaining[1] != '\'' {
// no escape sequence detected, pop the closing quote and return the
// literal
t.remaining = t.remaining[1:]
return ret
}

// escape sequence detected, push a single quote onto the string, pop the 2
// quotes, and continue
ret += "'"
t.remaining = t.remaining[2:]
}
}

func (t *tokenizer) chopLeadingWhitespace() {
t.remaining = strings.TrimLeftFunc(t.remaining, unicode.IsSpace)
}
Expand Down
11 changes: 9 additions & 2 deletions pkg/input/arm/tokenizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,19 @@ func TestTokenizesARMExpressions(t *testing.T) {
closeParen{},
},
},
{
name: "tokenizes expression containing string literals containing escaped single quotes",
input: "'some ''escaped'' ''quotes'''",
expected: []token{
stringLiteral{"some 'escaped' 'quotes'"},
},
},
{
name: "tokenizes expression containing string literals with special characters",
input: "'[some]' '(special, characters)'",
input: "'[some]' 'more(special, characters)'",
expected: []token{
stringLiteral{"[some]"},
stringLiteral{"(special, characters)"},
stringLiteral{"more(special, characters)"},
},
},
{
Expand Down

0 comments on commit 7ff9c68

Please sign in to comment.