Skip to content

Commit cc7a71c

Browse files
authored
feat: add exec-plugin argument and environment support (#5316)
* feat: add exec-plugin argument and environment support Previously, the documentation lead to think that this is working, but it's not been implemented. This PR is fixing this Signed-off-by: Matthias Riegler <[email protected]> * chore: disable linting for env var split Signed-off-by: Matthias Riegler <[email protected]> --------- Signed-off-by: Matthias Riegler <[email protected]>
1 parent 3be1af6 commit cc7a71c

File tree

5 files changed

+92
-5
lines changed

5 files changed

+92
-5
lines changed

kyaml/fn/runtime/exec/exec.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ type Filter struct {
2121
// Args are the arguments to the executable
2222
Args []string `yaml:"args,omitempty"`
2323

24+
// Env is exposed to the environment
25+
Env []string `yaml:"env,omitempty"`
26+
2427
// WorkingDir is the working directory that the executable
2528
// should run in
2629
WorkingDir string
@@ -35,6 +38,7 @@ func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
3538

3639
func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
3740
cmd := exec.Command(c.Path, c.Args...) //nolint:gosec
41+
cmd.Env = append(os.Environ(), c.Env...)
3842
cmd.Stdin = reader
3943
cmd.Stdout = writer
4044
cmd.Stderr = os.Stderr

kyaml/fn/runtime/exec/exec_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
func TestFunctionFilter_Filter(t *testing.T) {
1818
wd, err := os.Getwd()
1919
require.NoError(t, err)
20-
var tests = []struct {
20+
tests := []struct {
2121
name string
2222
input []string
2323
functionConfig string
@@ -57,8 +57,9 @@ metadata:
5757
},
5858
expectedError: "",
5959
instance: exec.Filter{
60-
Path: "sed",
61-
Args: []string{"s/Deployment/StatefulSet/g"},
60+
Path: "sh",
61+
Env: []string{"TARGET=StatefulSet"},
62+
Args: []string{"-c", `sed "s/Deployment/$TARGET/g"`},
6263
WorkingDir: wd,
6364
},
6465
},

kyaml/fn/runtime/runtimeutil/functiontypes.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ type FunctionSpec struct {
138138

139139
type ExecSpec struct {
140140
Path string `json:"path,omitempty" yaml:"path,omitempty"`
141+
142+
// Args is a slice of args that will be passed as arguments to script
143+
Args []string `json:"args,omitempty" yaml:"args,omitempty"`
144+
145+
// Env is a slice of env string that will be exposed to container
146+
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
141147
}
142148

143149
// ContainerSpec defines a spec for running a function as a container

kyaml/runfn/runfn.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ func (r RunFns) getFunctionsFromFunctions() ([]kio.Filter, error) {
281281
return r.getFunctionFilters(true, r.Functions...)
282282
}
283283

284-
// mergeContainerEnv will merge the envs specified by command line (imperative) and config
285-
// file (declarative). If they have same key, the imperative value will be respected.
284+
// mergeContainerEnv is container-specific and will merge the envs specified by command line (imperative)
285+
// and config file (declarative). If they have same key, the imperative value will be respected.
286286
func (r RunFns) mergeContainerEnv(envs []string) []string {
287287
imperative := runtimeutil.NewContainerEnvFromStringSlice(r.Env)
288288
declarative := runtimeutil.NewContainerEnvFromStringSlice(envs)
@@ -297,6 +297,28 @@ func (r RunFns) mergeContainerEnv(envs []string) []string {
297297
return declarative.Raw()
298298
}
299299

300+
// mergeExecEnv will merge the envs specified by command line (imperative) and config
301+
// file (declarative). If they have same key, the imperative value will be respected.
302+
func (r RunFns) mergeExecEnv(envs []string) []string {
303+
envMap := map[string]string{}
304+
305+
for _, env := range append(envs, r.Env...) {
306+
res := strings.Split(env, "=")
307+
//nolint:gomnd
308+
if len(res) == 2 {
309+
envMap[res[0]] = res[1]
310+
}
311+
}
312+
313+
mergedEnv := []string{}
314+
for key, value := range envMap {
315+
mergedEnv = append(mergedEnv, fmt.Sprintf("%s=%s", key, value))
316+
}
317+
// Sort the envs to make the output deterministic
318+
sort.Strings(mergedEnv)
319+
return mergedEnv
320+
}
321+
300322
func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
301323
[]kio.Filter, error) {
302324
var fltrs []kio.Filter
@@ -494,6 +516,8 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode, currentUser
494516
if r.EnableExec && spec.Exec.Path != "" {
495517
ef := &exec.Filter{
496518
Path: spec.Exec.Path,
519+
Args: spec.Exec.Args,
520+
Env: r.mergeExecEnv(spec.Exec.Env),
497521
WorkingDir: r.WorkingDir,
498522
}
499523

kyaml/runfn/runfn_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,3 +1282,55 @@ func TestRunFns_mergeContainerEnv(t *testing.T) {
12821282
})
12831283
}
12841284
}
1285+
1286+
func TestRunFns_mergeExecEnv(t *testing.T) {
1287+
testcases := []struct {
1288+
name string
1289+
instance RunFns
1290+
inputEnvs []string
1291+
expect []string
1292+
}{
1293+
{
1294+
name: "all empty",
1295+
instance: RunFns{},
1296+
expect: []string{},
1297+
},
1298+
{
1299+
name: "empty command line envs",
1300+
instance: RunFns{},
1301+
inputEnvs: []string{"foo=bar"},
1302+
expect: []string{"foo=bar"},
1303+
},
1304+
{
1305+
name: "empty declarative envs",
1306+
instance: RunFns{
1307+
Env: []string{"foo=bar"},
1308+
},
1309+
expect: []string{"foo=bar"},
1310+
},
1311+
{
1312+
name: "same key",
1313+
instance: RunFns{
1314+
Env: []string{"foo=bar"},
1315+
},
1316+
inputEnvs: []string{"foo=bar1"},
1317+
expect: []string{"foo=bar"},
1318+
},
1319+
{
1320+
name: "same exported key",
1321+
instance: RunFns{
1322+
Env: []string{"foo=bar"},
1323+
},
1324+
inputEnvs: []string{"foo1=bar1"},
1325+
expect: []string{"foo1=bar1", "foo=bar"},
1326+
},
1327+
}
1328+
1329+
for i := range testcases {
1330+
tc := testcases[i]
1331+
t.Run(tc.name, func(t *testing.T) {
1332+
envs := tc.instance.mergeExecEnv(tc.inputEnvs)
1333+
assert.Equal(t, tc.expect, envs)
1334+
})
1335+
}
1336+
}

0 commit comments

Comments
 (0)