Skip to content
Snippets Groups Projects
Commit 43956db7 authored by Maksim Nabokikh's avatar Maksim Nabokikh Committed by m.nabokikh
Browse files

Change workdir for gomplate (#3684)

Workaround to run gomplate from a non-root directory in distroless images, because gomplate tries to access CWD on start.
See: https://github.com/hairyhenderson/gomplate/pull/2202



Signed-off-by: default avatarm.nabokikh <maksim.nabokikh@flant.com>
parent 54fb570f
Branches v2.41.x
Tags v2.41.1
No related merge requests found
...@@ -22,20 +22,13 @@ func main() { ...@@ -22,20 +22,13 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if err := run(args, realExec, realWhich); err != nil { if err := run(args, realExec, realWhich, realGomplate); err != nil {
fmt.Println("error:", err.Error()) fmt.Println("error:", err.Error())
os.Exit(1) os.Exit(1)
} }
} }
func realExec(fork bool, args ...string) error { func realExec(args ...string) error {
if fork {
if output, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
return fmt.Errorf("cannot fork/exec command %s: %w (output: %q)", args, err, string(output))
}
return nil
}
argv0, err := exec.LookPath(args[0]) argv0, err := exec.LookPath(args[0])
if err != nil { if err != nil {
return fmt.Errorf("cannot lookup path for command %s: %w", args[0], err) return fmt.Errorf("cannot lookup path for command %s: %w", args[0], err)
...@@ -56,34 +49,49 @@ func realWhich(path string) string { ...@@ -56,34 +49,49 @@ func realWhich(path string) string {
return fullPath return fullPath
} }
func run(args []string, execFunc func(bool, ...string) error, whichFunc func(string) string) error { func realGomplate(path string) (string, error) {
tmpFile, err := os.CreateTemp("/tmp", "dex.config.yaml-*")
if err != nil {
return "", fmt.Errorf("cannot create temp file: %w", err)
}
cmd := exec.Command("gomplate", "-f", path, "-o", tmpFile.Name())
// TODO(nabokihms): Workaround to run gomplate from a non-root directory in distroless images
// gomplate tries to access CWD on start, see: https://github.com/hairyhenderson/gomplate/pull/2202
cmd.Dir = "/etc/dex"
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("error executing gomplate: %w, (output: %q)", err, string(output))
}
return tmpFile.Name(), nil
}
func run(args []string, execFunc func(...string) error, whichFunc func(string) string, gomplateFunc func(string) (string, error)) error {
if args[0] != "dex" && args[0] != whichFunc("dex") { if args[0] != "dex" && args[0] != whichFunc("dex") {
return execFunc(false, args...) return execFunc(args...)
} }
if args[1] != "serve" { if args[1] != "serve" {
return execFunc(false, args...) return execFunc(args...)
} }
newArgs := []string{} newArgs := []string{}
for _, tplCandidate := range args { for _, tplCandidate := range args {
if hasSuffixes(tplCandidate, ".tpl", ".tmpl", ".yaml") { if hasSuffixes(tplCandidate, ".tpl", ".tmpl", ".yaml") {
tmpFile, err := os.CreateTemp("/tmp", "dex.config.yaml-*") fileName, err := gomplateFunc(tplCandidate)
if err != nil { if err != nil {
return fmt.Errorf("cannot create temp file: %w", err)
}
if err := execFunc(true, "gomplate", "-f", tplCandidate, "-o", tmpFile.Name()); err != nil {
return err return err
} }
newArgs = append(newArgs, tmpFile.Name()) newArgs = append(newArgs, fileName)
} else { } else {
newArgs = append(newArgs, tplCandidate) newArgs = append(newArgs, tplCandidate)
} }
} }
return execFunc(false, newArgs...) return execFunc(newArgs...)
} }
func hasSuffixes(s string, suffixes ...string) bool { func hasSuffixes(s string, suffixes ...string) bool {
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
) )
type execArgs struct { type execArgs struct {
fork bool gomplate bool
argPrefixes []string argPrefixes []string
} }
...@@ -16,98 +16,89 @@ func TestRun(t *testing.T) { ...@@ -16,98 +16,89 @@ func TestRun(t *testing.T) {
args []string args []string
execReturns error execReturns error
whichReturns string whichReturns string
wantExecArgs []execArgs wantExecArgs execArgs
wantErr error wantErr error
}{ }{
{ {
name: "executable not dex", name: "executable not dex",
args: []string{"tuna", "fish"}, args: []string{"tuna", "fish"},
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"tuna", "fish"}}}, wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"tuna", "fish"}},
}, },
{ {
name: "executable is full path to dex", name: "executable is full path to dex",
args: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}, args: []string{"/usr/local/bin/dex", "marshmallow", "zelda"},
whichReturns: "/usr/local/bin/dex", whichReturns: "/usr/local/bin/dex",
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}}}, wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}},
}, },
{ {
name: "command is not serve", name: "command is not serve",
args: []string{"dex", "marshmallow", "zelda"}, args: []string{"dex", "marshmallow", "zelda"},
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "marshmallow", "zelda"}}}, wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "marshmallow", "zelda"}},
}, },
{ {
name: "no templates", name: "no templates",
args: []string{"dex", "serve", "config.yaml.not-a-template"}, args: []string{"dex", "serve", "config.yaml.not-a-template"},
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}}, wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}},
}, },
{ {
name: "no templates", name: "no templates",
args: []string{"dex", "serve", "config.yaml.not-a-template"}, args: []string{"dex", "serve", "config.yaml.not-a-template"},
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}}, wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}},
}, },
{ {
name: ".tpl template", name: ".tpl template",
args: []string{"dex", "serve", "config.tpl"}, args: []string{"dex", "serve", "config.tpl"},
wantExecArgs: []execArgs{ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
{fork: true, argPrefixes: []string{"gomplate", "-f", "config.tpl", "-o", "/tmp/dex.config.yaml-"}},
{fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
}, },
{ {
name: ".tmpl template", name: ".tmpl template",
args: []string{"dex", "serve", "config.tmpl"}, args: []string{"dex", "serve", "config.tmpl"},
wantExecArgs: []execArgs{ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
{fork: true, argPrefixes: []string{"gomplate", "-f", "config.tmpl", "-o", "/tmp/dex.config.yaml-"}},
{fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
}, },
{ {
name: ".yaml template", name: ".yaml template",
args: []string{"dex", "serve", "some/path/config.yaml"}, args: []string{"dex", "serve", "some/path/config.yaml"},
wantExecArgs: []execArgs{ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
{fork: true, argPrefixes: []string{"gomplate", "-f", "some/path/config.yaml", "-o", "/tmp/dex.config.yaml-"}},
{fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
var gotExecForks []bool var gotExecArgs []string
var gotExecArgs [][]string var runsGomplate bool
fakeExec := func(fork bool, args ...string) error {
gotExecForks = append(gotExecForks, fork) fakeExec := func(args ...string) error {
gotExecArgs = append(gotExecArgs, args) gotExecArgs = append(args, gotExecArgs...)
return test.execReturns return test.execReturns
} }
fakeWhich := func(_ string) string { return test.whichReturns } fakeWhich := func(_ string) string { return test.whichReturns }
gotErr := run(test.args, fakeExec, fakeWhich) fakeGomplate := func(file string) (string, error) {
runsGomplate = true
return "/tmp/dex.config.yaml-", nil
}
gotErr := run(test.args, fakeExec, fakeWhich, fakeGomplate)
if (test.wantErr == nil) != (gotErr == nil) { if (test.wantErr == nil) != (gotErr == nil) {
t.Errorf("wanted error %s, got %s", test.wantErr, gotErr) t.Errorf("wanted error %s, got %s", test.wantErr, gotErr)
} }
if !execArgsMatch(test.wantExecArgs, gotExecForks, gotExecArgs) {
t.Errorf("wanted exec args %+v, got %+v %+v", test.wantExecArgs, gotExecForks, gotExecArgs) if !execArgsMatch(test.wantExecArgs, runsGomplate, gotExecArgs) {
t.Errorf("wanted exec args %+v (running gomplate: %+v), got %+v (running gomplate: %+v)",
test.wantExecArgs.argPrefixes, test.wantExecArgs.gomplate, gotExecArgs, runsGomplate)
} }
}) })
} }
} }
func execArgsMatch(wantExecArgs []execArgs, gotForks []bool, gotExecArgs [][]string) bool { func execArgsMatch(wantExecArgs execArgs, gomplate bool, gotExecArgs []string) bool {
if len(wantExecArgs) != len(gotForks) { if wantExecArgs.gomplate != gomplate {
return false return false
} }
for i := range wantExecArgs.argPrefixes {
for i := range wantExecArgs { if !strings.HasPrefix(gotExecArgs[i], wantExecArgs.argPrefixes[i]) {
if wantExecArgs[i].fork != gotForks[i] {
return false return false
} }
for j := range wantExecArgs[i].argPrefixes {
if !strings.HasPrefix(gotExecArgs[i][j], wantExecArgs[i].argPrefixes[j]) {
return false
}
}
} }
return true return true
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment