From 104340ee1bf00f1e9534d6854747c2c065eaced6 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Thu, 9 Jan 2025 17:00:27 +0100 Subject: [PATCH] Encapsulate debug flag handling Introduce internal.DebugFlags to replace debug and verbose logging flag handling. Move the flag definitions from the "PersistentFlagSet" to a struct method. Remove the debug flag handling from the root command and move it to the subcommands. This allows for the removal of CallPersistentPreRun, since the commands no longer rely on proper chaining of the PersistentPreRun hooks. Signed-off-by: Tom Wieczorek --- cmd/airgap/listimages.go | 17 +++-- cmd/api/api.go | 18 ++--- cmd/backup/backup_unix.go | 15 +++-- cmd/config/config.go | 17 +++-- cmd/config/create.go | 16 +++-- cmd/config/validate.go | 7 +- cmd/controller/controller.go | 25 +++---- cmd/controller/controller_test.go | 4 +- cmd/etcd/etcd.go | 11 ++-- cmd/install/controller_test.go | 10 +-- cmd/install/install.go | 16 +++-- cmd/internal/debug.go | 105 ++++++++++++++++++++++++++++++ cmd/kubeconfig/kubeconfig.go | 16 +++-- cmd/kubectl/kubectl.go | 11 ++-- cmd/reset/reset.go | 18 +++-- cmd/restore/restore_unix.go | 15 +++-- cmd/root.go | 29 +-------- cmd/start/start.go | 15 +++-- cmd/status/status.go | 19 ++++-- cmd/stop/stop.go | 15 +++-- cmd/sysinfo/sysinfo.go | 18 +++-- cmd/token/preshared.go | 30 ++++----- cmd/token/token.go | 14 ++-- cmd/worker/worker.go | 17 ++--- main.go | 1 - pkg/config/cli.go | 62 ++---------------- 26 files changed, 329 insertions(+), 212 deletions(-) create mode 100644 cmd/internal/debug.go 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 18db4e07a3ba..2264f5282fc0 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -29,7 +29,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" @@ -50,15 +50,13 @@ type command struct { } func NewAPICmd() *cobra.Command { + var debugFlags internal.DebugFlags + cmd := &cobra.Command{ - Use: "api", - Short: "Run the controller API", - Args: cobra.NoArgs, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - logrus.SetOutput(cmd.OutOrStdout()) - internallog.SetInfoLevel() - return config.CallParentPersistentPreRun(cmd, args) - }, + Use: "api", + Short: "Run the controller API", + Args: cobra.NoArgs, + PersistentPreRun: debugFlags.Run, RunE: func(cmd *cobra.Command, _ []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { @@ -68,6 +66,8 @@ func NewAPICmd() *cobra.Command { }, } + debugFlags.LongRunning().AddToFlagSet(cmd.PersistentFlags()) + cmd.Flags().AddFlagSet(config.GetPersistentFlagSet()) return cmd diff --git a/cmd/backup/backup_unix.go b/cmd/backup/backup_unix.go index 2cd05adc0a0a..a5cdf7207dab 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" "github.com/k0sproject/k0s/pkg/backup" "github.com/k0sproject/k0s/pkg/component/status" @@ -37,12 +38,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 { @@ -60,6 +65,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 9036d803c085..80c44891b9ce 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/sysinfo" "github.com/k0sproject/k0s/internal/sync/value" "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" @@ -70,7 +70,10 @@ import ( type command config.CLIOptions func NewControllerCmd() *cobra.Command { - var ignorePreFlightChecks bool + var ( + debugFlags internal.DebugFlags + ignorePreFlightChecks bool + ) cmd := &cobra.Command{ Use: "controller [join-token]", @@ -83,12 +86,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 { @@ -117,10 +116,12 @@ func NewControllerCmd() *cobra.Command { ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM) defer cancel() - return c.start(ctx) + return c.start(ctx, debugFlags.IsDebug()) }, } + debugFlags.LongRunning().AddToFlagSet(cmd.PersistentFlags()) + flags := cmd.Flags() flags.AddFlagSet(config.GetPersistentFlagSet()) flags.AddFlagSet(config.GetControllerFlags()) @@ -130,7 +131,7 @@ func NewControllerCmd() *cobra.Command { return cmd } -func (c *command) start(ctx context.Context) error { +func (c *command) start(ctx context.Context, debug bool) error { perfTimer := performance.NewTimer("controller-start").Buffer().Start() nodeConfig, err := c.K0sVars.NodeConfig() @@ -264,8 +265,8 @@ func (c *command) start(ctx context.Context) error { 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..dfaa730b25fa 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 := config.GetPersistentFlagSet() + 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 b156d1f79c81..c453ffbac732 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, c.WorkerOptions.CriSocket) + cfg, err := cleanup.NewConfig(debug, c.K0sVars, 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 923bbd18c92c..b29f4eb63b05 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" @@ -35,11 +34,8 @@ import ( "github.com/k0sproject/k0s/cmd/token" "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" ) @@ -51,33 +47,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()) 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 3ac0e433e38e..f8e8391d9b2f 100644 --- a/cmd/sysinfo/sysinfo.go +++ b/cmd/sysinfo/sysinfo.go @@ -21,6 +21,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" @@ -31,14 +32,17 @@ import ( ) func NewSysinfoCmd() *cobra.Command { - - var sysinfoSpec sysinfo.K0sSysinfoSpec + var ( + debugFlags internal.DebugFlags + sysinfoSpec sysinfo.K0sSysinfoSpec + ) 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() @@ -60,6 +64,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 769481600877..519815c736d7 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" "github.com/k0sproject/k0s/internal/pkg/file" @@ -35,6 +35,7 @@ import ( "github.com/k0sproject/k0s/pkg/token" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func preSharedCmd() *cobra.Command { @@ -51,38 +52,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 2f1fcea6d771..2a264029777a 100644 --- a/cmd/worker/worker.go +++ b/cmd/worker/worker.go @@ -25,7 +25,7 @@ import ( "runtime" "syscall" - internallog "github.com/k0sproject/k0s/internal/pkg/log" + "github.com/k0sproject/k0s/cmd/internal" "github.com/k0sproject/k0s/internal/pkg/sysinfo" "github.com/k0sproject/k0s/pkg/component/iptables" "github.com/k0sproject/k0s/pkg/component/manager" @@ -44,7 +44,10 @@ import ( type Command config.CLIOptions func NewWorkerCmd() *cobra.Command { - var ignorePreFlightChecks bool + var ( + debugFlags internal.DebugFlags + ignorePreFlightChecks bool + ) cmd := &cobra.Command{ Use: "worker [join-token]", @@ -56,12 +59,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 { @@ -93,6 +92,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 874e32149cf6..c590f0e06b1d 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,17 +26,13 @@ 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 controllerOpts ControllerOptions ) @@ -47,11 +41,8 @@ var ( type CLIOptions struct { WorkerOptions ControllerOptions - CfgFile string - Debug bool - DebugListenOn string - K0sVars *CfgVars - Verbose bool + CfgFile string + K0sVars *CfgVars } // Shared controller cli flags @@ -196,25 +187,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 } @@ -314,41 +294,7 @@ func GetCmdOpts(cobraCmd command) (*CLIOptions, error) { ControllerOptions: controllerOpts, 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 -}