From e74216f4f98f407b51fc09dddd9f77b39cea6cf0 Mon Sep 17 00:00:00 2001 From: Brad Davidson Date: Fri, 18 Oct 2024 20:31:51 +0000 Subject: [PATCH] Unlink existing CNI bin symlinks Fixes "file exists" error when upgrading k3s. Signed-off-by: Brad Davidson --- cmd/k3s/main.go | 58 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/cmd/k3s/main.go b/cmd/k3s/main.go index 0b3cad81d423..52d5cefbf62e 100644 --- a/cmd/k3s/main.go +++ b/cmd/k3s/main.go @@ -30,6 +30,10 @@ var criDefaultConfigPath = "/etc/crictl.yaml" // main entrypoint for the k3s multicall binary func main() { + if findDebug(os.Args) { + logrus.SetLevel(logrus.DebugLevel) + } + dataDir := findDataDir(os.Args) // Handle direct invocation via symlink alias (multicall binary behavior) @@ -87,6 +91,24 @@ func main() { } } +// findDebug reads debug settings from the environment, CLI args, and config file. +func findDebug(args []string) bool { + debug, _ := strconv.ParseBool(os.Getenv(version.ProgramUpper + "_DEBUG")) + if debug { + return debug + } + fs := pflag.NewFlagSet("debug-set", pflag.ContinueOnError) + fs.ParseErrorsWhitelist.UnknownFlags = true + fs.SetOutput(io.Discard) + fs.BoolVarP(&debug, "debug", "", false, "(logging) Turn on debug logs") + fs.Parse(args) + if debug { + return debug + } + debug, _ = strconv.ParseBool(configfilearg.MustFindString(args, "debug")) + return debug +} + // findDataDir reads data-dir settings from the environment, CLI args, and config file. // If not found, the default will be used, which varies depending on whether // k3s is being run as root or not. @@ -280,11 +302,6 @@ func extract(dataDir string) (string, error) { return "", err } - // Rename the new directory into place, before updating symlinks - if err := os.Rename(tempDest, dir); err != nil { - return "", err - } - // Create a stable CNI bin dir and place it first in the path so that users have a // consistent location to drop their own CNI plugin binaries. cniPath := filepath.Join(dataDir, "data", "cni") @@ -292,19 +309,38 @@ func extract(dataDir string) (string, error) { if err := os.MkdirAll(cniPath, 0755); err != nil { return "", err } + // Create symlink that points at the cni multicall binary itself + logrus.Debugf("Creating symlink %s -> %s", filepath.Join(cniPath, "cni"), cniBin) + os.Remove(filepath.Join(cniPath, "cni")) if err := os.Symlink(cniBin, filepath.Join(cniPath, "cni")); err != nil { return "", err } // Find symlinks that point to the cni multicall binary, and clone them in the stable CNI bin dir. - ents, err := os.ReadDir(filepath.Join(dir, "bin")) + // Non-symlink plugins in the stable CNI bin dir will not be overwritten, to allow users to replace our + // CNI plugins with their own versions if they want. Note that the cni multicall binary itself is always + // symlinked into the stable bin dir and should not be replaced. + ents, err := os.ReadDir(filepath.Join(tempDest, "bin")) if err != nil { return "", err } for _, ent := range ents { if info, err := ent.Info(); err == nil && info.Mode()&fs.ModeSymlink != 0 { - if target, err := os.Readlink(filepath.Join(dir, "bin", ent.Name())); err == nil && target == "cni" { - if err := os.Symlink(cniBin, filepath.Join(cniPath, ent.Name())); err != nil { + if target, err := os.Readlink(filepath.Join(tempDest, "bin", ent.Name())); err == nil && target == "cni" { + src := filepath.Join(cniPath, ent.Name()) + // Check if plugin already exists in stable CNI bin dir + if info, err := os.Lstat(src); err == nil { + if info.Mode()&fs.ModeSymlink != 0 { + // Exists and is a symlink, remove it so we can create a new symlink for the new bin. + os.Remove(src) + } else { + // Not a symlink, leave it alone + logrus.Debugf("Not replacing non-symlink CNI plugin %s with mode %O", src, info.Mode()) + continue + } + } + logrus.Debugf("Creating symlink %s -> %s", src, cniBin) + if err := os.Symlink(cniBin, src); err != nil { return "", err } } @@ -324,6 +360,12 @@ func extract(dataDir string) (string, error) { return "", err } + // Rename the new directory into place after updating symlinks, so that the k3s binary check at the start + // of this function only succeeds if everything else has been completed successfully. + if err := os.Rename(tempDest, dir); err != nil { + return "", err + } + return dir, nil }