Merged envsubst into env operators

This commit is contained in:
Mike Farah 2022-01-27 09:20:53 +11:00
parent 8195ff8b9b
commit a9e7f669d0
8 changed files with 134 additions and 147 deletions

View File

@ -1,6 +1,21 @@
# Env Variable Operators # Env Variable Operators
This operator is used to handle environment variables usage in path expressions. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read. Note that there are two forms, `env` which will parse the environment variable as a yaml (be it a map, array, string, number of boolean) and `strenv` which will always parse the argument as a string. These operators are used to handle environment variables usage in expressions and documents. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read.
There are three operators:
- `env` which takes a single environment variable name and parse the variable as a yaml node (be it a map, array, string, number of boolean)
- `strenv` which also takes a single environment variable name, and always parses the variable as a string.
- `envsubst` which you pipe strings into and it interpolates environment variables in strings using [envsubst](https://github.com/a8m/envsubst).
## Tip
To replace environment variables across all values in a document, `envsubst` can be used with the recursive descent operator
as follows:
```bash
yq eval '(.. | select(tag == "!!str")) |= envsubst' file.yaml
```
## Read string environment variable ## Read string environment variable
Running Running
@ -77,3 +92,47 @@ will output
meow meow
``` ```
## 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
```

View File

@ -1,55 +0,0 @@
# 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
```

View File

@ -1,3 +1,18 @@
# Env Variable Operators # Env Variable Operators
This operator is used to handle environment variables usage in path expressions. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read. Note that there are two forms, `env` which will parse the environment variable as a yaml (be it a map, array, string, number of boolean) and `strenv` which will always parse the argument as a string. These operators are used to handle environment variables usage in expressions and documents. While environment variables can, of course, be passed in via your CLI with string interpolation, this often comes with complex quote escaping and can be tricky to write and read.
There are three operators:
- `env` which takes a single environment variable name and parse the variable as a yaml node (be it a map, array, string, number of boolean)
- `strenv` which also takes a single environment variable name, and always parses the variable as a string.
- `envsubst` which you pipe strings into and it interpolates environment variables in strings using [envsubst](https://github.com/a8m/envsubst).
## Tip
To replace environment variables across all values in a document, `envsubst` can be used with the recursive descent operator
as follows:
```bash
yq eval '(.. | select(tag == "!!str")) |= envsubst' file.yaml
```

View File

@ -1,10 +0,0 @@
# 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
```

View File

@ -1,10 +1,12 @@
package yqlib package yqlib
import ( import (
"container/list"
"fmt" "fmt"
"os" "os"
"strings" "strings"
envsubst "github.com/a8m/envsubst"
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
) )
@ -47,3 +49,26 @@ func envOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
return context.SingleChildContext(target), nil return context.SingleChildContext(target), nil
} }
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
}

View File

@ -62,6 +62,39 @@ var envOperatorScenarios = []expressionScenario{
"D0, P[cat], (!!str)::meow\n", "D0, P[cat], (!!str)::meow\n",
}, },
}, },
{
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 TestEnvOperatorScenarios(t *testing.T) { func TestEnvOperatorScenarios(t *testing.T) {

View File

@ -1,32 +0,0 @@
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
}

View File

@ -1,48 +0,0 @@
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)
}