From b6c7da71b425e9d4b7d5ac886c69f6eadb62a3b4 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Fri, 20 Dec 2024 14:55:18 +0100 Subject: [PATCH 1/4] Compile users.LookupUID via build tags On Windows, there's no numeric UIDs, so using this there is futile. Signed-off-by: Tom Wieczorek --- internal/pkg/users/lookup_other.go | 29 ++++++++ internal/pkg/users/lookup_unix.go | 67 +++++++++++++++++++ .../{users_test.go => lookup_unix_test.go} | 7 +- internal/pkg/users/users.go | 44 ------------ 4 files changed, 97 insertions(+), 50 deletions(-) create mode 100644 internal/pkg/users/lookup_other.go create mode 100644 internal/pkg/users/lookup_unix.go rename internal/pkg/users/{users_test.go => lookup_unix_test.go} (90%) diff --git a/internal/pkg/users/lookup_other.go b/internal/pkg/users/lookup_other.go new file mode 100644 index 000000000000..ec86cfdb7112 --- /dev/null +++ b/internal/pkg/users/lookup_other.go @@ -0,0 +1,29 @@ +//go:build !unix + +/* +Copyright 2024 k0s 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 users + +import ( + "errors" + "fmt" + "runtime" +) + +func LookupUID(string) (int, error) { + return 0, fmt.Errorf("%w on %s", errors.ErrUnsupported, runtime.GOOS) +} diff --git a/internal/pkg/users/lookup_unix.go b/internal/pkg/users/lookup_unix.go new file mode 100644 index 000000000000..4cb1f617c269 --- /dev/null +++ b/internal/pkg/users/lookup_unix.go @@ -0,0 +1,67 @@ +//go:build unix + +/* +Copyright 2024 k0s 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 users + +import ( + "bytes" + "errors" + "fmt" + "os/exec" + "os/user" + "strconv" +) + +// Lookup looks up a user's UID by username. If the user cannot be found, the +// returned error is [ErrNotExist]. If an error is returned, the returned UID +// will be [UnknownUID]. +func LookupUID(name string) (int, error) { + var uid string + + if entry, err := user.Lookup(name); err != nil { + if !errors.Is(err, user.UnknownUserError(name)) { + return UnknownUID, err + } + + err = ErrNotExist + + // fallback to call external `id` in case NSS is used + out, idErr := exec.Command("id", "-u", name).Output() + if idErr != nil { + var exitErr *exec.ExitError + if errors.As(idErr, &exitErr) { + return UnknownUID, fmt.Errorf("%w (%w: %s)", err, idErr, bytes.TrimSpace(exitErr.Stderr)) + } + return UnknownUID, fmt.Errorf("%w (%w)", err, idErr) + } + + uid = string(bytes.TrimSpace(out)) + } else { + uid = entry.Uid + } + + parsedUID, err := strconv.Atoi(uid) + if err != nil { + return UnknownUID, fmt.Errorf("UID %q is not a decimal integer: %w", uid, err) + } + if parsedUID < 0 { + return UnknownUID, fmt.Errorf("UID is negative: %d", parsedUID) + } + + return parsedUID, nil +} diff --git a/internal/pkg/users/users_test.go b/internal/pkg/users/lookup_unix_test.go similarity index 90% rename from internal/pkg/users/users_test.go rename to internal/pkg/users/lookup_unix_test.go index befd9c6d84b8..1bf444bc429c 100644 --- a/internal/pkg/users/users_test.go +++ b/internal/pkg/users/lookup_unix_test.go @@ -18,17 +18,12 @@ package users import ( "os/exec" - "runtime" "testing" "github.com/stretchr/testify/assert" ) -func TestGetUID(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("No numeric user IDs on Windows") - } - +func TestLookupUID(t *testing.T) { uid, err := LookupUID("root") if assert.NoError(t, err, "Failed to get UID for root user") { assert.Equal(t, 0, uid, "root's UID is not 0?") diff --git a/internal/pkg/users/users.go b/internal/pkg/users/users.go index e633a9e17fd1..f8927424246d 100644 --- a/internal/pkg/users/users.go +++ b/internal/pkg/users/users.go @@ -17,12 +17,7 @@ limitations under the License. package users import ( - "bytes" "errors" - "fmt" - "os/exec" - "os/user" - "strconv" ) const ( @@ -39,42 +34,3 @@ const ( ) var ErrNotExist = errors.New("user does not exist") - -// Lookup looks up a user's UID by username. If the user cannot be found, the -// returned error is [ErrNotExist]. If an error is returned, the returned UID -// will be [UnknownUID]. -func LookupUID(name string) (int, error) { - var uid string - - if entry, err := user.Lookup(name); err != nil { - if !errors.Is(err, user.UnknownUserError(name)) { - return UnknownUID, err - } - - err = ErrNotExist - - // fallback to call external `id` in case NSS is used - out, idErr := exec.Command("id", "-u", name).Output() - if idErr != nil { - var exitErr *exec.ExitError - if errors.As(idErr, &exitErr) { - return UnknownUID, fmt.Errorf("%w (%w: %s)", err, idErr, bytes.TrimSpace(exitErr.Stderr)) - } - return UnknownUID, fmt.Errorf("%w (%w)", err, idErr) - } - - uid = string(bytes.TrimSpace(out)) - } else { - uid = entry.Uid - } - - parsedUID, err := strconv.Atoi(uid) - if err != nil { - return UnknownUID, fmt.Errorf("UID %q is not a decimal integer: %w", uid, err) - } - if parsedUID < 0 { - return UnknownUID, fmt.Errorf("UID is negative: %d", parsedUID) - } - - return parsedUID, nil -} From ae58fd0dd4e8d444f19fdd31ba1b5b2f19e89573 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Sat, 21 Dec 2024 00:52:53 +0100 Subject: [PATCH 2/4] Remove global StatusSocket variable ...and harmonize the status-socket flag in the status sub-command. The shared use of the StatusSocket variable, along with the non-empty default value in the status sub-command, caused the registration order of sub-commands to change the flag value for each sub-command, even if it wasn't set from the command line. This broke the configuration defaulting. Signed-off-by: Tom Wieczorek --- cmd/status/status.go | 3 +-- pkg/config/cli.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/status/status.go b/cmd/status/status.go index f797b6ebd033..e2294b9c483b 100644 --- a/cmd/status/status.go +++ b/cmd/status/status.go @@ -20,7 +20,6 @@ import ( "encoding/json" "fmt" "io" - "path/filepath" "github.com/k0sproject/k0s/pkg/component/status" "github.com/k0sproject/k0s/pkg/config" @@ -56,7 +55,7 @@ func NewStatusCmd() *cobra.Command { } cmd.PersistentFlags().StringVarP(&output, "out", "o", "", "sets type of output to json or yaml") - cmd.PersistentFlags().StringVar(&config.StatusSocket, "status-socket", filepath.Join(config.K0sVars.RunDir, "status.sock"), "Full file path to the socket file.") + cmd.PersistentFlags().String("status-socket", "", "Full file path to the socket file. (default: /status.sock)") cmd.AddCommand(NewStatusSubCmdComponents()) return cmd } diff --git a/pkg/config/cli.go b/pkg/config/cli.go index a567b5a3c8ab..874e32149cf6 100644 --- a/pkg/config/cli.go +++ b/pkg/config/cli.go @@ -36,7 +36,6 @@ var ( CfgFile string Debug bool DebugListenOn string - StatusSocket string K0sVars CfgVars workerOpts WorkerOptions Verbose bool @@ -200,7 +199,7 @@ func GetPersistentFlagSet() *pflag.FlagSet { flagset.BoolVarP(&Debug, "debug", "d", false, "Debug logging (default: false)") flagset.BoolVarP(&Verbose, "verbose", "v", false, "Verbose logging (default: false)") flagset.String("data-dir", constant.DataDirDefault, "Data Directory for k0s. DO NOT CHANGE for an existing setup, things will break!") - flagset.StringVar(&StatusSocket, "status-socket", "", "Full file path to the socket file. (default: /status.sock)") + flagset.String("status-socket", "", "Full file path to the socket file. (default: /status.sock)") flagset.StringVar(&DebugListenOn, "debugListenOn", ":6060", "Http listenOn for Debug pprof handler") return flagset } From 008c5a48a158546bf5b1d49e2a288ac94f5590f0 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Tue, 7 Jan 2025 10:17:41 +0100 Subject: [PATCH 3/4] Inline setup function for install commands Configuration loading, validation, and user setup are required only for controllers. Inline this functionality into their commands, making the worker command lighter. Signed-off-by: Tom Wieczorek --- cmd/install/controller.go | 29 +++++++++++++++++++++-------- cmd/install/install.go | 34 ---------------------------------- cmd/install/util.go | 1 - cmd/install/worker.go | 17 ++++++++++------- pkg/install/service.go | 13 +++++-------- 5 files changed, 36 insertions(+), 58 deletions(-) diff --git a/cmd/install/controller.go b/cmd/install/controller.go index 36e62c01b252..d27ab6f94fa6 100644 --- a/cmd/install/controller.go +++ b/cmd/install/controller.go @@ -19,8 +19,11 @@ package install import ( "errors" "fmt" + "os" + "testing/iotest" "github.com/k0sproject/k0s/pkg/config" + "github.com/k0sproject/k0s/pkg/install" "github.com/spf13/cobra" ) @@ -38,14 +41,17 @@ With the controller subcommand you can setup a single node cluster by running: `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - opts, err := config.GetCmdOpts(cmd) - if err != nil { - return err + if os.Geteuid() != 0 { + return errors.New("this command must be run as root") } - c := (*command)(opts) + cmd.SetIn(iotest.ErrReader(errors.New("cannot read configuration from standard input when installing k0s"))) + k0sVars, err := config.NewCfgVars(cmd) + if err != nil { + return fmt.Errorf("failed to initialize configuration variables: %w", err) + } - nodeConfig, err := c.K0sVars.NodeConfig() + nodeConfig, err := k0sVars.NodeConfig() if err != nil { return fmt.Errorf("failed to load node config: %w", err) } @@ -59,10 +65,17 @@ With the controller subcommand you can setup a single node cluster by running: return err } - flagsAndVals = append([]string{"controller"}, flagsAndVals...) - if err := c.setup("controller", flagsAndVals, installFlags); err != nil { - return err + systemUsers := nodeConfig.Spec.Install.SystemUsers + homeDir := k0sVars.DataDir + if err := install.EnsureControllerUsers(systemUsers, homeDir); err != nil { + return fmt.Errorf("failed to create controller users: %w", err) + } + + args := append([]string{"controller"}, flagsAndVals...) + if err := install.InstallService(args, installFlags.envVars, installFlags.force); err != nil { + return fmt.Errorf("failed to install controller service: %w", err) } + return nil }, } diff --git a/cmd/install/install.go b/cmd/install/install.go index 11bd266faa30..1b2d89c49613 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -17,18 +17,11 @@ limitations under the License. package install import ( - "errors" - "fmt" - "os" - "github.com/k0sproject/k0s/pkg/config" - "github.com/k0sproject/k0s/pkg/install" "github.com/spf13/cobra" ) -type command config.CLIOptions - type installFlags struct { force bool envVars []string @@ -51,30 +44,3 @@ func NewInstallCmd() *cobra.Command { cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) return cmd } - -// The setup functions: -// - Ensures that the proper users are created. -// - Sets up startup and logging for k0s. -func (c *command) setup(role string, args []string, installFlags *installFlags) error { - if os.Geteuid() != 0 { - return errors.New("this command must be run as root") - } - - nodeConfig, err := c.K0sVars.NodeConfig() - if err != nil { - return err - } - - if role == "controller" { - systemUsers := nodeConfig.Spec.Install.SystemUsers - homeDir := c.K0sVars.DataDir - if err := install.EnsureControllerUsers(systemUsers, homeDir); err != nil { - return fmt.Errorf("failed to create controller users: %w", err) - } - } - err = install.EnsureService(args, installFlags.envVars, installFlags.force) - if err != nil { - return fmt.Errorf("failed to install k0s service: %w", err) - } - return nil -} diff --git a/cmd/install/util.go b/cmd/install/util.go index 6ba8e661c0bd..f71f50c46221 100644 --- a/cmd/install/util.go +++ b/cmd/install/util.go @@ -23,7 +23,6 @@ import ( "strings" "github.com/spf13/cobra" - "github.com/spf13/pflag" ) diff --git a/cmd/install/worker.go b/cmd/install/worker.go index 55c79cb7dc8a..30f693c02a6f 100644 --- a/cmd/install/worker.go +++ b/cmd/install/worker.go @@ -17,9 +17,14 @@ limitations under the License. package install import ( + "errors" + "fmt" + "os" + "github.com/spf13/cobra" "github.com/k0sproject/k0s/pkg/config" + "github.com/k0sproject/k0s/pkg/install" ) func installWorkerCmd(installFlags *installFlags) *cobra.Command { @@ -32,20 +37,18 @@ All default values of worker command will be passed to the service stub unless o Windows flags like "--api-server", "--cidr-range" and "--cluster-dns" will be ignored since install command doesn't yet support Windows services`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - opts, err := config.GetCmdOpts(cmd) - if err != nil { - return err + if os.Geteuid() != 0 { + return errors.New("this command must be run as root") } - c := (*command)(opts) flagsAndVals, err := cmdFlagsToArgs(cmd) if err != nil { return err } - flagsAndVals = append([]string{"worker"}, flagsAndVals...) - if err := c.setup("worker", flagsAndVals, installFlags); err != nil { - return err + args := append([]string{"worker"}, flagsAndVals...) + if err := install.InstallService(args, installFlags.envVars, installFlags.force); err != nil { + return fmt.Errorf("failed to install worker service: %w", err) } return nil diff --git a/pkg/install/service.go b/pkg/install/service.go index c7cccd5d4b1c..dd3c75238dcc 100644 --- a/pkg/install/service.go +++ b/pkg/install/service.go @@ -18,7 +18,6 @@ package install import ( "errors" - "fmt" "github.com/kardianos/service" "github.com/sirupsen/logrus" @@ -65,8 +64,8 @@ func InstalledService() (service.Service, error) { return s, errors.New("k0s has not been installed as a service") } -// EnsureService installs the k0s service, per the given arguments, and the detected platform -func EnsureService(args []string, envVars []string, force bool) error { +// InstallService installs the k0s service, per the given arguments, and the detected platform +func InstallService(args []string, envVars []string, force bool) error { var deps []string var svcConfig *service.Config @@ -114,6 +113,7 @@ func EnsureService(args []string, envVars []string, force bool) error { svcConfig.Dependencies = deps svcConfig.Arguments = args + if force { logrus.Infof("Uninstalling %s service", svcConfig.Name) err = s.Uninstall() @@ -121,12 +121,9 @@ func EnsureService(args []string, envVars []string, force bool) error { logrus.Warnf("failed to uninstall service: %v", err) } } + logrus.Infof("Installing %s service", svcConfig.Name) - err = s.Install() - if err != nil { - return fmt.Errorf("failed to install service: %w", err) - } - return nil + return s.Install() } func UninstallService(role string) error { From 90fb8e29e49a5922f3eefc52e2e7acec11a944ff Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Tue, 7 Jan 2025 15:09:43 +0100 Subject: [PATCH 4/4] Provide several sub-commands only on Linux They won't work on non-Linux systems anyways. Use build flags to add platform-specific sub-commands instead of runtime checks. Also add build tag to files that only make sense on specific platforms. This includes Autopilot, as this relies on the status socket, which is not available on Windows at the moment. Signed-off-by: Tom Wieczorek --- cmd/controller/controller.go | 2 + cmd/controller/controller_test.go | 5 ++ cmd/install/controller.go | 2 + cmd/install/controller_test.go | 2 + cmd/install/install.go | 3 +- .../install_linux.go} | 18 ++---- cmd/install/install_other.go | 25 ++++++++ cmd/install/worker.go | 3 +- cmd/reset/reset.go | 2 + cmd/root.go | 19 +----- cmd/root_linux.go | 35 +++++++++++ .../bridge_other.go => cmd/root_other.go | 10 ++-- cmd/root_test.go | 9 ++- cmd/start/start.go | 3 +- cmd/status/status.go | 2 + cmd/stop/stop.go | 3 +- cmd/token/create.go | 2 + cmd/token/token.go | 3 +- cmd/token/token_other.go | 25 ++++++++ .../backup_windows.go => token/token_unix.go} | 21 ++----- cmd/worker/worker.go | 28 +-------- cmd/worker/worker_other.go | 31 ++++++++++ cmd/worker/worker_unix.go | 59 +++++++++++++++++++ internal/pkg/users/lookup_unix_test.go | 2 + pkg/autopilot/controller/root_controller.go | 2 + .../controller/root_controller_test.go | 2 + pkg/autopilot/controller/root_worker.go | 2 + pkg/autopilot/controller/setup.go | 2 + pkg/autopilot/controller/signal/init.go | 2 + pkg/autopilot/controller/signal/k0s/apply.go | 2 + pkg/autopilot/controller/signal/k0s/cordon.go | 17 ++++++ .../controller/signal/k0s/download.go | 2 + pkg/autopilot/controller/signal/k0s/init.go | 51 +++------------- .../{restart_windows.go => restart_other.go} | 4 ++ .../controller/signal/k0s/restart_unix.go | 13 ++++ pkg/autopilot/controller/signal/k0s/signal.go | 18 ++++-- .../controller/signal/k0s/signal_test.go | 2 + .../controller/signal/k0s/uncordon.go | 2 + .../controller/updates/periodicupdater.go | 2 + .../controller/updates/update_controller.go | 2 + pkg/autopilot/controller/updates/updater.go | 2 + pkg/cleanup/cleanup.go | 2 + pkg/cleanup/{cni.go => cni_linux.go} | 0 pkg/cleanup/{users.go => users_linux.go} | 0 pkg/component/controller/autopilot.go | 2 + pkg/component/status/client.go | 3 +- pkg/component/status/status.go | 3 + pkg/component/worker/autopilot.go | 2 + pkg/install/service.go | 27 +-------- pkg/install/service_linux.go | 43 ++++++++++++++ pkg/install/service_other.go | 25 ++++++++ pkg/install/{users.go => users_linux.go} | 2 + 52 files changed, 388 insertions(+), 162 deletions(-) rename cmd/{restore/restore_windows.go => install/install_linux.go} (59%) create mode 100644 cmd/install/install_other.go create mode 100644 cmd/root_linux.go rename pkg/cleanup/bridge_other.go => cmd/root_other.go (79%) create mode 100644 cmd/token/token_other.go rename cmd/{backup/backup_windows.go => token/token_unix.go} (60%) create mode 100644 cmd/worker/worker_other.go create mode 100644 cmd/worker/worker_unix.go rename pkg/autopilot/controller/signal/k0s/{restart_windows.go => restart_other.go} (97%) rename pkg/cleanup/{cni.go => cni_linux.go} (100%) rename pkg/cleanup/{users.go => users_linux.go} (100%) create mode 100644 pkg/install/service_linux.go create mode 100644 pkg/install/service_other.go rename pkg/install/{users.go => users_linux.go} (97%) diff --git a/cmd/controller/controller.go b/cmd/controller/controller.go index 40bcd9ace826..d3a7bce163cd 100644 --- a/cmd/controller/controller.go +++ b/cmd/controller/controller.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2021 k0s authors diff --git a/cmd/controller/controller_test.go b/cmd/controller/controller_test.go index 2f3f270775e8..16d05c94faca 100644 --- a/cmd/controller/controller_test.go +++ b/cmd/controller/controller_test.go @@ -17,6 +17,7 @@ limitations under the License. package controller_test import ( + "runtime" "strconv" "strings" "testing" @@ -28,6 +29,10 @@ import ( ) func TestControllerCmd_Help(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("Running controllers is only supported on Linux") + } + defaultConfigPath := strconv.Quote(constant.K0sConfigPathDefault) defaultDataDir := strconv.Quote(constant.DataDirDefault) diff --git a/cmd/install/controller.go b/cmd/install/controller.go index d27ab6f94fa6..06f665237af2 100644 --- a/cmd/install/controller.go +++ b/cmd/install/controller.go @@ -1,3 +1,5 @@ +//go:build linux + /* Copyright 2021 k0s authors diff --git a/cmd/install/controller_test.go b/cmd/install/controller_test.go index bfab32f960c3..33abab182580 100644 --- a/cmd/install/controller_test.go +++ b/cmd/install/controller_test.go @@ -1,3 +1,5 @@ +//go:build linux + /* Copyright 2024 k0s authors diff --git a/cmd/install/install.go b/cmd/install/install.go index 1b2d89c49613..850c42cd7cee 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -37,8 +37,9 @@ func NewInstallCmd() *cobra.Command { Run: func(*cobra.Command, []string) { /* Enforce arg validation. */ }, } - cmd.AddCommand(installControllerCmd(&installFlags)) cmd.AddCommand(installWorkerCmd(&installFlags)) + addPlatformSpecificCommands(cmd, &installFlags) + cmd.PersistentFlags().BoolVar(&installFlags.force, "force", false, "force init script creation") cmd.PersistentFlags().StringArrayVarP(&installFlags.envVars, "env", "e", nil, "set environment variable") cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) diff --git a/cmd/restore/restore_windows.go b/cmd/install/install_linux.go similarity index 59% rename from cmd/restore/restore_windows.go rename to cmd/install/install_linux.go index a23e2e4cb6f2..6c306f7548cd 100644 --- a/cmd/restore/restore_windows.go +++ b/cmd/install/install_linux.go @@ -1,5 +1,5 @@ /* -Copyright 2021 k0s authors +Copyright 2025 k0s authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,22 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restore +package install import ( - "errors" - "github.com/spf13/cobra" ) -var restoredConfigPath string - -func NewRestoreCmd() *cobra.Command { - return &cobra.Command{ - Use: "restore", - Short: "restore k0s state from given backup archive. Not supported in Windows OS", - RunE: func(cmd *cobra.Command, args []string) error { - return errors.New("unsupported Operating System for this command") - }, - } +func addPlatformSpecificCommands(install *cobra.Command, installFlags *installFlags) { + install.AddCommand(installControllerCmd(installFlags)) } diff --git a/cmd/install/install_other.go b/cmd/install/install_other.go new file mode 100644 index 000000000000..8764e65a3c7b --- /dev/null +++ b/cmd/install/install_other.go @@ -0,0 +1,25 @@ +//go:build !linux + +/* +Copyright 2025 k0s 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 install + +import "github.com/spf13/cobra" + +func addPlatformSpecificCommands(*cobra.Command, *installFlags) { + // no-op +} diff --git a/cmd/install/worker.go b/cmd/install/worker.go index 30f693c02a6f..cbcd0649c688 100644 --- a/cmd/install/worker.go +++ b/cmd/install/worker.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "os" + "runtime" "github.com/spf13/cobra" @@ -37,7 +38,7 @@ All default values of worker command will be passed to the service stub unless o Windows flags like "--api-server", "--cidr-range" and "--cluster-dns" will be ignored since install command doesn't yet support Windows services`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - if os.Geteuid() != 0 { + if runtime.GOOS != "windows" && os.Geteuid() != 0 { return errors.New("this command must be run as root") } diff --git a/cmd/reset/reset.go b/cmd/reset/reset.go index 59dd16d1a5dd..3e9f886d64e2 100644 --- a/cmd/reset/reset.go +++ b/cmd/reset/reset.go @@ -1,3 +1,5 @@ +//go:build linux + /* Copyright 2021 k0s authors diff --git a/cmd/root.go b/cmd/root.go index 3892d83c5cb6..923bbd18c92c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,22 +20,16 @@ import ( "errors" "net/http" "os" - "runtime" "github.com/k0sproject/k0s/cmd/airgap" "github.com/k0sproject/k0s/cmd/api" - "github.com/k0sproject/k0s/cmd/backup" configcmd "github.com/k0sproject/k0s/cmd/config" - "github.com/k0sproject/k0s/cmd/controller" "github.com/k0sproject/k0s/cmd/ctr" "github.com/k0sproject/k0s/cmd/etcd" "github.com/k0sproject/k0s/cmd/install" "github.com/k0sproject/k0s/cmd/kubeconfig" "github.com/k0sproject/k0s/cmd/kubectl" - "github.com/k0sproject/k0s/cmd/reset" - "github.com/k0sproject/k0s/cmd/restore" "github.com/k0sproject/k0s/cmd/start" - "github.com/k0sproject/k0s/cmd/status" "github.com/k0sproject/k0s/cmd/stop" "github.com/k0sproject/k0s/cmd/sysinfo" "github.com/k0sproject/k0s/cmd/token" @@ -82,24 +76,13 @@ func NewRootCmd() *cobra.Command { cmd.AddCommand(airgap.NewAirgapCmd()) cmd.AddCommand(api.NewAPICmd()) - cmd.AddCommand(backup.NewBackupCmd()) - cmd.AddCommand(controller.NewControllerCmd()) cmd.AddCommand(ctr.NewCtrCommand()) cmd.AddCommand(configcmd.NewConfigCmd()) cmd.AddCommand(etcd.NewEtcdCmd()) cmd.AddCommand(install.NewInstallCmd()) cmd.AddCommand(kubeconfig.NewKubeConfigCmd()) cmd.AddCommand(kubectl.NewK0sKubectlCmd()) - if runtime.GOOS == "linux" { - // Currently only supported on Linux - cmd.AddCommand(reset.NewResetCmd()) - } - cmd.AddCommand(restore.NewRestoreCmd()) cmd.AddCommand(start.NewStartCmd()) - if runtime.GOOS == "linux" { - // Currently only supported on Linux - cmd.AddCommand(status.NewStatusCmd()) - } cmd.AddCommand(stop.NewStopCmd()) cmd.AddCommand(sysinfo.NewSysinfoCmd()) cmd.AddCommand(token.NewTokenCmd()) @@ -109,6 +92,8 @@ func NewRootCmd() *cobra.Command { cmd.AddCommand(newCompletionCmd()) cmd.AddCommand(newDocsCmd()) + addPlatformSpecificCommands(cmd) + cmd.DisableAutoGenTag = true longDesc = "k0s - The zero friction Kubernetes - https://k0sproject.io" if build.EulaNotice != "" { diff --git a/cmd/root_linux.go b/cmd/root_linux.go new file mode 100644 index 000000000000..ed88c3d69c17 --- /dev/null +++ b/cmd/root_linux.go @@ -0,0 +1,35 @@ +/* +Copyright 2025 k0s 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 cmd + +import ( + "github.com/k0sproject/k0s/cmd/backup" + "github.com/k0sproject/k0s/cmd/controller" + "github.com/k0sproject/k0s/cmd/reset" + "github.com/k0sproject/k0s/cmd/restore" + "github.com/k0sproject/k0s/cmd/status" + + "github.com/spf13/cobra" +) + +func addPlatformSpecificCommands(root *cobra.Command) { + root.AddCommand(backup.NewBackupCmd()) + root.AddCommand(controller.NewControllerCmd()) + root.AddCommand(reset.NewResetCmd()) + root.AddCommand(restore.NewRestoreCmd()) + root.AddCommand(status.NewStatusCmd()) +} diff --git a/pkg/cleanup/bridge_other.go b/cmd/root_other.go similarity index 79% rename from pkg/cleanup/bridge_other.go rename to cmd/root_other.go index 215f255e2395..24b49be8ba17 100644 --- a/pkg/cleanup/bridge_other.go +++ b/cmd/root_other.go @@ -1,7 +1,7 @@ //go:build !linux /* -Copyright 2021 k0s authors +Copyright 2025 k0s authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cleanup +package cmd -func newBridgeStep() Step { - return nil -} +import "github.com/spf13/cobra" + +func addPlatformSpecificCommands(root *cobra.Command) { /* no-op */ } diff --git a/cmd/root_test.go b/cmd/root_test.go index 263c45c80ca8..e3bc7eec7327 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "io" + "runtime" "slices" "strings" "testing" @@ -75,12 +76,16 @@ func TestRootCmd_Flags(t *testing.T) { func TestUnknownSubCommandsAreRejected(t *testing.T) { commandsWithArguments := []string{ - "controller", "kubeconfig create", - "restore", "token invalidate", "worker", } + if runtime.GOOS == "linux" { + commandsWithArguments = append(commandsWithArguments, + "controller", + "restore", + ) + } t.Cleanup(func() { if !t.Failed() { assert.Empty(t, commandsWithArguments, "Some sub-commands are listed unnecessarily") diff --git a/cmd/start/start.go b/cmd/start/start.go index c0e511e04bb8..46197c08a0f7 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -19,6 +19,7 @@ package start import ( "errors" "os" + "runtime" "github.com/k0sproject/k0s/pkg/install" @@ -32,7 +33,7 @@ func NewStartCmd() *cobra.Command { Short: "Start the k0s service configured on this host. Must be run as root (or with sudo)", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - if os.Geteuid() != 0 { + if runtime.GOOS != "windows" && os.Geteuid() != 0 { return errors.New("this command must be run as root") } svc, err := install.InstalledService() diff --git a/cmd/status/status.go b/cmd/status/status.go index e2294b9c483b..258ab2adffdc 100644 --- a/cmd/status/status.go +++ b/cmd/status/status.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2021 k0s authors diff --git a/cmd/stop/stop.go b/cmd/stop/stop.go index 69429b9d26c0..7c1368505186 100644 --- a/cmd/stop/stop.go +++ b/cmd/stop/stop.go @@ -19,6 +19,7 @@ package stop import ( "errors" "os" + "runtime" "github.com/k0sproject/k0s/pkg/install" @@ -32,7 +33,7 @@ func NewStopCmd() *cobra.Command { Short: "Stop the k0s service configured on this host. Must be run as root (or with sudo)", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - if os.Geteuid() != 0 { + if runtime.GOOS != "windows" && os.Geteuid() != 0 { return errors.New("this command must be run as root") } svc, err := install.InstalledService() diff --git a/cmd/token/create.go b/cmd/token/create.go index 05e24e45ae2c..724870c19fe0 100644 --- a/cmd/token/create.go +++ b/cmd/token/create.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2021 k0s authors diff --git a/cmd/token/token.go b/cmd/token/token.go index dee7086113d7..1c1dc674ca71 100644 --- a/cmd/token/token.go +++ b/cmd/token/token.go @@ -32,10 +32,11 @@ func NewTokenCmd() *cobra.Command { Run: func(*cobra.Command, []string) { /* Enforce arg validation. */ }, } - cmd.AddCommand(tokenCreateCmd()) cmd.AddCommand(tokenListCmd()) cmd.AddCommand(tokenInvalidateCmd()) cmd.AddCommand(preSharedCmd()) + addPlatformSpecificCommands(cmd) + return cmd } diff --git a/cmd/token/token_other.go b/cmd/token/token_other.go new file mode 100644 index 000000000000..9ff8da998678 --- /dev/null +++ b/cmd/token/token_other.go @@ -0,0 +1,25 @@ +//go:build !unix + +/* +Copyright 2025 k0s 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 token + +import ( + "github.com/spf13/cobra" +) + +func addPlatformSpecificCommands(*cobra.Command) { /* no-op */ } diff --git a/cmd/backup/backup_windows.go b/cmd/token/token_unix.go similarity index 60% rename from cmd/backup/backup_windows.go rename to cmd/token/token_unix.go index bd2a43661168..c203937b3aa6 100644 --- a/cmd/backup/backup_windows.go +++ b/cmd/token/token_unix.go @@ -1,5 +1,7 @@ +//go:build unix + /* -Copyright 2021 k0s authors +Copyright 2025 k0s authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,23 +16,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package backup +package token import ( - "errors" - "github.com/spf13/cobra" ) -var savePath string - -func NewBackupCmd() *cobra.Command { - return &cobra.Command{ - Use: "backup", - Short: "Back-Up k0s configuration. Not supported on Windows OS", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return errors.New("unsupported Operating System for this command") - }, - } +func addPlatformSpecificCommands(token *cobra.Command) { + token.AddCommand(tokenCreateCmd()) } diff --git a/cmd/worker/worker.go b/cmd/worker/worker.go index 9b4f23e1ba33..51eb2e917fac 100644 --- a/cmd/worker/worker.go +++ b/cmd/worker/worker.go @@ -27,11 +27,9 @@ import ( internallog "github.com/k0sproject/k0s/internal/pkg/log" "github.com/k0sproject/k0s/internal/pkg/sysinfo" - "github.com/k0sproject/k0s/pkg/build" "github.com/k0sproject/k0s/pkg/component/iptables" "github.com/k0sproject/k0s/pkg/component/manager" "github.com/k0sproject/k0s/pkg/component/prober" - "github.com/k0sproject/k0s/pkg/component/status" "github.com/k0sproject/k0s/pkg/component/worker" workerconfig "github.com/k0sproject/k0s/pkg/component/worker/config" "github.com/k0sproject/k0s/pkg/component/worker/containerd" @@ -172,31 +170,7 @@ func (c *Command) Start(ctx context.Context) error { certManager := worker.NewCertificateManager(kubeletKubeconfigPath) - // if running inside a controller, status component is already running - if !c.SingleNode && !c.EnableWorker { - componentManager.Add(ctx, &status.Status{ - Prober: prober.DefaultProber, - StatusInformation: status.K0sStatus{ - Pid: os.Getpid(), - Role: "worker", - Args: os.Args, - Version: build.Version, - Workloads: true, - SingleNode: false, - K0sVars: c.K0sVars, - // worker does not have cluster config. this is only shown in "k0s status -o json". - // todo: if it's needed, a worker side config client can be set up and used to load the config - ClusterConfig: nil, - }, - CertManager: certManager, - Socket: c.K0sVars.StatusSocketPath, - }) - } - - componentManager.Add(ctx, &worker.Autopilot{ - K0sVars: c.K0sVars, - CertManager: certManager, - }) + addPlatformSpecificComponents(ctx, componentManager, c.K0sVars, &c.ControllerOptions, certManager) // extract needed components if err := componentManager.Init(ctx); err != nil { diff --git a/cmd/worker/worker_other.go b/cmd/worker/worker_other.go new file mode 100644 index 000000000000..2f0ef1153dc8 --- /dev/null +++ b/cmd/worker/worker_other.go @@ -0,0 +1,31 @@ +//go:build !unix + +/* +Copyright 2025 k0s 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 worker + +import ( + "context" + + "github.com/k0sproject/k0s/pkg/component/manager" + "github.com/k0sproject/k0s/pkg/component/worker" + "github.com/k0sproject/k0s/pkg/config" +) + +func addPlatformSpecificComponents(context.Context, *manager.Manager, *config.CfgVars, *config.ControllerOptions, *worker.CertificateManager) { + // no-op +} diff --git a/cmd/worker/worker_unix.go b/cmd/worker/worker_unix.go new file mode 100644 index 000000000000..0f4908f9cd5c --- /dev/null +++ b/cmd/worker/worker_unix.go @@ -0,0 +1,59 @@ +//go:build unix + +/* +Copyright 2025 k0s 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 worker + +import ( + "context" + "os" + + "github.com/k0sproject/k0s/pkg/build" + "github.com/k0sproject/k0s/pkg/component/manager" + "github.com/k0sproject/k0s/pkg/component/prober" + "github.com/k0sproject/k0s/pkg/component/status" + "github.com/k0sproject/k0s/pkg/component/worker" + "github.com/k0sproject/k0s/pkg/config" +) + +func addPlatformSpecificComponents(ctx context.Context, m *manager.Manager, k0sVars *config.CfgVars, opts *config.ControllerOptions, certManager *worker.CertificateManager) { + // if running inside a controller, status component is already running + if !opts.SingleNode && !opts.EnableWorker { + m.Add(ctx, &status.Status{ + Prober: prober.DefaultProber, + StatusInformation: status.K0sStatus{ + Pid: os.Getpid(), + Role: "worker", + Args: os.Args, + Version: build.Version, + Workloads: true, + SingleNode: false, + K0sVars: k0sVars, + // worker does not have cluster config. this is only shown in "k0s status -o json". + // todo: if it's needed, a worker side config client can be set up and used to load the config + ClusterConfig: nil, + }, + CertManager: certManager, + Socket: k0sVars.StatusSocketPath, + }) + } + + m.Add(ctx, &worker.Autopilot{ + K0sVars: k0sVars, + CertManager: certManager, + }) +} diff --git a/internal/pkg/users/lookup_unix_test.go b/internal/pkg/users/lookup_unix_test.go index 1bf444bc429c..a240d173887a 100644 --- a/internal/pkg/users/lookup_unix_test.go +++ b/internal/pkg/users/lookup_unix_test.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2022 k0s authors diff --git a/pkg/autopilot/controller/root_controller.go b/pkg/autopilot/controller/root_controller.go index b332a7439d8a..13290b2ff4bb 100644 --- a/pkg/autopilot/controller/root_controller.go +++ b/pkg/autopilot/controller/root_controller.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/root_controller_test.go b/pkg/autopilot/controller/root_controller_test.go index 9c778fd10194..2c3d2d3697d7 100644 --- a/pkg/autopilot/controller/root_controller_test.go +++ b/pkg/autopilot/controller/root_controller_test.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/root_worker.go b/pkg/autopilot/controller/root_worker.go index 8bb43c670700..531e556cb64c 100644 --- a/pkg/autopilot/controller/root_worker.go +++ b/pkg/autopilot/controller/root_worker.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2022 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/setup.go b/pkg/autopilot/controller/setup.go index d39075e0fe12..fa6d4f0e36f4 100644 --- a/pkg/autopilot/controller/setup.go +++ b/pkg/autopilot/controller/setup.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2022 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/signal/init.go b/pkg/autopilot/controller/signal/init.go index 4c51c1315b3b..980cf7bd42cf 100644 --- a/pkg/autopilot/controller/signal/init.go +++ b/pkg/autopilot/controller/signal/init.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2022 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/signal/k0s/apply.go b/pkg/autopilot/controller/signal/k0s/apply.go index 003792f7fc77..c25910db5e3b 100644 --- a/pkg/autopilot/controller/signal/k0s/apply.go +++ b/pkg/autopilot/controller/signal/k0s/apply.go @@ -36,6 +36,8 @@ import ( crpred "sigs.k8s.io/controller-runtime/pkg/predicate" ) +const ApplyingUpdate = "ApplyingUpdate" + // applyingUpdateEventFilter creates a controller-runtime predicate that governs which // objects will make it into reconciliation, and which will be ignored. func applyingUpdateEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { diff --git a/pkg/autopilot/controller/signal/k0s/cordon.go b/pkg/autopilot/controller/signal/k0s/cordon.go index d449990284f0..8c132b5b7d91 100644 --- a/pkg/autopilot/controller/signal/k0s/cordon.go +++ b/pkg/autopilot/controller/signal/k0s/cordon.go @@ -22,11 +22,13 @@ import ( autopilotv1beta2 "github.com/k0sproject/k0s/pkg/apis/autopilot/v1beta2" apcomm "github.com/k0sproject/k0s/pkg/autopilot/common" + apconst "github.com/k0sproject/k0s/pkg/autopilot/constant" apdel "github.com/k0sproject/k0s/pkg/autopilot/controller/delegate" apsigpred "github.com/k0sproject/k0s/pkg/autopilot/controller/signal/common/predicate" apsigv2 "github.com/k0sproject/k0s/pkg/autopilot/signaling/v2" cr "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" crcli "sigs.k8s.io/controller-runtime/pkg/client" crev "sigs.k8s.io/controller-runtime/pkg/event" crman "sigs.k8s.io/controller-runtime/pkg/manager" @@ -38,6 +40,8 @@ import ( "k8s.io/kubectl/pkg/drain" ) +const Cordoning = "Cordoning" + // cordoningEventFilter creates a controller-runtime predicate that governs which objects // will make it into reconciliation, and which will be ignored. func cordoningEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { @@ -204,3 +208,16 @@ func (r *cordoning) drainNode(ctx context.Context, signalNode crcli.Object) erro return nil } + +func needsCordoning(signalNode client.Object) bool { + kind := signalNode.GetObjectKind().GroupVersionKind().Kind + if kind == "Node" { + return true + } + for k, v := range signalNode.GetAnnotations() { + if k == apconst.K0SControlNodeModeAnnotation && v == apconst.K0SControlNodeModeControllerWorker { + return true + } + } + return false +} diff --git a/pkg/autopilot/controller/signal/k0s/download.go b/pkg/autopilot/controller/signal/k0s/download.go index 721d7f7877eb..36c1048e7bd1 100644 --- a/pkg/autopilot/controller/signal/k0s/download.go +++ b/pkg/autopilot/controller/signal/k0s/download.go @@ -33,6 +33,8 @@ import ( crpred "sigs.k8s.io/controller-runtime/pkg/predicate" ) +const Downloading = "Downloading" + // downloadEventFilter creates a controller-runtime predicate that governs which objects // will make it into reconciliation, and which will be ignored. func downloadEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { diff --git a/pkg/autopilot/controller/signal/k0s/init.go b/pkg/autopilot/controller/signal/k0s/init.go index 82b5fa21dfe4..aafb5f074d4f 100644 --- a/pkg/autopilot/controller/signal/k0s/init.go +++ b/pkg/autopilot/controller/signal/k0s/init.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,26 +23,14 @@ import ( "path/filepath" apcomm "github.com/k0sproject/k0s/pkg/autopilot/common" - apconst "github.com/k0sproject/k0s/pkg/autopilot/constant" apdel "github.com/k0sproject/k0s/pkg/autopilot/controller/delegate" apsigpred "github.com/k0sproject/k0s/pkg/autopilot/controller/signal/common/predicate" - apsigv2 "github.com/k0sproject/k0s/pkg/autopilot/signaling/v2" "github.com/k0sproject/k0s/pkg/component/status" "github.com/sirupsen/logrus" - "sigs.k8s.io/controller-runtime/pkg/client" crman "sigs.k8s.io/controller-runtime/pkg/manager" ) -const ( - Downloading = "Downloading" - Cordoning = "Cordoning" - CordoningFailed = "CordoningFailed" - UnCordoning = "UnCordoning" - ApplyingUpdate = "ApplyingUpdate" - Restart = "Restart" -) - // RegisterControllers registers all of the autopilot controllers used for updating `k0s` // to the controller-runtime manager. func RegisterControllers(ctx context.Context, logger *logrus.Entry, mgr crman.Manager, delegate apdel.ControllerDelegate, clusterID string) error { @@ -59,7 +49,11 @@ func RegisterControllers(ctx context.Context, logger *logrus.Entry, mgr crman.Ma logger.Infof("Using effective hostname = '%v'", hostname) - if err := registerSignalController(logger, mgr, signalControllerEventFilter(hostname, apsigpred.DefaultErrorHandler(logger, "k0s signal")), delegate, clusterID); err != nil { + k0sVersionHandler := func() (string, error) { + return getK0sVersion(DefaultK0sStatusSocketPath) + } + + if err := registerSignalController(logger, mgr, signalControllerEventFilter(hostname, apsigpred.DefaultErrorHandler(logger, "k0s signal")), delegate, clusterID, k0sVersionHandler); err != nil { return fmt.Errorf("unable to register k0s 'signal' controller: %w", err) } @@ -100,34 +94,3 @@ func getK0sVersion(statusSocketPath string) (string, error) { return status.Version, nil } - -// getK0sPid returns the PID of a running k0s based on its status socket. -func getK0sPid(statusSocketPath string) (int, error) { - status, err := status.GetStatusInfo(statusSocketPath) - if err != nil { - return -1, err - } - - return status.Pid, nil -} - -// signalDataUpdateCommandK0sPredicate creates a predicate that ensures that the -// provided SignalData is an 'k0s' update. -func signalDataUpdateCommandK0sPredicate() apsigpred.SignalDataPredicate { - return func(signalData apsigv2.SignalData) bool { - return signalData.Command.K0sUpdate != nil - } -} - -func needsCordoning(signalNode client.Object) bool { - kind := signalNode.GetObjectKind().GroupVersionKind().Kind - if kind == "Node" { - return true - } - for k, v := range signalNode.GetAnnotations() { - if k == apconst.K0SControlNodeModeAnnotation && v == apconst.K0SControlNodeModeControllerWorker { - return true - } - } - return false -} diff --git a/pkg/autopilot/controller/signal/k0s/restart_windows.go b/pkg/autopilot/controller/signal/k0s/restart_other.go similarity index 97% rename from pkg/autopilot/controller/signal/k0s/restart_windows.go rename to pkg/autopilot/controller/signal/k0s/restart_other.go index 9c32a1a665e9..1c151bf09aee 100644 --- a/pkg/autopilot/controller/signal/k0s/restart_windows.go +++ b/pkg/autopilot/controller/signal/k0s/restart_other.go @@ -1,3 +1,5 @@ +//go:build !unix + // Copyright 2022 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +24,8 @@ import ( crpred "sigs.k8s.io/controller-runtime/pkg/predicate" ) +const Restart = "Restart" + // restartEventFilter creates a controller-runtime predicate that governs which // objects will make it into reconciliation, and which will be ignored. func restartEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { diff --git a/pkg/autopilot/controller/signal/k0s/restart_unix.go b/pkg/autopilot/controller/signal/k0s/restart_unix.go index 4059b0f35166..44e6010edb15 100644 --- a/pkg/autopilot/controller/signal/k0s/restart_unix.go +++ b/pkg/autopilot/controller/signal/k0s/restart_unix.go @@ -26,6 +26,7 @@ import ( apdel "github.com/k0sproject/k0s/pkg/autopilot/controller/delegate" apsigpred "github.com/k0sproject/k0s/pkg/autopilot/controller/signal/common/predicate" apsigv2 "github.com/k0sproject/k0s/pkg/autopilot/signaling/v2" + "github.com/k0sproject/k0s/pkg/component/status" "github.com/sirupsen/logrus" cr "sigs.k8s.io/controller-runtime" @@ -35,6 +36,8 @@ import ( crpred "sigs.k8s.io/controller-runtime/pkg/predicate" ) +const Restart = "Restart" + const ( restartRequeueDuration = 5 * time.Second ) @@ -149,3 +152,13 @@ func (r *restart) Reconcile(ctx context.Context, req cr.Request) (cr.Result, err return cr.Result{}, nil } + +// getK0sPid returns the PID of a running k0s based on its status socket. +func getK0sPid(statusSocketPath string) (int, error) { + status, err := status.GetStatusInfo(statusSocketPath) + if err != nil { + return -1, err + } + + return status.Pid, nil +} diff --git a/pkg/autopilot/controller/signal/k0s/signal.go b/pkg/autopilot/controller/signal/k0s/signal.go index 63c4741f6770..379241ce9aa8 100644 --- a/pkg/autopilot/controller/signal/k0s/signal.go +++ b/pkg/autopilot/controller/signal/k0s/signal.go @@ -40,6 +40,14 @@ const ( type k0sVersionHandlerFunc func() (string, error) +// signalDataUpdateCommandK0sPredicate creates a predicate that ensures that the +// provided SignalData is an 'k0s' update. +func signalDataUpdateCommandK0sPredicate() apsigpred.SignalDataPredicate { + return func(signalData apsigv2.SignalData) bool { + return signalData.Command.K0sUpdate != nil + } +} + // signalControllerEventFilter creates a controller-runtime predicate that governs which objects // will make it into reconciliation, and which will be ignored. func signalControllerEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { @@ -71,7 +79,7 @@ type signalControllerHandler struct { // // This controller is only interested in changes to its own annotations, and is the main // mechanism in identifying incoming autopilot k0s signaling updates. -func registerSignalController(logger *logrus.Entry, mgr crman.Manager, eventFilter crpred.Predicate, delegate apdel.ControllerDelegate, clusterID string) error { +func registerSignalController(logger *logrus.Entry, mgr crman.Manager, eventFilter crpred.Predicate, delegate apdel.ControllerDelegate, clusterID string, k0sVersionHandler k0sVersionHandlerFunc) error { logr := logger.WithFields(logrus.Fields{"updatetype": "k0s"}) logr.Infof("Registering 'signal' reconciler for '%s'", delegate.Name()) @@ -86,11 +94,9 @@ func registerSignalController(logger *logrus.Entry, mgr crman.Manager, eventFilt mgr.GetClient(), delegate, &signalControllerHandler{ - timeout: SignalResponseProcessingTimeout, - clusterID: clusterID, - k0sVersionHandler: func() (string, error) { - return getK0sVersion(DefaultK0sStatusSocketPath) - }, + timeout: SignalResponseProcessingTimeout, + clusterID: clusterID, + k0sVersionHandler: k0sVersionHandler, }, ), ) diff --git a/pkg/autopilot/controller/signal/k0s/signal_test.go b/pkg/autopilot/controller/signal/k0s/signal_test.go index 88aaaeb59a8f..2a4726c0ac29 100644 --- a/pkg/autopilot/controller/signal/k0s/signal_test.go +++ b/pkg/autopilot/controller/signal/k0s/signal_test.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/signal/k0s/uncordon.go b/pkg/autopilot/controller/signal/k0s/uncordon.go index 0f03fa9316f4..ee8a3290d966 100644 --- a/pkg/autopilot/controller/signal/k0s/uncordon.go +++ b/pkg/autopilot/controller/signal/k0s/uncordon.go @@ -38,6 +38,8 @@ import ( crpred "sigs.k8s.io/controller-runtime/pkg/predicate" ) +const UnCordoning = "UnCordoning" + // unCordoningEventFilter creates a controller-runtime predicate that governs which objects // will make it into reconciliation, and which will be ignored. func unCordoningEventFilter(hostname string, handler apsigpred.ErrorHandler) crpred.Predicate { diff --git a/pkg/autopilot/controller/updates/periodicupdater.go b/pkg/autopilot/controller/updates/periodicupdater.go index fae29196b6ac..0332c0b2afe9 100644 --- a/pkg/autopilot/controller/updates/periodicupdater.go +++ b/pkg/autopilot/controller/updates/periodicupdater.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2023 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/updates/update_controller.go b/pkg/autopilot/controller/updates/update_controller.go index ab574a9efd92..4ccfa70ab8cc 100644 --- a/pkg/autopilot/controller/updates/update_controller.go +++ b/pkg/autopilot/controller/updates/update_controller.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/autopilot/controller/updates/updater.go b/pkg/autopilot/controller/updates/updater.go index 89fadaa3c725..6ad3ccadc86c 100644 --- a/pkg/autopilot/controller/updates/updater.go +++ b/pkg/autopilot/controller/updates/updater.go @@ -1,3 +1,5 @@ +//go:build unix + // Copyright 2021 k0s authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/cleanup/cleanup.go b/pkg/cleanup/cleanup.go index a2d97571ac97..756a1819d417 100644 --- a/pkg/cleanup/cleanup.go +++ b/pkg/cleanup/cleanup.go @@ -1,3 +1,5 @@ +//go:build linux + /* Copyright 2021 k0s authors diff --git a/pkg/cleanup/cni.go b/pkg/cleanup/cni_linux.go similarity index 100% rename from pkg/cleanup/cni.go rename to pkg/cleanup/cni_linux.go diff --git a/pkg/cleanup/users.go b/pkg/cleanup/users_linux.go similarity index 100% rename from pkg/cleanup/users.go rename to pkg/cleanup/users_linux.go diff --git a/pkg/component/controller/autopilot.go b/pkg/component/controller/autopilot.go index 04c95249af12..4794393a0865 100644 --- a/pkg/component/controller/autopilot.go +++ b/pkg/component/controller/autopilot.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2022 k0s authors diff --git a/pkg/component/status/client.go b/pkg/component/status/client.go index f2278a170e92..9c37736d41e0 100644 --- a/pkg/component/status/client.go +++ b/pkg/component/status/client.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2022 k0s authors @@ -43,7 +45,6 @@ type K0sStatus struct { ClusterConfig *v1beta1.ClusterConfig K0sVars *config.CfgVars } - type ProbeStatus struct { Message string Success bool diff --git a/pkg/component/status/status.go b/pkg/component/status/status.go index 888f42ce4326..1ac61fab7b03 100644 --- a/pkg/component/status/status.go +++ b/pkg/component/status/status.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2021 k0s authors @@ -41,6 +43,7 @@ import ( type Stater interface { State(maxCount int) prober.State } + type Status struct { StatusInformation K0sStatus Prober Stater diff --git a/pkg/component/worker/autopilot.go b/pkg/component/worker/autopilot.go index d6bcc772c86e..44a7a2ac22b0 100644 --- a/pkg/component/worker/autopilot.go +++ b/pkg/component/worker/autopilot.go @@ -1,3 +1,5 @@ +//go:build unix + /* Copyright 2022 k0s authors diff --git a/pkg/install/service.go b/pkg/install/service.go index dd3c75238dcc..531b11031c23 100644 --- a/pkg/install/service.go +++ b/pkg/install/service.go @@ -66,7 +66,6 @@ func InstalledService() (service.Service, error) { // InstallService installs the k0s service, per the given arguments, and the detected platform func InstallService(args []string, envVars []string, force bool) error { - var deps []string var svcConfig *service.Config prg := &Program{} @@ -82,36 +81,12 @@ func InstallService(args []string, envVars []string, force bool) error { return err } - // fetch service type - svcType := s.Platform() - switch svcType { - case "linux-openrc": - deps = []string{"need cgroups", "need net", "use dns", "after firewall"} - svcConfig.Option = map[string]interface{}{ - "OpenRCScript": openRCScript, - } - case "linux-upstart": - svcConfig.Option = map[string]interface{}{ - "UpstartScript": upstartScript, - } - case "unix-systemv": - svcConfig.Option = map[string]interface{}{ - "SysVScript": sysvScript, - } - case "linux-systemd": - deps = []string{"After=network-online.target", "Wants=network-online.target"} - svcConfig.Option = map[string]interface{}{ - "SystemdScript": systemdScript, - "LimitNOFILE": 999999, - } - default: - } + configureServicePlatform(s, svcConfig) if len(envVars) > 0 { svcConfig.Option["Environment"] = envVars } - svcConfig.Dependencies = deps svcConfig.Arguments = args if force { diff --git a/pkg/install/service_linux.go b/pkg/install/service_linux.go new file mode 100644 index 000000000000..641ab957c011 --- /dev/null +++ b/pkg/install/service_linux.go @@ -0,0 +1,43 @@ +/* +Copyright 2025 k0s 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 install + +import "github.com/kardianos/service" + +func configureServicePlatform(s service.Service, svcConfig *service.Config) { + switch s.Platform() { + case "linux-openrc": + svcConfig.Dependencies = []string{"need cgroups", "need net", "use dns", "after firewall"} + svcConfig.Option = map[string]interface{}{ + "OpenRCScript": openRCScript, + } + case "linux-upstart": + svcConfig.Option = map[string]interface{}{ + "UpstartScript": upstartScript, + } + case "unix-systemv": + svcConfig.Option = map[string]interface{}{ + "SysVScript": sysvScript, + } + case "linux-systemd": + svcConfig.Dependencies = []string{"After=network-online.target", "Wants=network-online.target"} + svcConfig.Option = map[string]interface{}{ + "SystemdScript": systemdScript, + "LimitNOFILE": 999999, + } + } +} diff --git a/pkg/install/service_other.go b/pkg/install/service_other.go new file mode 100644 index 000000000000..0c3c6b7f0acc --- /dev/null +++ b/pkg/install/service_other.go @@ -0,0 +1,25 @@ +//go:build !linux + +/* +Copyright 2025 k0s 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 install + +import "github.com/kardianos/service" + +func configureServicePlatform(s service.Service, svcConfig *service.Config) { + // no-op +} diff --git a/pkg/install/users.go b/pkg/install/users_linux.go similarity index 97% rename from pkg/install/users.go rename to pkg/install/users_linux.go index 9a85ec83aeaf..ebe83d7689d6 100644 --- a/pkg/install/users.go +++ b/pkg/install/users_linux.go @@ -65,6 +65,8 @@ func DeleteControllerUsers(systemUsers *v1beta1.SystemUser) error { if err := deleteUser(userName); err != nil { errs = append(errs, err) } + } else if !errors.Is(err, users.ErrNotExist) { + errs = append(errs, err) } }