Skip to content

Commit 61ce5ed

Browse files
committed
feat: add --k8s-aware support to envsubst
This routes the envsubst changes to go through to a new `kustomize.SubstituteEnvVariables` to the pkgs `kustomize` module which supports the `kustomize.toolkit.fluxcd.io/substitute` annotation. Requires fluxcd/pkg#1176 Fixes fluxcd#5108 Signed-off-by: Jaakko Sirén <jaakko@craci.com>
1 parent 7d27a26 commit 61ce5ed

8 files changed

Lines changed: 235 additions & 18 deletions

File tree

cmd/flux/envsubst.go

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ limitations under the License.
1717
package main
1818

1919
import (
20-
"bufio"
2120
"fmt"
21+
"io"
22+
"os"
2223

2324
"github.com/fluxcd/pkg/envsubst"
25+
"github.com/fluxcd/pkg/kustomize"
2426
"github.com/spf13/cobra"
2527
)
2628

@@ -37,38 +39,50 @@ to replicate the behavior of the Flux Kustomization post-build substitutions.`),
3739
3840
# Run env var substitutions and error out if a variable is not set
3941
kustomize build . | flux envsubst --strict
42+
43+
# Run env var substitutions, skipping resources with substitute disabled
44+
kustomize build . | flux envsubst --strict --k8s-aware
4045
`,
4146
RunE: runEnvsubstCmd,
4247
}
4348

4449
type envsubstFlags struct {
45-
strict bool
50+
strict bool
51+
k8sAware bool
4652
}
4753

4854
var envsubstArgs envsubstFlags
4955

5056
func init() {
5157
envsubstCmd.Flags().BoolVar(&envsubstArgs.strict, "strict", false,
5258
"fail if a variable without a default value is declared in the input but is missing from the environment")
59+
envsubstCmd.Flags().BoolVar(&envsubstArgs.k8sAware, "k8s-aware", false,
60+
"treat the input as multi-doc Kubernetes YAML and skip substitution for resources "+
61+
"annotated or labeled with kustomize.toolkit.fluxcd.io/substitute: disabled")
5362
rootCmd.AddCommand(envsubstCmd)
5463
}
5564

