From fe9f49987b20905295059176c4f73a84463f4354 Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Sat, 26 Sep 2026 13:17:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(felis):=20PATH=20=E7=BC=BA=20/usr/local/bin?= =?UTF-8?q?=20=E6=97=B6=E8=A1=A5=E4=B8=8A=E4=BB=A5=E6=89=BE=E5=88=B0=20k3s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/felis/main.go | 24 ++++++++++++++++++++++++ cmd/felis/main_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 cmd/felis/main_test.go 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") + } +}