From 8533b58d96d3fa9a9c8e0ba6cfa9cc1e29edd03f Mon Sep 17 00:00:00 2001 From: Tom Gehrke Date: Wed, 3 Apr 2024 20:51:11 +0200 Subject: [PATCH] implement some basic k8s distro detection --- cmd/node-installer/detect.go | 61 +++++++++++++++++++++++++++++++++ cmd/node-installer/install.go | 12 +++++-- cmd/node-installer/root.go | 2 +- cmd/node-installer/uninstall.go | 12 +++++-- internal/preset/preset.go | 55 +++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 cmd/node-installer/detect.go create mode 100644 internal/preset/preset.go diff --git a/cmd/node-installer/detect.go b/cmd/node-installer/detect.go new file mode 100644 index 0000000..848ef9c --- /dev/null +++ b/cmd/node-installer/detect.go @@ -0,0 +1,61 @@ +/* + Copyright The SpinKube Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package main + +import ( + "errors" + "fmt" + "log/slog" + + "github.com/spf13/afero" + "github.com/spinkube/runtime-class-manager/internal/preset" +) + +var containerdConfigLocations = map[string]preset.Settings{ + // Microk8s + "/var/snap/microk8s/current/args/containerd-template.toml": preset.MicroK8s, + // RKE2 + "/var/lib/rancher/rke2/agent/etc/containerd/config.toml": preset.RKE2, + // K3s + "/var/lib/rancher/k3s/agent/etc/containerd/config.toml": preset.K3s, + // default + "/etc/containerd/config.toml": preset.Default, +} + +func DetectDistro(config Config, hostFs afero.Fs) (preset.Settings, error) { + if config.Runtime.ConfigPath != "" { + // containerd config path has been set explicitly + if distro, ok := containerdConfigLocations[config.Runtime.ConfigPath]; ok { + return distro, nil + } + slog.Warn("could not determine distro from containerd config, falling back to defaults", "config", config.Runtime.ConfigPath) + return preset.Default.WithConfigPath(config.Runtime.ConfigPath), nil + } + + var errs []error + + for loc, distro := range containerdConfigLocations { + _, err := hostFs.Stat(loc) + if err == nil { + // config file found, return corresponding distro settings + return distro, nil + } + errs = append(errs, err) + } + + return preset.Settings{}, fmt.Errorf("failed to detect containerd config path: %w", errors.Join(errs...)) +} diff --git a/cmd/node-installer/install.go b/cmd/node-installer/install.go index 3ad6418..27ce0a1 100644 --- a/cmd/node-installer/install.go +++ b/cmd/node-installer/install.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spinkube/runtime-class-manager/internal/containerd" + "github.com/spinkube/runtime-class-manager/internal/preset" "github.com/spinkube/runtime-class-manager/internal/shim" ) @@ -36,9 +37,16 @@ var installCmd = &cobra.Command{ Run: func(_ *cobra.Command, _ []string) { rootFs := afero.NewOsFs() hostFs := afero.NewBasePathFs(rootFs, config.Host.RootPath) - restarter := containerd.NewRestarter() - if err := RunInstall(config, rootFs, hostFs, restarter); err != nil { + distro, err := DetectDistro(config, hostFs) + if err != nil { + slog.Error("failed to detect containerd config", "error", err) + os.Exit(1) + } + + config.Runtime.ConfigPath = distro.ConfigPath + + if err := RunInstall(config, rootFs, hostFs, distro.Restarter(preset.Env{ConfigPath: distro.ConfigPath, HostFs: hostFs})); err != nil { slog.Error("failed to install", "error", err) os.Exit(1) } diff --git a/cmd/node-installer/root.go b/cmd/node-installer/root.go index 3efbf46..42b1d8a 100644 --- a/cmd/node-installer/root.go +++ b/cmd/node-installer/root.go @@ -51,7 +51,7 @@ func Execute() { func init() { rootCmd.PersistentFlags().StringVarP(&config.Runtime.Name, "runtime", "r", "containerd", "Set the container runtime to configure (containerd, cri-o)") - rootCmd.PersistentFlags().StringVarP(&config.Runtime.ConfigPath, "runtime-config", "c", "/etc/containerd/config.toml", "Path to the runtime config file") + rootCmd.PersistentFlags().StringVarP(&config.Runtime.ConfigPath, "runtime-config", "c", "", "Path to the runtime config file. Will try to autodetect if left empty") rootCmd.PersistentFlags().StringVarP(&config.Kwasm.Path, "kwasm-path", "k", "/opt/kwasm", "Working directory for kwasm on the host") rootCmd.PersistentFlags().StringVarP(&config.Host.RootPath, "host-root", "H", "/", "Path to the host root path") } diff --git a/cmd/node-installer/uninstall.go b/cmd/node-installer/uninstall.go index aecbe0a..1b71ca0 100644 --- a/cmd/node-installer/uninstall.go +++ b/cmd/node-installer/uninstall.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" "github.com/spinkube/runtime-class-manager/internal/containerd" + "github.com/spinkube/runtime-class-manager/internal/preset" "github.com/spinkube/runtime-class-manager/internal/shim" ) @@ -36,9 +37,16 @@ var uninstallCmd = &cobra.Command{ Run: func(_ *cobra.Command, _ []string) { rootFs := afero.NewOsFs() hostFs := afero.NewBasePathFs(rootFs, config.Host.RootPath) - restarter := containerd.NewRestarter() - if err := RunUninstall(config, rootFs, hostFs, restarter); err != nil { + distro, err := DetectDistro(config, hostFs) + if err != nil { + slog.Error("failed to detect containerd config", "error", err) + os.Exit(1) + } + + config.Runtime.ConfigPath = distro.ConfigPath + + if err := RunUninstall(config, rootFs, hostFs, distro.Restarter(preset.Env{ConfigPath: distro.ConfigPath, HostFs: hostFs})); err != nil { slog.Error("failed to uninstall", "error", err) os.Exit(1) } diff --git a/internal/preset/preset.go b/internal/preset/preset.go new file mode 100644 index 0000000..dade9cd --- /dev/null +++ b/internal/preset/preset.go @@ -0,0 +1,55 @@ +package preset + +import ( + "errors" + "os" + + "github.com/spf13/afero" + "github.com/spinkube/runtime-class-manager/internal/containerd" +) + +type Settings struct { + ConfigPath string + Setup func(Env) error + Restarter func(Env) containerd.Restarter +} + +type Env struct { + HostFs afero.Fs + ConfigPath string +} + +var Default = Settings{ + ConfigPath: "/etc/containerd/config.toml", + Setup: func(_ Env) error { return nil }, + Restarter: func(_ Env) containerd.Restarter { return containerd.NewRestarter() }, +} + +func (s Settings) WithConfigPath(path string) Settings { + s.ConfigPath = path + return s +} + +func (s Settings) WithSetup(setup func(env Env) error) Settings { + s.Setup = setup + return s +} + +var MicroK8s = Default.WithConfigPath("/var/snap/microk8s/current/args/containerd-template.toml") + +var RKE2 = Default.WithConfigPath("/var/lib/rancher/rke2/agent/etc/containerd/config.toml.tmpl"). + WithSetup(func(env Env) error { + _, err := env.HostFs.Stat(env.ConfigPath) + if err == nil { + return nil + } + + if errors.Is(err, os.ErrNotExist) { + // TODO: Copy file from original file to new config file + return nil + } + + return err + }) + +var K3s = RKE2.WithConfigPath("/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl")