diff --git a/cmd/felis/main.go b/cmd/felis/main.go index bd1015b..3956d7f 100644 --- a/cmd/felis/main.go +++ b/cmd/felis/main.go @@ -6,8 +6,32 @@ package main import ( "os" + "path/filepath" ) func main() { + ensureHostBinDirOnPath() os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) } + +// hostBinDir is where deploy/bootstrap.sh installs felis, k3s and cloudflared. +const hostBinDir = "/usr/local/bin" + +// ensureHostBinDirOnPath appends hostBinDir to PATH when it is missing, so the +// k3s and cloudflared this binary execs are found beside it. sudo's secure_path +// on EL leaves /usr/local/bin out: `sudo /usr/local/bin/felis db backup` would +// otherwise run with no k3s to reach the database's pod through. Appended, so a +// PATH that names another k3s first keeps it. +func ensureHostBinDirOnPath() { + path := os.Getenv("PATH") + for _, dir := range filepath.SplitList(path) { + if dir == hostBinDir { + return + } + } + if path == "" { + os.Setenv("PATH", hostBinDir) + return + } + os.Setenv("PATH", path+string(os.PathListSeparator)+hostBinDir) +} diff --git a/cmd/felis/main_test.go b/cmd/felis/main_test.go new file mode 100644 index 0000000..e32ee59 --- /dev/null +++ b/cmd/felis/main_test.go @@ -0,0 +1,33 @@ +package main + +import ( + "os" + "regexp" + "testing" +) + +func TestEnsureHostBinDirOnPath(t *testing.T) { + for _, c := range []struct{ in, want string }{ + {"/usr/sbin:/usr/bin", "/usr/sbin:/usr/bin:/usr/local/bin"}, + {"/usr/local/bin:/usr/bin", "/usr/local/bin:/usr/bin"}, + {"/opt/k3s:/usr/bin:/usr/local/bin", "/opt/k3s:/usr/bin:/usr/local/bin"}, + {"", "/usr/local/bin"}, + } { + t.Setenv("PATH", c.in) + ensureHostBinDirOnPath() + if got := os.Getenv("PATH"); got != c.want { + t.Errorf("PATH %q became %q, want %q", c.in, got, c.want) + } + } +} + +// run is what the tests drive, so the PATH fix must sit in main, before it. +func TestMainFixesPathBeforeRunning(t *testing.T) { + b, err := os.ReadFile("main.go") + if err != nil { + t.Fatal(err) + } + if !regexp.MustCompile(`func main\(\) \{\n\tensureHostBinDirOnPath\(\)\n\tos\.Exit\(run\(`).Match(b) { + t.Fatal("main does not call ensureHostBinDirOnPath before run") + } +}