mirror of https://github.com/dexidp/dex.git
Browse Source
See go doc ./cmd/docker-entrypoint for why. Signed-off-by: Andrew Keesler <akeesler@vmware.com>pull/2378/head
5 changed files with 208 additions and 35 deletions
@ -0,0 +1,92 @@
|
||||
// Package main provides a utility program to launch the Dex container process with an optional
|
||||
// templating step (provided by gomplate).
|
||||
//
|
||||
// This was originally written as a shell script, but we rewrote it as a Go program so that it could
|
||||
// run as a raw binary in a distroless container.
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"os/exec" |
||||
"strings" |
||||
"syscall" |
||||
) |
||||
|
||||
func main() { |
||||
// Note that this docker-entrypoint program is args[0], and it is provided with the true process
|
||||
// args.
|
||||
args := os.Args[1:] |
||||
|
||||
if err := run(args, realExec, realWhich); err != nil { |
||||
fmt.Println("error:", err.Error()) |
||||
os.Exit(1) |
||||
} |
||||
} |
||||
|
||||
func realExec(fork bool, 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]) |
||||
if err != nil { |
||||
return fmt.Errorf("cannot lookup path for command %s: %w", args[0], err) |
||||
} |
||||
|
||||
if err := syscall.Exec(argv0, args, os.Environ()); err != nil { |
||||
return fmt.Errorf("cannot exec command %s (%q): %w", args, argv0, err) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func realWhich(path string) string { |
||||
fullPath, err := exec.LookPath(path) |
||||
if err != nil { |
||||
return "" |
||||
} |
||||
return fullPath |
||||
} |
||||
|
||||
func run(args []string, execFunc func(bool, ...string) error, whichFunc func(string) string) error { |
||||
if args[0] != "dex" && args[0] != whichFunc("dex") { |
||||
return execFunc(false, args...) |
||||
} |
||||
|
||||
if args[1] != "serve" { |
||||
return execFunc(false, args...) |
||||
} |
||||
|
||||
newArgs := []string{} |
||||
for _, tplCandidate := range args { |
||||
if hasSuffixes(tplCandidate, ".tpl", ".tmpl", ".yaml") { |
||||
tmpFile, err := os.CreateTemp("/tmp", "dex.config.yaml-*") |
||||
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 |
||||
} |
||||
|
||||
newArgs = append(newArgs, tmpFile.Name()) |
||||
} else { |
||||
newArgs = append(newArgs, tplCandidate) |
||||
} |
||||
} |
||||
|
||||
return execFunc(false, newArgs...) |
||||
} |
||||
|
||||
func hasSuffixes(s string, suffixes ...string) bool { |
||||
for _, suffix := range suffixes { |
||||
if strings.HasSuffix(s, suffix) { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
@ -0,0 +1,113 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"strings" |
||||
"testing" |
||||
) |
||||
|
||||
type execArgs struct { |
||||
fork bool |
||||
argPrefixes []string |
||||
} |
||||
|
||||
func TestRun(t *testing.T) { |
||||
tests := []struct { |
||||
name string |
||||
args []string |
||||
execReturns error |
||||
whichReturns string |
||||
wantExecArgs []execArgs |
||||
wantErr error |
||||
}{ |
||||
{ |
||||
name: "executable not dex", |
||||
args: []string{"tuna", "fish"}, |
||||
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"tuna", "fish"}}}, |
||||
}, |
||||
{ |
||||
name: "executable is full path to dex", |
||||
args: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}, |
||||
whichReturns: "/usr/local/bin/dex", |
||||
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}}}, |
||||
}, |
||||
{ |
||||
name: "command is not serve", |
||||
args: []string{"dex", "marshmallow", "zelda"}, |
||||
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "marshmallow", "zelda"}}}, |
||||
}, |
||||
{ |
||||
name: "no templates", |
||||
args: []string{"dex", "serve", "config.yaml.not-a-template"}, |
||||
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}}, |
||||
}, |
||||
{ |
||||
name: "no templates", |
||||
args: []string{"dex", "serve", "config.yaml.not-a-template"}, |
||||
wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}}, |
||||
}, |
||||
{ |
||||
name: ".tpl template", |
||||
args: []string{"dex", "serve", "config.tpl"}, |
||||
wantExecArgs: []execArgs{ |
||||
{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", |
||||
args: []string{"dex", "serve", "config.tmpl"}, |
||||
wantExecArgs: []execArgs{ |
||||
{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", |
||||
args: []string{"dex", "serve", "some/path/config.yaml"}, |
||||
wantExecArgs: []execArgs{ |
||||
{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 { |
||||
t.Run(test.name, func(t *testing.T) { |
||||
var gotExecForks []bool |
||||
var gotExecArgs [][]string |
||||
fakeExec := func(fork bool, args ...string) error { |
||||
gotExecForks = append(gotExecForks, fork) |
||||
gotExecArgs = append(gotExecArgs, args) |
||||
return test.execReturns |
||||
} |
||||
|
||||
fakeWhich := func(_ string) string { return test.whichReturns } |
||||
|
||||
gotErr := run(test.args, fakeExec, fakeWhich) |
||||
if (test.wantErr == nil) != (gotErr == nil) { |
||||
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) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func execArgsMatch(wantExecArgs []execArgs, gotForks []bool, gotExecArgs [][]string) bool { |
||||
if len(wantExecArgs) != len(gotForks) { |
||||
return false |
||||
} |
||||
|
||||
for i := range wantExecArgs { |
||||
if wantExecArgs[i].fork != gotForks[i] { |
||||
return false |
||||
} |
||||
for j := range wantExecArgs[i].argPrefixes { |
||||
if !strings.HasPrefix(gotExecArgs[i][j], wantExecArgs[i].argPrefixes[j]) { |
||||
return false |
||||
} |
||||
} |
||||
} |
||||
|
||||
return true |
||||
} |
||||
@ -1,32 +0,0 @@
|
||||
#!/bin/sh -e |
||||
|
||||
### Usage: /docker-entrypoint.sh <command> <args> |
||||
function main() { |
||||
executable=$1 |
||||
command=$2 |
||||
|
||||
if [[ "$executable" != "dex" ]] && [[ "$executable" != "$(which dex)" ]]; then |
||||
exec $@ |
||||
fi |
||||
|
||||
if [[ "$command" != "serve" ]]; then |
||||
exec $@ |
||||
fi |
||||
|
||||
for tpl_candidate in $@ ; do |
||||
case "$tpl_candidate" in |
||||
*.tpl|*.tmpl|*.yaml) |
||||
tmp_file=$(mktemp /tmp/dex.config.yaml-XXXXXX) |
||||
gomplate -f "$tpl_candidate" -o "$tmp_file" |
||||
|
||||
args="${args} ${tmp_file}" |
||||
;; |
||||
*) |
||||
args="${args} ${tpl_candidate}" |
||||
;; |
||||
esac |
||||
done |
||||
exec $args |
||||
} |
||||
|
||||
main $@ |
||||
Loading…
Reference in new issue