diff --git a/cmd/airgap/listimages.go b/cmd/airgap/listimages.go index e27e506cc547..ea66ff77ed6c 100644 --- a/cmd/airgap/listimages.go +++ b/cmd/airgap/listimages.go @@ -19,6 +19,7 @@ package airgap import ( "fmt" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/airgap" "github.com/k0sproject/k0s/pkg/config" @@ -26,13 +27,17 @@ import ( ) func NewAirgapListImagesCmd() *cobra.Command { - var all bool + var ( + debugFlags internal.DebugFlags + all bool + ) cmd := &cobra.Command{ - Use: "list-images", - Short: "List image names and version needed for air-gap install", - Example: `k0s airgap list-images`, - Args: cobra.NoArgs, + Use: "list-images", + Short: "List image names and version needed for air-gap install", + Example: `k0s airgap list-images`, + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -54,6 +59,8 @@ func NewAirgapListImagesCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.AddFlagSet(config.FileInputFlag()) diff --git a/cmd/api/api.go b/cmd/api/api.go index c2391a8aec84..64a30cd2efff 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -32,7 +32,7 @@ import ( "strings" "time" - internallog "github.com/k0sproject/k0s/internal/pkg/log" + "github.com/k0sproject/k0s/cmd/internal" mw "github.com/k0sproject/k0s/internal/pkg/middleware" "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" "github.com/k0sproject/k0s/pkg/config" @@ -52,17 +52,15 @@ import ( ) func NewAPICmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ Use: "api", Short: "Run the controller API", Long: `Run the controller API. Reads the runtime configuration from standard input.`, - Args: cobra.NoArgs, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - logrus.SetOutput(cmd.OutOrStdout()) - internallog.SetInfoLevel() - return config.CallParentPersistentPreRun(cmd, args) - }, + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { var run func() error @@ -76,6 +74,8 @@ Reads the runtime configuration from standard input.`, }, } + debugFlags.LongRunning().AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { switch f.Name { diff --git a/cmd/backup/backup_unix.go b/cmd/backup/backup_unix.go index d0bd1111cd3e..cda6f9701982 100644 --- a/cmd/backup/backup_unix.go +++ b/cmd/backup/backup_unix.go @@ -25,6 +25,7 @@ import ( "os" "strings" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/internal/pkg/dir" k0sv1beta1 "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" "github.com/k0sproject/k0s/pkg/backup" @@ -38,12 +39,16 @@ import ( type command config.CLIOptions func NewBackupCmd() *cobra.Command { - var savePath string + var ( + debugFlags internal.DebugFlags + savePath string + ) cmd := &cobra.Command{ - Use: "backup", - Short: "Back-Up k0s configuration. Must be run as root (or with sudo)", - Args: cobra.NoArgs, + Use: "backup", + Short: "Back-Up k0s configuration. Must be run as root (or with sudo)", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -61,6 +66,8 @@ func NewBackupCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.StringVar(&savePath, "save-path", "", "destination directory path for backup assets, use '-' for stdout") diff --git a/cmd/config/config.go b/cmd/config/config.go index 9971b1d76e65..ae846bf95b59 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -17,21 +17,30 @@ limitations under the License. package config import ( + "github.com/k0sproject/k0s/cmd/internal" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) func NewConfigCmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ - Use: "config", - Short: "Configuration related sub-commands", - Args: cobra.NoArgs, - RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation + Use: "config", + Short: "Configuration related sub-commands", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, + RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation } + + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + cmd.AddCommand(NewCreateCmd()) cmd.AddCommand(NewEditCmd()) cmd.AddCommand(NewStatusCmd()) cmd.AddCommand(NewValidateCmd()) + return cmd } diff --git a/cmd/config/create.go b/cmd/config/create.go index 6c8177017521..530d1ea7f451 100644 --- a/cmd/config/create.go +++ b/cmd/config/create.go @@ -17,13 +17,15 @@ limitations under the License. package config import ( - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/yaml" - "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" k0sscheme "github.com/k0sproject/k0s/pkg/client/clientset/scheme" "github.com/k0sproject/k0s/pkg/config" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "sigs.k8s.io/yaml" ) func NewCreateCmd() *cobra.Command { @@ -57,7 +59,11 @@ func NewCreateCmd() *cobra.Command { } flags := cmd.Flags() - flags.AddFlagSet(config.GetPersistentFlagSet()) + config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { + f.Hidden = true + f.Deprecated = "it has no effect and will be removed in a future release" + cmd.PersistentFlags().AddFlag(f) + }) flags.BoolVar(&includeImages, "include-images", false, "include the default images in the output") return cmd diff --git a/cmd/config/validate.go b/cmd/config/validate.go index 1a9916e1b624..56278f98676d 100644 --- a/cmd/config/validate.go +++ b/cmd/config/validate.go @@ -26,6 +26,7 @@ import ( "github.com/k0sproject/k0s/pkg/config" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func NewValidateCmd() *cobra.Command { @@ -63,7 +64,11 @@ func NewValidateCmd() *cobra.Command { } flags := cmd.Flags() - flags.AddFlagSet(config.GetPersistentFlagSet()) + config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { + f.Hidden = true + f.Deprecated = "it has no effect and will be removed in a future release" + cmd.PersistentFlags().AddFlag(f) + }) flags.AddFlagSet(config.FileInputFlag()) _ = cmd.MarkFlagRequired("config") diff --git a/cmd/controller/controller.go b/cmd/controller/controller.go index 78fa13269715..21838015c906 100644 --- a/cmd/controller/controller.go +++ b/cmd/controller/controller.go @@ -33,10 +33,10 @@ import ( "time" "github.com/avast/retry-go" + "github.com/k0sproject/k0s/cmd/internal" workercmd "github.com/k0sproject/k0s/cmd/worker" "github.com/k0sproject/k0s/internal/pkg/dir" "github.com/k0sproject/k0s/internal/pkg/file" - internallog "github.com/k0sproject/k0s/internal/pkg/log" "github.com/k0sproject/k0s/internal/pkg/stringmap" "github.com/k0sproject/k0s/internal/pkg/sysinfo" "github.com/k0sproject/k0s/internal/sync/value" @@ -73,6 +73,7 @@ type command config.CLIOptions func NewControllerCmd() *cobra.Command { var ( + debugFlags internal.DebugFlags controllerFlags config.ControllerOptions ignorePreFlightChecks bool ) @@ -88,12 +89,8 @@ func NewControllerCmd() *cobra.Command { or CLI flag: $ k0s controller --token-file [path_to_file] Note: Token can be passed either as a CLI argument or as a flag`, - Args: cobra.MaximumNArgs(1), - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - logrus.SetOutput(cmd.OutOrStdout()) - internallog.SetInfoLevel() - return config.CallParentPersistentPreRun(cmd, args) - }, + Args: cobra.MaximumNArgs(1), + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, args []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -122,10 +119,12 @@ func NewControllerCmd() *cobra.Command { ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM) defer cancel() - return c.start(ctx, &controllerFlags) + return c.start(ctx, &controllerFlags, debugFlags.IsDebug()) }, } + debugFlags.LongRunning().AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.AddFlagSet(config.GetControllerFlags(&controllerFlags)) @@ -136,7 +135,7 @@ func NewControllerCmd() *cobra.Command { return cmd } -func (c *command) start(ctx context.Context, flags *config.ControllerOptions) error { +func (c *command) start(ctx context.Context, flags *config.ControllerOptions, debug bool) error { perfTimer := performance.NewTimer("controller-start").Buffer().Start() nodeConfig, err := c.K0sVars.NodeConfig() @@ -274,8 +273,8 @@ func (c *command) start(ctx context.Context, flags *config.ControllerOptions) er nodeComponents.Add(ctx, &cplb.Keepalived{ K0sVars: c.K0sVars, Config: cplbCfg.Keepalived, - DetailedLogging: c.Debug, - LogConfig: c.Debug, + DetailedLogging: debug, + LogConfig: debug, KubeConfigPath: c.K0sVars.AdminKubeConfigPath, APIPort: nodeConfig.Spec.API.Port, }) diff --git a/cmd/controller/controller_test.go b/cmd/controller/controller_test.go index 16d05c94faca..62e38434f806 100644 --- a/cmd/controller/controller_test.go +++ b/cmd/controller/controller_test.go @@ -64,7 +64,7 @@ Flags: -c, --config string config file, use '-' to read the config from stdin (default `+defaultConfigPath+`) --cri-socket string container runtime socket to use, default to internal containerd. Format: [remote|docker]:[path-to-socket] --data-dir string Data Directory for k0s. DO NOT CHANGE for an existing setup, things will break! (default `+defaultDataDir+`) - -d, --debug Debug logging (default: false) + -d, --debug Debug logging (implies verbose logging) --debugListenOn string Http listenOn for Debug pprof handler (default ":6060") --disable-components strings disable components (valid items: applier-manager,autopilot,control-api,coredns,csr-approver,endpoint-reconciler,helm,konnectivity-server,kube-controller-manager,kube-proxy,kube-scheduler,metrics-server,network-provider,node-role,system-rbac,windows-node,worker-config) --enable-cloud-provider Whether or not to enable cloud provider support in kubelet @@ -88,6 +88,6 @@ Flags: --status-socket string Full file path to the socket file. (default: /status.sock) --taints strings Node taints, list of key=value:effect strings --token-file string Path to the file containing join-token. - -v, --verbose Verbose logging (default: false) + -v, --verbose Verbose logging (default true) `, out.String()) } diff --git a/cmd/etcd/etcd.go b/cmd/etcd/etcd.go index 3259789bf81f..1aec84c804d4 100644 --- a/cmd/etcd/etcd.go +++ b/cmd/etcd/etcd.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" "github.com/k0sproject/k0s/pkg/config" @@ -28,14 +29,14 @@ import ( ) func NewEtcdCmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ Use: "etcd", Short: "Manage etcd cluster", Args: cobra.NoArgs, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - if err := config.CallParentPersistentPreRun(cmd, args); err != nil { - return err - } + debugFlags.Run(cmd, args) opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -56,7 +57,9 @@ func NewEtcdCmd() *cobra.Command { RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation } - cmd.PersistentFlags().AddFlagSet(config.GetPersistentFlagSet()) + pflags := cmd.PersistentFlags() + debugFlags.AddToFlagSet(pflags) + pflags.AddFlagSet(config.GetPersistentFlagSet()) cmd.AddCommand(etcdLeaveCmd()) cmd.AddCommand(etcdListCmd()) diff --git a/cmd/install/controller_test.go b/cmd/install/controller_test.go index 33abab182580..f40124b2de60 100644 --- a/cmd/install/controller_test.go +++ b/cmd/install/controller_test.go @@ -60,8 +60,6 @@ Flags: -c, --config string config file, use '-' to read the config from stdin (default `+defaultConfigPath+`) --cri-socket string container runtime socket to use, default to internal containerd. Format: [remote|docker]:[path-to-socket] --data-dir string Data Directory for k0s. DO NOT CHANGE for an existing setup, things will break! (default `+defaultDataDir+`) - -d, --debug Debug logging (default: false) - --debugListenOn string Http listenOn for Debug pprof handler (default ":6060") --disable-components strings disable components (valid items: applier-manager,autopilot,control-api,coredns,csr-approver,endpoint-reconciler,helm,konnectivity-server,kube-controller-manager,kube-proxy,kube-scheduler,metrics-server,network-provider,node-role,system-rbac,windows-node,worker-config) --enable-cloud-provider Whether or not to enable cloud provider support in kubelet --enable-dynamic-config enable cluster-wide dynamic config based on custom resource @@ -83,10 +81,12 @@ Flags: --status-socket string Full file path to the socket file. (default: /status.sock) --taints strings Node taints, list of key=value:effect strings --token-file string Path to the file containing join-token. - -v, --verbose Verbose logging (default: false) Global Flags: - -e, --env stringArray set environment variable - --force force init script creation + -d, --debug Debug logging (implies verbose logging) + --debugListenOn string Http listenOn for Debug pprof handler (default ":6060") + -e, --env stringArray set environment variable + --force force init script creation + -v, --verbose Verbose logging `, out.String()) } diff --git a/cmd/install/install.go b/cmd/install/install.go index 8bc7adcb37d6..8e4ac32f9f93 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -17,6 +17,7 @@ limitations under the License. package install import ( + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/config" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -28,16 +29,21 @@ type installFlags struct { } func NewInstallCmd() *cobra.Command { - var installFlags installFlags + var ( + debugFlags internal.DebugFlags + installFlags installFlags + ) cmd := &cobra.Command{ - Use: "install", - Short: "Install k0s on a brand-new system. Must be run as root (or with sudo)", - Args: cobra.NoArgs, - RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation + Use: "install", + Short: "Install k0s on a brand-new system. Must be run as root (or with sudo)", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, + RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation } pflags := cmd.PersistentFlags() + debugFlags.AddToFlagSet(pflags) config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { f.Hidden = true f.Deprecated = "it has no effect and will be removed in a future release" diff --git a/cmd/internal/debug.go b/cmd/internal/debug.go new file mode 100644 index 000000000000..cc50590dc20a --- /dev/null +++ b/cmd/internal/debug.go @@ -0,0 +1,105 @@ +/* +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 internal + +import ( + "errors" + "net/http" + _ "net/http/pprof" + "os" + "strconv" + + internallog "github.com/k0sproject/k0s/internal/pkg/log" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +type DebugFlags struct { + logsToStdout bool + verbose bool + verboseByDefault bool + debug bool + debugListenOn string +} + +func (f *DebugFlags) IsDebug() bool { + return f.debug +} + +// Configures the debug flags for long-running commands. +// Must be called before adding the flags to a FlagSet. +func (f *DebugFlags) LongRunning() *DebugFlags { + f.logsToStdout = true + + // The default value won't be reflected in the flag set + // once the flags have been added. + f.verboseByDefault = true + + return f +} + +// Adds the debug flags to the given FlagSet. +func (f *DebugFlags) AddToFlagSet(flags *pflag.FlagSet) { + flags.BoolVarP(&f.verbose, "verbose", "v", f.verboseByDefault, "Verbose logging") + flags.BoolVarP(&f.debug, "debug", "d", false, "Debug logging (implies verbose logging)") + flags.StringVar(&f.debugListenOn, "debugListenOn", ":6060", "Http listenOn for Debug pprof handler") +} + +// Adds the debug flags to the given FlagSet when in "kubectl" mode. +// This won't use shorthands, as this will interfere with kubectl's flags. +func (f *DebugFlags) AddToKubectlFlagSet(flags *pflag.FlagSet) { + debugDefault := false + if v, ok := os.LookupEnv("DEBUG"); ok { + debugDefault, _ = strconv.ParseBool(v) + } + + flags.BoolVar(&f.debug, "debug", debugDefault, "Debug logging [$DEBUG]") +} + +func (f *DebugFlags) Run(cmd *cobra.Command, _ []string) { + if f.logsToStdout { + logrus.SetOutput(cmd.OutOrStdout()) + } + + switch { + case f.debug: + internallog.SetDebugLevel() + + if f.verbose { + if !f.verboseByDefault { + logrus.Debug("--debug already implies --verbose") + } + } else if f.verboseByDefault { + logrus.Debug("--debug overrides --verbose=false") + } + + go func() { + log := logrus.WithField("debug_server", f.debugListenOn) + log.Debug("Starting debug server") + if err := http.ListenAndServe(f.debugListenOn, nil); !errors.Is(err, http.ErrServerClosed) { + log.WithError(err).Debug("Failed to start debug server") + } else { + log.Debug("Debug server closed") + } + }() + + case f.verbose: + internallog.SetInfoLevel() + } +} diff --git a/cmd/kubeconfig/kubeconfig.go b/cmd/kubeconfig/kubeconfig.go index 869a0d257067..66ef9eebc07a 100644 --- a/cmd/kubeconfig/kubeconfig.go +++ b/cmd/kubeconfig/kubeconfig.go @@ -17,6 +17,7 @@ limitations under the License. package kubeconfig import ( + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/config" "github.com/spf13/cobra" @@ -24,17 +25,22 @@ import ( ) func NewKubeConfigCmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ - Use: "kubeconfig [command]", - Short: "Create a kubeconfig file for a specified user", - Args: cobra.NoArgs, - RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation + Use: "kubeconfig [command]", + Short: "Create a kubeconfig file for a specified user", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, + RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation } + pflags := cmd.PersistentFlags() + debugFlags.AddToFlagSet(pflags) config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { f.Hidden = true f.Deprecated = "it has no effect and will be removed in a future release" - cmd.PersistentFlags().AddFlag(f) + pflags.AddFlag(f) }) cmd.AddCommand(kubeconfigCreateCmd()) diff --git a/cmd/kubectl/kubectl.go b/cmd/kubectl/kubectl.go index 5fa636b5df66..bb09ae99f351 100644 --- a/cmd/kubectl/kubectl.go +++ b/cmd/kubectl/kubectl.go @@ -26,9 +26,11 @@ import ( "slices" "strings" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/config" "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/component-base/logs" kubectl "k8s.io/kubectl/pkg/cmd" "k8s.io/kubectl/pkg/cmd/plugin" @@ -70,7 +72,7 @@ func (h *kubectlPluginHandler) Execute(executablePath string, cmdArgs, environme func NewK0sKubectlCmd() *cobra.Command { // Create a new kubectl command without a plugin handler. kubectlCmd := kubectl.NewKubectlCommand(kubectl.KubectlOptions{ - IOStreams: genericclioptions.IOStreams{ + IOStreams: genericiooptions.IOStreams{ In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr, @@ -104,6 +106,7 @@ func hookKubectlPluginHandler(kubectlCmd *cobra.Command) { // Intercept kubectl's PreRunE, so that generic k0s flags are honored and // kubectl plugins may be handled properly, e.g. so that `k0s kc foo bar` // works as expected when there's a `kubectl-foo` plugin installed. + var debugFlags internal.DebugFlags originalPreRunE := kubectlCmd.PersistentPreRunE kubectlCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { handleKubectlPlugins(kubectlCmd) @@ -126,9 +129,7 @@ func hookKubectlPluginHandler(kubectlCmd *cobra.Command) { logs.InitLogs() cobra.OnFinalize(logs.FlushLogs) - if err := config.CallParentPersistentPreRun(kubectlCmd, args); err != nil { - return err - } + debugFlags.Run(kubectlCmd, args) if err := fallbackToK0sKubeconfig(cmd); err != nil { return err @@ -136,6 +137,8 @@ func hookKubectlPluginHandler(kubectlCmd *cobra.Command) { return originalPreRunE(cmd, args) } + + debugFlags.AddToKubectlFlagSet(kubectlCmd.PersistentFlags()) } // handleKubectlPlugins calls kubectl's plugin handler and execs the plugin diff --git a/cmd/reset/reset.go b/cmd/reset/reset.go index 47bf8f871a12..065df7090085 100644 --- a/cmd/reset/reset.go +++ b/cmd/reset/reset.go @@ -23,6 +23,7 @@ import ( "fmt" "os" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/cleanup" "github.com/k0sproject/k0s/pkg/component/status" "github.com/k0sproject/k0s/pkg/config" @@ -34,20 +35,25 @@ import ( type command config.CLIOptions func NewResetCmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ - Use: "reset", - Short: "Uninstall k0s. Must be run as root (or with sudo)", - Args: cobra.NoArgs, + Use: "reset", + Short: "Uninstall k0s. Must be run as root (or with sudo)", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { return err } c := (*command)(opts) - return c.reset() + return c.reset(debugFlags.IsDebug()) }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.AddFlagSet(config.GetCriSocketFlag()) @@ -57,7 +63,7 @@ func NewResetCmd() *cobra.Command { return cmd } -func (c *command) reset() error { +func (c *command) reset(debug bool) error { if os.Geteuid() != 0 { return errors.New("this command must be run as root!") } @@ -76,7 +82,7 @@ func (c *command) reset() error { } // Get Cleanup Config - cfg, err := cleanup.NewConfig(c.Debug, c.K0sVars, nodeCfg.Spec.Install.SystemUsers, c.WorkerOptions.CriSocket) + cfg, err := cleanup.NewConfig(debug, c.K0sVars, nodeCfg.Spec.Install.SystemUsers, c.WorkerOptions.CriSocket) if err != nil { return fmt.Errorf("failed to configure cleanup: %w", err) } diff --git a/cmd/restore/restore_unix.go b/cmd/restore/restore_unix.go index a8e4b0ea0846..d10e665bd753 100644 --- a/cmd/restore/restore_unix.go +++ b/cmd/restore/restore_unix.go @@ -26,6 +26,7 @@ import ( "path/filepath" "strings" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/internal/pkg/dir" "github.com/k0sproject/k0s/internal/pkg/file" "github.com/k0sproject/k0s/pkg/backup" @@ -43,12 +44,16 @@ type command struct { } func NewRestoreCmd() *cobra.Command { - var restoredConfigPath string + var ( + debugFlags internal.DebugFlags + restoredConfigPath string + ) cmd := &cobra.Command{ - Use: "restore filename", - Short: "restore k0s state from given backup archive. Use '-' as filename to read from stdin. Must be run as root (or with sudo)", - Args: cobra.ExactArgs(1), + Use: "restore filename", + Short: "restore k0s state from given backup archive. Use '-' as filename to read from stdin. Must be run as root (or with sudo)", + Args: cobra.ExactArgs(1), + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, args []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -61,6 +66,8 @@ func NewRestoreCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.StringVar(&restoredConfigPath, "config-out", "", "Specify desired name and full path for the restored k0s.yaml file (default: k0s_.yaml") diff --git a/cmd/root.go b/cmd/root.go index cd224bee8ae7..187d8e2d9952 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,12 +18,11 @@ package cmd import ( "errors" - "net/http" "os" "github.com/k0sproject/k0s/cmd/airgap" "github.com/k0sproject/k0s/cmd/api" - configcmd "github.com/k0sproject/k0s/cmd/config" + "github.com/k0sproject/k0s/cmd/config" "github.com/k0sproject/k0s/cmd/ctr" "github.com/k0sproject/k0s/cmd/etcd" "github.com/k0sproject/k0s/cmd/install" @@ -36,11 +35,8 @@ import ( "github.com/k0sproject/k0s/cmd/validate" "github.com/k0sproject/k0s/cmd/version" "github.com/k0sproject/k0s/cmd/worker" - internallog "github.com/k0sproject/k0s/internal/pkg/log" "github.com/k0sproject/k0s/pkg/build" - "github.com/k0sproject/k0s/pkg/config" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" ) @@ -52,33 +48,12 @@ func NewRootCmd() *cobra.Command { Use: "k0s", Short: "k0s - Zero Friction Kubernetes", SilenceUsage: true, - - PersistentPreRun: func(cmd *cobra.Command, args []string) { - if config.Verbose { - internallog.SetInfoLevel() - } - - if config.Debug { - // TODO: check if it actually works and is not overwritten by something else - internallog.SetDebugLevel() - - go func() { - log := logrus.WithField("debug_server", config.DebugListenOn) - log.Debug("Starting debug server") - if err := http.ListenAndServe(config.DebugListenOn, nil); !errors.Is(err, http.ErrServerClosed) { - log.WithError(err).Debug("Failed to start debug server") - } else { - log.Debug("Debug server closed") - } - }() - } - }, } cmd.AddCommand(airgap.NewAirgapCmd()) cmd.AddCommand(api.NewAPICmd()) cmd.AddCommand(ctr.NewCtrCommand()) - cmd.AddCommand(configcmd.NewConfigCmd()) + cmd.AddCommand(config.NewConfigCmd()) cmd.AddCommand(etcd.NewEtcdCmd()) cmd.AddCommand(install.NewInstallCmd()) cmd.AddCommand(kubeconfig.NewKubeConfigCmd()) @@ -125,7 +100,7 @@ func newDocsCmd() *cobra.Command { } func newDefaultConfigCmd() *cobra.Command { - cmd := configcmd.NewCreateCmd() + cmd := config.NewCreateCmd() cmd.Hidden = true cmd.Deprecated = "use 'k0s config create' instead" cmd.Use = "default-config" diff --git a/cmd/start/start.go b/cmd/start/start.go index 46197c08a0f7..42a992c65f4f 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -21,6 +21,7 @@ import ( "os" "runtime" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/install" "github.com/kardianos/service" @@ -28,10 +29,13 @@ import ( ) func NewStartCmd() *cobra.Command { - return &cobra.Command{ - Use: "start", - Short: "Start the k0s service configured on this host. Must be run as root (or with sudo)", - Args: cobra.NoArgs, + var debugFlags internal.DebugFlags + + cmd := &cobra.Command{ + Use: "start", + Short: "Start the k0s service configured on this host. Must be run as root (or with sudo)", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { if runtime.GOOS != "windows" && os.Geteuid() != 0 { return errors.New("this command must be run as root") @@ -48,4 +52,7 @@ func NewStartCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + + return cmd } diff --git a/cmd/status/status.go b/cmd/status/status.go index a743ded8f726..3d4d0cb61ec1 100644 --- a/cmd/status/status.go +++ b/cmd/status/status.go @@ -23,6 +23,7 @@ import ( "fmt" "io" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/component/status" "github.com/k0sproject/k0s/pkg/config" @@ -31,13 +32,17 @@ import ( ) func NewStatusCmd() *cobra.Command { - var output string + var ( + debugFlags internal.DebugFlags + output string + ) cmd := &cobra.Command{ - Use: "status", - Short: "Get k0s instance status information", - Example: `The command will return information about system init, PID, k0s role, kubeconfig and similar.`, - Args: cobra.NoArgs, + Use: "status", + Short: "Get k0s instance status information", + Example: `The command will return information about system init, PID, k0s role, kubeconfig and similar.`, + PersistentPreRun: debugFlags.Run, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -57,7 +62,9 @@ func NewStatusCmd() *cobra.Command { }, } - cmd.PersistentFlags().String("status-socket", "", "Full file path to the socket file. (default: /status.sock)") + pflags := cmd.PersistentFlags() + debugFlags.AddToFlagSet(pflags) + pflags.String("status-socket", "", "Full file path to the socket file. (default: /status.sock)") cmd.AddCommand(NewStatusSubCmdComponents()) diff --git a/cmd/stop/stop.go b/cmd/stop/stop.go index 7c1368505186..bac1d5ba65e9 100644 --- a/cmd/stop/stop.go +++ b/cmd/stop/stop.go @@ -21,6 +21,7 @@ import ( "os" "runtime" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/install" "github.com/kardianos/service" @@ -28,10 +29,13 @@ import ( ) func NewStopCmd() *cobra.Command { - return &cobra.Command{ - Use: "stop", - Short: "Stop the k0s service configured on this host. Must be run as root (or with sudo)", - Args: cobra.NoArgs, + var debugFlags internal.DebugFlags + + cmd := &cobra.Command{ + Use: "stop", + Short: "Stop the k0s service configured on this host. Must be run as root (or with sudo)", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { if runtime.GOOS != "windows" && os.Geteuid() != 0 { return errors.New("this command must be run as root") @@ -51,4 +55,7 @@ func NewStopCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + + return cmd } diff --git a/cmd/sysinfo/sysinfo.go b/cmd/sysinfo/sysinfo.go index 160d585ddfff..d58cf1fcb030 100644 --- a/cmd/sysinfo/sysinfo.go +++ b/cmd/sysinfo/sysinfo.go @@ -23,6 +23,7 @@ import ( "io" "strings" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/internal/pkg/sysinfo" "github.com/k0sproject/k0s/internal/pkg/sysinfo/probes" "github.com/k0sproject/k0s/pkg/constant" @@ -34,15 +35,18 @@ import ( ) func NewSysinfoCmd() *cobra.Command { - - var sysinfoSpec sysinfo.K0sSysinfoSpec - var outputFormat string + var ( + debugFlags internal.DebugFlags + sysinfoSpec sysinfo.K0sSysinfoSpec + outputFormat string + ) cmd := &cobra.Command{ - Use: "sysinfo", - Short: "Display system information", - Long: `Runs k0s's pre-flight checks and issues the results to stdout.`, - Args: cobra.NoArgs, + Use: "sysinfo", + Short: "Display system information", + Long: `Runs k0s's pre-flight checks and issues the results to stdout.`, + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { sysinfoSpec.AddDebugProbes = true probes := sysinfoSpec.NewSysinfoProbes() @@ -76,6 +80,8 @@ func NewSysinfoCmd() *cobra.Command { }, } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + // append flags flags := cmd.Flags() flags.BoolVar(&sysinfoSpec.ControllerRoleEnabled, "controller", true, "Include controller-specific sysinfo") diff --git a/cmd/token/preshared.go b/cmd/token/preshared.go index 231d79952aeb..9694fdb99688 100644 --- a/cmd/token/preshared.go +++ b/cmd/token/preshared.go @@ -19,7 +19,6 @@ package token import ( "bufio" "bytes" - "errors" "fmt" "io" "os" @@ -28,6 +27,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/serializer/json" + "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes/scheme" bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1" @@ -36,6 +36,7 @@ import ( "github.com/k0sproject/k0s/pkg/token" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func preSharedCmd() *cobra.Command { @@ -52,38 +53,29 @@ func preSharedCmd() *cobra.Command { Short: "Generates token and secret and stores them as a files", Example: `k0s token pre-shared --role worker --cert //ca.crt --url https://:/`, Args: cobra.NoArgs, - PreRunE: func(cmd *cobra.Command, _ []string) error { - err := checkTokenRole(preSharedRole) - if err != nil { + RunE: func(cmd *cobra.Command, _ []string) error { + if err := checkTokenRole(preSharedRole); err != nil { return err } - if certPath == "" { - return errors.New("please, provide --cert argument") - } - if joinURL == "" { - return errors.New("please, provide --url argument") - } - return nil - }, - RunE: func(cmd *cobra.Command, _ []string) error { t, err := createSecret(preSharedRole, validity, outDir) if err != nil { return err } - err = createKubeConfig(t, preSharedRole, joinURL, certPath, outDir) - if err != nil { - return err - } - return nil + return createKubeConfig(t, preSharedRole, joinURL, certPath, outDir) }, } flags := cmd.Flags() - flags.AddFlagSet(config.GetPersistentFlagSet()) + config.GetPersistentFlagSet().VisitAll(func(f *pflag.Flag) { + f.Hidden = true + flags.AddFlag(f) + }) flags.StringVar(&certPath, "cert", "", "path to the CA certificate file") + runtime.Must(cobra.MarkFlagRequired(flags, "cert")) flags.StringVar(&joinURL, "url", "", "url of the api server to join") + runtime.Must(cobra.MarkFlagRequired(flags, "url")) flags.StringVar(&preSharedRole, "role", "worker", "token role. valid values: worker, controller. Default: worker") flags.StringVar(&outDir, "out", ".", "path to the output directory. Default: current dir") flags.DurationVar(&validity, "valid", 0, "how long token is valid, in Go duration format") diff --git a/cmd/token/token.go b/cmd/token/token.go index 2df982b3de2d..094c210bca3b 100644 --- a/cmd/token/token.go +++ b/cmd/token/token.go @@ -19,6 +19,7 @@ package token import ( "fmt" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/pkg/token" "github.com/spf13/cobra" @@ -26,13 +27,18 @@ import ( ) func NewTokenCmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ - Use: "token", - Short: "Manage join tokens", - Args: cobra.NoArgs, - RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation + Use: "token", + Short: "Manage join tokens", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, + RunE: func(*cobra.Command, []string) error { return pflag.ErrHelp }, // Enforce arg validation } + debugFlags.AddToFlagSet(cmd.PersistentFlags()) + cmd.AddCommand(tokenListCmd()) cmd.AddCommand(tokenInvalidateCmd()) cmd.AddCommand(preSharedCmd()) diff --git a/cmd/worker/worker.go b/cmd/worker/worker.go index 79d8b7fe8b0d..6d89d2c743c0 100644 --- a/cmd/worker/worker.go +++ b/cmd/worker/worker.go @@ -25,8 +25,8 @@ import ( "runtime" "syscall" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/internal/pkg/flags" - internallog "github.com/k0sproject/k0s/internal/pkg/log" "github.com/k0sproject/k0s/internal/pkg/stringmap" "github.com/k0sproject/k0s/internal/pkg/sysinfo" "github.com/k0sproject/k0s/pkg/component/iptables" @@ -54,7 +54,10 @@ type EmbeddingController interface { } func NewWorkerCmd() *cobra.Command { - var ignorePreFlightChecks bool + var ( + debugFlags internal.DebugFlags + ignorePreFlightChecks bool + ) cmd := &cobra.Command{ Use: "worker [join-token]", @@ -66,12 +69,8 @@ func NewWorkerCmd() *cobra.Command { or CLI flag: $ k0s worker --token-file [path_to_file] Note: Token can be passed either as a CLI argument or as a flag`, - Args: cobra.MaximumNArgs(1), - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - logrus.SetOutput(cmd.OutOrStdout()) - internallog.SetInfoLevel() - return config.CallParentPersistentPreRun(cmd, args) - }, + Args: cobra.MaximumNArgs(1), + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, args []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -108,6 +107,8 @@ func NewWorkerCmd() *cobra.Command { }, } + debugFlags.LongRunning().AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.AddFlagSet(config.GetWorkerFlags()) diff --git a/main.go b/main.go index 007a235e2fad..2fe16510115f 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,6 @@ limitations under the License. package main import ( - _ "net/http/pprof" "os" "path" "strings" diff --git a/pkg/config/cli.go b/pkg/config/cli.go index 8d5348e21fba..79298a4b73d6 100644 --- a/pkg/config/cli.go +++ b/pkg/config/cli.go @@ -18,9 +18,7 @@ package config import ( "fmt" - "os" "slices" - "strconv" "strings" "time" @@ -28,28 +26,21 @@ import ( "github.com/k0sproject/k0s/pkg/constant" "github.com/k0sproject/k0s/pkg/k0scloudprovider" - "github.com/spf13/cobra" "github.com/spf13/pflag" ) var ( - CfgFile string - Debug bool - DebugListenOn string - K0sVars CfgVars - workerOpts WorkerOptions - Verbose bool + CfgFile string + K0sVars CfgVars + workerOpts WorkerOptions ) // This struct holds all the CLI options & settings required by the // different k0s sub-commands type CLIOptions struct { WorkerOptions - CfgFile string - Debug bool - DebugListenOn string - K0sVars *CfgVars - Verbose bool + CfgFile string + K0sVars *CfgVars } type ControllerMode uint8 @@ -221,25 +212,14 @@ func (f *logLevelsFlag) String() string { func GetPersistentFlagSet() *pflag.FlagSet { flagset := &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.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 } -// XX: not a pretty hack, but we need the data-dir flag for the kubectl subcommand -// XX: when other global flags cannot be used (specifically -d and -c) func GetKubeCtlFlagSet() *pflag.FlagSet { - debugDefault := false - if v, ok := os.LookupEnv("DEBUG"); ok { - debugDefault, _ = strconv.ParseBool(v) - } - flagset := &pflag.FlagSet{} flagset.String("data-dir", constant.DataDirDefault, "Data Directory for k0s. DO NOT CHANGE for an existing setup, things will break!") - flagset.BoolVar(&Debug, "debug", debugDefault, "Debug logging [$DEBUG]") return flagset } @@ -333,41 +313,7 @@ func GetCmdOpts(cobraCmd command) (*CLIOptions, error) { return &CLIOptions{ WorkerOptions: workerOpts, - CfgFile: CfgFile, - Debug: Debug, - Verbose: Verbose, - K0sVars: k0sVars, - DebugListenOn: DebugListenOn, + CfgFile: CfgFile, + K0sVars: k0sVars, }, nil } - -// CallParentPersistentPreRun runs the parent command's persistent pre-run. -// Cobra does not do this automatically. -// -// See: https://github.com/spf13/cobra/issues/216 -// See: https://github.com/spf13/cobra/blob/v1.4.0/command.go#L833-L843 -func CallParentPersistentPreRun(cmd *cobra.Command, args []string) error { - for p := cmd.Parent(); p != nil; p = p.Parent() { - preRunE := p.PersistentPreRunE - preRun := p.PersistentPreRun - - p.PersistentPreRunE = nil - p.PersistentPreRun = nil - - defer func() { - p.PersistentPreRunE = preRunE - p.PersistentPreRun = preRun - }() - - if preRunE != nil { - return preRunE(cmd, args) - } - - if preRun != nil { - preRun(cmd, args) - return nil - } - } - - return nil -}