Add a new envsubst operator to replace environment variables in strings (#1082)

This commit is contained in:
Samuel Cormier-Iijima
2022-01-25 19:33:30 +11:00
committed by GitHub
parent 9b1a7bf451
commit 8195ff8b9b
8 changed files with 152 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
# Envsubst
This operator is used to replace environment variables in strings using [envsubst](https://github.com/a8m/envsubst).
To replace environment variables across all values in a document, this can be used with the recursive descent operator
as follows:
```bash
yq eval '(.. | select(tag == "!!str)) |= envsubst' file.yaml
```
## Replace strings with envsubst
Running
```bash
myenv="cat" yq eval --null-input '"the ${myenv} meows" | envsubst'
```
will output
```yaml
the cat meows
```
## Replace strings with envsubst, missing variables
Running
```bash
myenv="cat" yq eval --null-input '"the ${myenvnonexisting} meows" | envsubst'
```
will output
```yaml
the meows
```
## Replace strings with envsubst, missing variables with defaults
Running
```bash
myenv="cat" yq eval --null-input '"the ${myenvnonexisting-dog} meows" | envsubst'
```
will output
```yaml
the dog meows
```
## Replace string environment variable in document
Given a sample.yml file of:
```yaml
v: ${myenv}
```
then
```bash
myenv="cat meow" yq eval '.v |= envsubst' sample.yml
```
will output
```yaml
v: cat meow
```
@@ -0,0 +1,10 @@
# Envsubst
This operator is used to replace environment variables in strings using [envsubst](https://github.com/a8m/envsubst).
To replace environment variables across all values in a document, this can be used with the recursive descent operator
as follows:
```bash
yq eval '(.. | select(tag == "!!str")) |= envsubst' file.yaml
```
+2
View File
@@ -472,6 +472,8 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`strenv\([^\)]+\)`), envOp(true))
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
lexer.Add([]byte(`envsubst`), opToken(envsubstOpType))
lexer.Add([]byte(`\[`), literalToken(openCollect, false))
lexer.Add([]byte(`\]\??`), literalToken(closeCollect, true))
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
+2
View File
@@ -131,6 +131,8 @@ var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler:
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator}
var envsubstOpType = &operationType{Type: "ENVSUBST", NumArgs: 0, Precedence: 50, Handler: envsubstOperator}
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator}
+32
View File
@@ -0,0 +1,32 @@
package yqlib
import (
"container/list"
"fmt"
envsubst "github.com/a8m/envsubst"
yaml "gopkg.in/yaml.v3"
)
func envsubstOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
node := unwrapDoc(candidate.Node)
if node.Tag != "!!str" {
log.Warning("EnvSubstOperator, env name:", node.Tag, node.Value)
return Context{}, fmt.Errorf("cannot substitute with %v, can only substitute strings. Hint: Most often you'll want to use '|=' over '=' for this operation", node.Tag)
}
value, err := envsubst.String(node.Value)
if err != nil {
return Context{}, err
}
targetNode := &yaml.Node{Kind: yaml.ScalarNode, Value: value, Tag: "!!str"}
result := candidate.CreateReplacement(targetNode)
results.PushBack(result)
}
return context.ChildContext(results), nil
}
+48
View File
@@ -0,0 +1,48 @@
package yqlib
import (
"testing"
)
var envsubstOperatorScenarios = []expressionScenario{
{
description: "Replace strings with envsubst",
environmentVariable: "cat",
expression: `"the ${myenv} meows" | envsubst`,
expected: []string{
"D0, P[], (!!str)::the cat meows\n",
},
},
{
description: "Replace strings with envsubst, missing variables",
environmentVariable: "cat",
expression: `"the ${myenvnonexisting} meows" | envsubst`,
expected: []string{
"D0, P[], (!!str)::the meows\n",
},
},
{
description: "Replace strings with envsubst, missing variables with defaults",
environmentVariable: "cat",
expression: `"the ${myenvnonexisting-dog} meows" | envsubst`,
expected: []string{
"D0, P[], (!!str)::the dog meows\n",
},
},
{
description: "Replace string environment variable in document",
environmentVariable: "cat meow",
document: "{v: \"${myenv}\"}",
expression: `.v |= envsubst`,
expected: []string{
"D0, P[], (doc)::{v: \"cat meow\"}\n",
},
},
}
func TestEnvSubstOperatorScenarios(t *testing.T) {
for _, tt := range envsubstOperatorScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "envsubst", envsubstOperatorScenarios)
}