Skip to content

Commit

Permalink
feat: add exec-plugin argument and environment support
Browse files Browse the repository at this point in the history
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 <matthias.riegler@ankorstore.com>
  • Loading branch information
xvzf committed Sep 5, 2023
1 parent 169fdd7 commit 570c8bc
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 6 deletions.
4 changes: 4 additions & 0 deletions kyaml/fn/runtime/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ type Filter struct {
// Args are the arguments to the executable
Args []string `yaml:"args,omitempty"`

// Env is exposed to the environment
Env []string `yaml:"env,omitempty"`

// WorkingDir is the working directory that the executable
// should run in
WorkingDir string
Expand All @@ -35,6 +38,7 @@ func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {

func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
cmd := exec.Command(c.Path, c.Args...) //nolint:gosec
cmd.Env = append(os.Environ(), c.Env...)
cmd.Stdin = reader
cmd.Stdout = writer
cmd.Stderr = os.Stderr
Expand Down
5 changes: 3 additions & 2 deletions kyaml/fn/runtime/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ metadata:
},
expectedError: "",
instance: exec.Filter{
Path: "sed",
Args: []string{"s/Deployment/StatefulSet/g"},
Path: "sh",
Env: []string{"TARGET=StatefulSet"},
Args: []string{"-c", `sed "s/Deployment/$TARGET/g"`},
WorkingDir: wd,
},
},
Expand Down
6 changes: 6 additions & 0 deletions kyaml/fn/runtime/runtimeutil/functiontypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ type FunctionSpec struct {

type ExecSpec struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`

// Args is a slice of args that will be passed as arguments to script
Args []string `json:"args,omitempty" yaml:"args,omitempty"`

// Env is a slice of env string that will be exposed to container
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
}

// ContainerSpec defines a spec for running a function as a container
Expand Down
30 changes: 26 additions & 4 deletions kyaml/runfn/runfn.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ func (r RunFns) getFunctionsFromFunctions() ([]kio.Filter, error) {
return r.getFunctionFilters(true, r.Functions...)
}

// mergeContainerEnv will merge the envs specified by command line (imperative) and config
// file (declarative). If they have same key, the imperative value will be respected.
// mergeContainerEnv is container-specific and will merge the envs specified by command line (imperative)
// and config file (declarative). If they have same key, the imperative value will be respected.
func (r RunFns) mergeContainerEnv(envs []string) []string {
imperative := runtimeutil.NewContainerEnvFromStringSlice(r.Env)
declarative := runtimeutil.NewContainerEnvFromStringSlice(envs)
Expand All @@ -303,8 +303,28 @@ func (r RunFns) mergeContainerEnv(envs []string) []string {
return declarative.Raw()
}

func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
[]kio.Filter, error) {
// mergeExecEnv will merge the envs specified by command line (imperative) and config
// file (declarative). If they have same key, the imperative value will be respected.
func (r RunFns) mergeExecEnv(envs []string) []string {
envMap := map[string]string{}

for _, env := range append(envs, r.Env...) {
res := strings.Split(env, "=")
if len(res) == 2 {
envMap[res[0]] = res[1]
}
}

mergedEnv := []string{}
for key, value := range envMap {
mergedEnv = append(mergedEnv, fmt.Sprintf("%s=%s", key, value))
}
// Sort the envs to make the output deterministic
sort.Strings(mergedEnv)
return mergedEnv
}

func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) ([]kio.Filter, error) {
var fltrs []kio.Filter
for i := range fns {
api := fns[i]
Expand Down Expand Up @@ -535,6 +555,8 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode, currentUser
if r.EnableExec && spec.Exec.Path != "" {
ef := &exec.Filter{
Path: spec.Exec.Path,
Args: spec.Exec.Args,
Env: r.mergeExecEnv(spec.Exec.Env),
WorkingDir: r.WorkingDir,
}

Expand Down
52 changes: 52 additions & 0 deletions kyaml/runfn/runfn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1371,3 +1371,55 @@ func TestRunFns_mergeContainerEnv(t *testing.T) {
})
}
}

func TestRunFns_mergeExecEnv(t *testing.T) {
testcases := []struct {
name string
instance RunFns
inputEnvs []string
expect []string
}{
{
name: "all empty",
instance: RunFns{},
expect: []string{},
},
{
name: "empty command line envs",
instance: RunFns{},
inputEnvs: []string{"foo=bar"},
expect: []string{"foo=bar"},
},
{
name: "empty declarative envs",
instance: RunFns{
Env: []string{"foo=bar"},
},
expect: []string{"foo=bar"},
},
{
name: "same key",
instance: RunFns{
Env: []string{"foo=bar"},
},
inputEnvs: []string{"foo=bar1"},
expect: []string{"foo=bar"},
},
{
name: "same exported key",
instance: RunFns{
Env: []string{"foo=bar"},
},
inputEnvs: []string{"foo1=bar1"},
expect: []string{"foo1=bar1", "foo=bar"},
},
}

for i := range testcases {
tc := testcases[i]
t.Run(tc.name, func(t *testing.T) {
envs := tc.instance.mergeExecEnv(tc.inputEnvs)
assert.Equal(t, tc.expect, envs)
})
}
}

0 comments on commit 570c8bc

Please sign in to comment.