5665
func runEnvsubstCmd(cmd *cobra.Command, args []string) error {
57-
stdin := bufio.NewScanner(rootCmd.InOrStdin())
58-
stdout := bufio.NewWriter(rootCmd.OutOrStdout())
59-
for stdin.Scan() {
60-
line, err := envsubst.EvalEnv(stdin.Text(), envsubstArgs.strict)
61-
if err != nil {
62-
return err
63-
}
64-
_, err = fmt.Fprintln(stdout, line)
65-
if err != nil {
66-
return err
67-
}
68-
err = stdout.Flush()
69-
if err != nil {
70-
return err
71-
}
66+
data, err := io.ReadAll(rootCmd.InOrStdin())
67+
if err != nil {
68+
return err
69+
}
70+
71+
mapping := envsubst.Getenv
72+
if envsubstArgs.strict {
73+
mapping = os.LookupEnv
7274
}
73-
return nil
75+
76+
var result string
77+
if envsubstArgs.k8sAware {
78+
result, err = kustomize.SubstituteEnvVariables(string(data), mapping)
79+
} else {
80+
result, err = envsubst.Eval(string(data), mapping)
81+
}
82+
if err != nil {
83+
return err
84+
}
85+
86+
_, err = fmt.Fprint(rootCmd.OutOrStdout(), result)
87+
return err
7488
}

cmd/flux/envsubst_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,80 @@ func TestEnvsubst_Strinct(t *testing.T) {
4848
g.Expect(err).To(HaveOccurred())
4949
g.Expect(err.Error()).To(ContainSubstring("variable not set (strict mode)"))
5050
}
51+
52+
func TestEnvsubst_K8sAware(t *testing.T) {
53+
tests := []struct {
54+
name string
55+
args string
56+
env map[string]string
57+
input string
58+
gold string
59+
wantErr string
60+
}{
61+
{
62+
name: "annotation disabled",
63+
args: "envsubst --k8s-aware",
64+
env: map[string]string{"REPO_NAME": "test"},
65+
input: "testdata/envsubst/k8s-aware.yaml",
66+
gold: "testdata/envsubst/k8s-aware.gold",
67+
},
68+
{
69+
name: "label disabled",
70+
args: "envsubst --k8s-aware",
71+
input: "testdata/envsubst/k8s-aware-label.yaml",
72+
gold: "testdata/envsubst/k8s-aware-label.gold",
73+
},
74+
{
75+
name: "strict skips disabled resources",
76+
args: "envsubst --strict --k8s-aware",
77+
env: map[string]string{"REPO_NAME": "test"},
78+
input: "testdata/envsubst/k8s-aware.yaml",
79+
gold: "testdata/envsubst/k8s-aware.gold",
80+
},
81+
{
82+
name: "strict errors on enabled resource with missing var",
83+
args: "envsubst --strict --k8s-aware",
84+
input: "testdata/envsubst/k8s-aware.yaml",
85+
wantErr: "variable not set (strict mode)",
86+
},
87+
{
88+
name: "bash script in disabled resource",
89+
args: "envsubst --k8s-aware",
90+
env: map[string]string{"APP_NAME": "myapp"},
91+
input: "testdata/envsubst/k8s-aware-bash.yaml",
92+
gold: "testdata/envsubst/k8s-aware-bash.gold",
93+
},
94+
{
95+
name: "strict with bash script in disabled resource",
96+
args: "envsubst --strict --k8s-aware",
97+
env: map[string]string{"APP_NAME": "myapp"},
98+
input: "testdata/envsubst/k8s-aware-bash.yaml",
99+
gold: "testdata/envsubst/k8s-aware-bash.gold",
100+
},
101+
}
102+
103+
for _, tt := range tests {
104+
t.Run(tt.name, func(t *testing.T) {
105+
g := NewWithT(t)
106+
107+
for k, v := range tt.env {
108+
t.Setenv(k, v)
109+
}
110+
111+
input, err := os.ReadFile(tt.input)
112+
g.Expect(err).NotTo(HaveOccurred())
113+
114+
output, err := executeCommandWithIn(tt.args, bytes.NewReader(input))
115+
if tt.wantErr != "" {
116+
g.Expect(err).To(HaveOccurred())
117+
g.Expect(err.Error()).To(ContainSubstring(tt.wantErr))
118+
return
119+
}
120+
g.Expect(err).NotTo(HaveOccurred())
121+
122+
expected, err := os.ReadFile(tt.gold)
123+
g.Expect(err).NotTo(HaveOccurred())
124+
g.Expect(output).To(Equal(string(expected)))
125+
})
126+
}
127+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: myapp
5+
namespace: default
6+
data:
7+
key: value
8+
---
9+
apiVersion: v1
10+
kind: ConfigMap
11+
metadata:
12+
name: init-scripts
13+
namespace: default
14+
annotations:
15+
kustomize.toolkit.fluxcd.io/substitute: disabled
16+
data:
17+
setup.sh: |
18+
#!/bin/bash
19+
process_args() {
20+
echo "First arg: $1"
21+
echo "Second arg: $2"
22+
echo "All args: $@"
23+
local name=${1:-default}
24+
local count=${2:-0}
25+
for i in $(seq 1 $count); do
26+
echo "$i: processing $name"
27+
done
28+
}
29+
process_args "$@"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: ${APP_NAME}
5+
namespace: ${APP_NAMESPACE:=default}
6+
data:
7+
key: value
8+
---
9+
apiVersion: v1
10+
kind: ConfigMap
11+
metadata:
12+
name: init-scripts
13+
namespace: default
14+
annotations:
15+
kustomize.toolkit.fluxcd.io/substitute: disabled
16+
data:
17+
setup.sh: |
18+
#!/bin/bash
19+
process_args() {
20+
echo "First arg: $1"
21+
echo "Second arg: $2"
22+
echo "All args: $@"
23+
local name=${1:-default}
24+
local count=${2:-0}
25+
for i in $(seq 1 $count); do
26+
echo "$i: processing $name"
27+
done
28+
}
29+
process_args "$@"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: grafana-dashboard
5+
namespace: monitoring
6+
labels:
7+
kustomize.toolkit.fluxcd.io/substitute: disabled
8+
data:
9+
dashboard.json: '{"panels": [{"datasource": "${DataSource}"}]}'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: grafana-dashboard
5+
namespace: monitoring
6+
labels:
7+
kustomize.toolkit.fluxcd.io/substitute: disabled
8+
data:
9+
dashboard.json: '{"panels": [{"datasource": "${DataSource}"}]}'
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: test
5+
namespace: flux-system
6+
data:
7+
key: value
8+
---
9+
apiVersion: v1
10+
kind: ConfigMap
11+
metadata:
12+
name: grafana-dashboard
13+
namespace: monitoring
14+
annotations:
15+
kustomize.toolkit.fluxcd.io/substitute: disabled
16+
data:
17+
dashboard.json: '{"panels": [{"datasource": "${DataSource}"}]}'
18+
---
19+
apiVersion: v1
20+
kind: ConfigMap
21+
metadata:
22+
name: test-config
23+
namespace: flux-system
24+
data:
25+
region: eu-central-1
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: ${REPO_NAME}
5+
namespace: ${REPO_NAMESPACE:=flux-system}
6+
data:
7+
key: value
8+
---
9+
apiVersion: v1
10+
kind: ConfigMap
11+
metadata:
12+
name: grafana-dashboard
13+
namespace: monitoring
14+
annotations:
15+
kustomize.toolkit.fluxcd.io/substitute: disabled
16+
data:
17+
dashboard.json: '{"panels": [{"datasource": "${DataSource}"}]}'
18+
---
19+
apiVersion: v1
20+
kind: ConfigMap
21+
metadata:
22+
name: ${REPO_NAME}-config
23+
namespace: ${REPO_NAMESPACE:=flux-system}
24+
data:
25+
region: ${CLUSTER_REGION:=eu-central-1}

0 commit comments

Comments
 (0)