From 7c700bb254c569b47111171bb9f521c940b33c0d Mon Sep 17 00:00:00 2001 From: Denis Tarabrin Date: Fri, 30 May 2025 18:50:00 +0400 Subject: [PATCH 1/3] Add command: d8 k debug Signed-off-by: Denis Tarabrin --- cmd/kubectl.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/cmd/kubectl.go b/cmd/kubectl.go index f4268041..a1be7a3a 100644 --- a/cmd/kubectl.go +++ b/cmd/kubectl.go @@ -17,18 +17,124 @@ limitations under the License. package cmd import ( + "context" + "fmt" + "os" + "time" + "github.com/spf13/cobra" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" cliflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/logs" kubecmd "k8s.io/kubectl/pkg/cmd" ) +const ( + cmNamespace = "d8-system" + cmName = "debug-container" + cmImageKey = "image" +) + +func getKubeConfig(kubeconfigPath string) (*rest.Config, error) { + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + + if kubeconfigPath != "" { + loadingRules.ExplicitPath = kubeconfigPath + } + + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + + config, err := kubeConfig.ClientConfig() + if err != nil { + return nil, fmt.Errorf("failed to load kubernetes config: %w", err) + } + + return config, nil +} + +func getDebugImage(cmd *cobra.Command) (string, error) { + kubeconfigPath, err := cmd.Flags().GetString("kubeconfig") + if err != nil { + return "", fmt.Errorf("failed to get kubeconfig flag: %w", err) + } + + config, err := getKubeConfig(kubeconfigPath) + if err != nil { + return "", fmt.Errorf("failed to setup Kubernetes client: %w", err) + } + + kubeCl, err := kubernetes.NewForConfig(config) + if err != nil { + return "", fmt.Errorf("failed to create Kubernetes client: %w", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + configMap, err := kubeCl.CoreV1().ConfigMaps(cmNamespace).Get(ctx, cmName, v1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("failed to get ConfigMap %s/%s: %w", cmNamespace, cmName, err) + } + + imageName, ok := configMap.Data[cmImageKey] + if !ok { + return "", fmt.Errorf("key '%s' not found in ConfigMap %s/%s", cmImageKey, cmNamespace, cmName) + } + + if imageName == "" { + return "", fmt.Errorf("image name is empty in ConfigMap %s/%s", cmNamespace, cmName) + } + + return imageName, nil +} + func init() { kubectlCmd := kubecmd.NewDefaultKubectlCommand() kubectlCmd.Use = "k" kubectlCmd.Aliases = []string{"kubectl"} kubectlCmd = ReplaceCommandName("kubectl", "d8 k", kubectlCmd) + var debugCmd *cobra.Command + for _, cmd := range kubectlCmd.Commands() { + if cmd.Name() == "debug" { + debugCmd = cmd + break + } + } + + if debugCmd != nil { + if imageFlag := debugCmd.Flags().Lookup("image"); imageFlag != nil { + imageFlag.Usage = "Container image to use for debug container. If not specified, the platform's recommended image will be used." + } + } + + rootCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file") + + originalPersistentPreRunE := kubectlCmd.PersistentPreRunE + kubectlCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + if cmd.Name() == "debug" || (cmd.Parent() != nil && cmd.Parent().Name() == "debug") { + imageFlag := cmd.Flags().Lookup("image") + if imageFlag != nil && imageFlag.Value.String() == "" { + debugImage, err := getDebugImage(cmd) + if err != nil { + fmt.Fprintf(os.Stderr, "Warning: cannot get debug container image from cluster: %v\n", err) + fmt.Fprintf(os.Stderr, "Continuing with default kubectl behavior...\n") + } else { + fmt.Fprintf(os.Stderr, "Using debug container image: %s\n", debugImage) + cmd.Flags().Set("image", debugImage) + } + } + } + + if originalPersistentPreRunE != nil { + return originalPersistentPreRunE(cmd, args) + } + return nil + } + // Based on https://github.com/kubernetes/kubernetes/blob/v1.29.3/staging/src/k8s.io/component-base/cli/run.go#L88 kubectlCmd.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc) From 08c05434f552a27b245c598166899219667a1886 Mon Sep 17 00:00:00 2001 From: Denis Tarabrin Date: Tue, 17 Jun 2025 11:23:52 +0400 Subject: [PATCH 2/3] Corrections to comments Signed-off-by: Denis Tarabrin --- cmd/kubectl.go | 55 +++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/cmd/kubectl.go b/cmd/kubectl.go index a1be7a3a..2042a6d3 100644 --- a/cmd/kubectl.go +++ b/cmd/kubectl.go @@ -18,14 +18,14 @@ package cmd import ( "context" + "errors" "fmt" "os" "time" + "github.com/deckhouse/deckhouse-cli/internal/utilk8s" "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" cliflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/logs" @@ -38,54 +38,47 @@ const ( cmImageKey = "image" ) -func getKubeConfig(kubeconfigPath string) (*rest.Config, error) { +func getDebugImage(cmd *cobra.Command) (string, error) { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - - if kubeconfigPath != "" { - loadingRules.ExplicitPath = kubeconfigPath - } - configOverrides := &clientcmd.ConfigOverrides{} - kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - config, err := kubeConfig.ClientConfig() + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loadingRules, + configOverrides, + ) + + rawConfig, err := kubeConfig.RawConfig() if err != nil { - return nil, fmt.Errorf("failed to load kubernetes config: %w", err) + return "", fmt.Errorf("failed to get raw kubeconfig: %w", err) } - return config, nil -} - -func getDebugImage(cmd *cobra.Command) (string, error) { - kubeconfigPath, err := cmd.Flags().GetString("kubeconfig") - if err != nil { - return "", fmt.Errorf("failed to get kubeconfig flag: %w", err) + currentContext := rawConfig.CurrentContext + if currentContext == "" { + return "", errors.New("no current context in kubeconfig") } - config, err := getKubeConfig(kubeconfigPath) - if err != nil { - return "", fmt.Errorf("failed to setup Kubernetes client: %w", err) + kubeconfigPath := "" + if len(loadingRules.Precedence) > 0 { + kubeconfigPath = loadingRules.Precedence[0] } - kubeCl, err := kubernetes.NewForConfig(config) + _, kubeCl, err := utilk8s.SetupK8sClientSet(kubeconfigPath, currentContext) if err != nil { return "", fmt.Errorf("failed to create Kubernetes client: %w", err) } + var ErrGenericImageFetch = errors.New("Cannot get debug image from cluster, please specify --image explicitly") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() + configMap, err := kubeCl.CoreV1().ConfigMaps(cmNamespace).Get(ctx, cmName, v1.GetOptions{}) if err != nil { - return "", fmt.Errorf("failed to get ConfigMap %s/%s: %w", cmNamespace, cmName, err) + return "", ErrGenericImageFetch } imageName, ok := configMap.Data[cmImageKey] - if !ok { - return "", fmt.Errorf("key '%s' not found in ConfigMap %s/%s", cmImageKey, cmNamespace, cmName) - } - - if imageName == "" { - return "", fmt.Errorf("image name is empty in ConfigMap %s/%s", cmNamespace, cmName) + if !ok || imageName == "" { + return "", ErrGenericImageFetch } return imageName, nil @@ -111,8 +104,6 @@ func init() { } } - rootCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file") - originalPersistentPreRunE := kubectlCmd.PersistentPreRunE kubectlCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { if cmd.Name() == "debug" || (cmd.Parent() != nil && cmd.Parent().Name() == "debug") { @@ -132,7 +123,7 @@ func init() { if originalPersistentPreRunE != nil { return originalPersistentPreRunE(cmd, args) } - return nil + panic("originalPersistentPreRunE is nil, cannot proceed") } // Based on https://github.com/kubernetes/kubernetes/blob/v1.29.3/staging/src/k8s.io/component-base/cli/run.go#L88 From e3448b9c4d8a8b74bc7910a1eba5517fe733a8bf Mon Sep 17 00:00:00 2001 From: Denis Tarabrin Date: Thu, 28 Aug 2025 19:55:14 +0400 Subject: [PATCH 3/3] add some changes Signed-off-by: Denis Tarabrin --- cmd/kubectl.go | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/cmd/kubectl.go b/cmd/kubectl.go index 2042a6d3..bc3deb76 100644 --- a/cmd/kubectl.go +++ b/cmd/kubectl.go @@ -26,7 +26,6 @@ import ( "github.com/deckhouse/deckhouse-cli/internal/utilk8s" "github.com/spf13/cobra" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/clientcmd" cliflag "k8s.io/component-base/cli/flag" "k8s.io/component-base/logs" kubecmd "k8s.io/kubectl/pkg/cmd" @@ -39,32 +38,19 @@ const ( ) func getDebugImage(cmd *cobra.Command) (string, error) { - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - - kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - loadingRules, - configOverrides, - ) - - rawConfig, err := kubeConfig.RawConfig() + kubeconfigPath, err := cmd.Flags().GetString("kubeconfig") if err != nil { - return "", fmt.Errorf("failed to get raw kubeconfig: %w", err) + return "", fmt.Errorf("Failed to setup Kubernetes client: %w", err) } - currentContext := rawConfig.CurrentContext - if currentContext == "" { - return "", errors.New("no current context in kubeconfig") - } - - kubeconfigPath := "" - if len(loadingRules.Precedence) > 0 { - kubeconfigPath = loadingRules.Precedence[0] + contextName, err := cmd.Flags().GetString("context") + if err != nil { + return "", fmt.Errorf("Failed to setup Kubernetes client: %w", err) } - _, kubeCl, err := utilk8s.SetupK8sClientSet(kubeconfigPath, currentContext) + _, kubeCl, err := utilk8s.SetupK8sClientSet(kubeconfigPath, contextName) if err != nil { - return "", fmt.Errorf("failed to create Kubernetes client: %w", err) + return "", fmt.Errorf("Failed to create Kubernetes client: %w", err) } var ErrGenericImageFetch = errors.New("Cannot get debug image from cluster, please specify --image explicitly")