From 6f08f37cb13ec8ac7696159f31cea168ebca67fc Mon Sep 17 00:00:00 2001 From: rxinui Date: Sun, 17 Aug 2025 21:34:03 +0200 Subject: [PATCH] print the current kubectl command encapsulated by kuberc on V(1) Signed-off-by: rxinui Kubernetes-commit: cbb7b29bc24f3c1d90f88602855ee3ca0b74d10a --- pkg/cmd/cmd.go | 9 ++- pkg/kuberc/kuberc.go | 153 ++++++++++++++++++++++---------------- pkg/kuberc/kuberc_test.go | 148 +++++++++++++++++++++--------------- 3 files changed, 188 insertions(+), 122 deletions(-) diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index ff41ff42a..0c582ff33 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -234,7 +234,6 @@ func Command(name string, arg ...string) *exec.Cmd { // Execute implements PluginHandler func (h *DefaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error { - // Windows does not support exec syscall. if runtime.GOOS == "windows" { cmd := Command(executablePath, cmdArgs...) @@ -496,6 +495,14 @@ func NewKubectlCommand(o KubectlOptions) *cobra.Command { cmds.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc) if !cmdutil.KubeRC.IsDisabled() { + existingPreRunE := cmds.PersistentPreRunE + cmds.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + if originalCommandArgs, ok := cmd.Annotations[kuberc.KubeRCOriginalCommandAnnotation]; ok { + originalCommand := fmt.Sprintf("%s %s", cmd.Root().Name(), originalCommandArgs) + klog.V(1).Info(fmt.Sprintf("original command: %q", originalCommand)) + } + return existingPreRunE(cmd, args) + } _, err := pref.Apply(cmds, o.Arguments, o.IOStreams.ErrOut) if err != nil { fmt.Fprintf(o.IOStreams.ErrOut, "error occurred while applying preferences %v\n", err) diff --git a/pkg/kuberc/kuberc.go b/pkg/kuberc/kuberc.go index 350e57c5d..f49886186 100644 --- a/pkg/kuberc/kuberc.go +++ b/pkg/kuberc/kuberc.go @@ -17,6 +17,7 @@ limitations under the License. package kuberc import ( + "bytes" "fmt" "io" "os" @@ -33,7 +34,10 @@ import ( "k8s.io/kubectl/pkg/config" ) -const RecommendedKubeRCFileName = "kuberc" +const ( + RecommendedKubeRCFileName = "kuberc" + KubeRCOriginalCommandAnnotation = "kubectl.kubernetes.io/original-command" +) var ( RecommendedConfigDir = filepath.Join(homedir.HomeDir(), clientcmd.RecommendedHomeDir) @@ -67,10 +71,11 @@ func NewPreferences() PreferencesHandler { } type aliasing struct { - appendArgs []string - prependArgs []string - flags []config.CommandOptionDefault - command *cobra.Command + appendArgs []string + prependArgs []string + flags []config.CommandOptionDefault + command *cobra.Command + originalCommand bytes.Buffer } // AddFlags adds kuberc related flags into the command. @@ -121,7 +126,7 @@ func (p *Preferences) applyOverrides(rootCmd *cobra.Command, kuberc *config.Pref if err != nil { return nil } - + originalCommand := bytes.Buffer{} for _, c := range kuberc.Defaults { parsedCmds := strings.Fields(c.Command) overrideCmd, _, err := rootCmd.Find(parsedCmds) @@ -160,7 +165,14 @@ func (p *Preferences) applyOverrides(rootCmd *cobra.Command, kuberc *config.Pref if err != nil { return fmt.Errorf("could not apply override value %s to flag %s in command %s err: %w", fl.Default, fl.Name, c.Command, err) } + originalCommand.WriteString(fmt.Sprintf(" --%s=%s", fl.Name, fl.Default)) } + // Add annotation to trace back command built with default values set within kuberc + if cmd.Annotations == nil { + cmd.Annotations = make(map[string]string, 1) + } + cmd.Annotations[KubeRCOriginalCommandAnnotation] = strings.Join(args, " ") + originalCommand.String() + originalCommand.Reset() } return nil @@ -179,7 +191,6 @@ func (p *Preferences) applyAliases(rootCmd *cobra.Command, kuberc *config.Prefer } var aliasArgs *aliasing - var commandName string // first "non-flag" arguments var commandIndex int for index, arg := range args[1:] { @@ -213,70 +224,86 @@ func (p *Preferences) applyAliases(rootCmd *cobra.Command, kuberc *config.Prefer newCmd.Aliases = []string{} aliasCmd := &newCmd - aliasArgs = &aliasing{ - prependArgs: alias.PrependArgs, - appendArgs: alias.AppendArgs, - flags: alias.Options, - command: aliasCmd, + if alias.Name == commandName { + aliasArgs = &aliasing{ + prependArgs: alias.PrependArgs, + appendArgs: alias.AppendArgs, + flags: alias.Options, + command: aliasCmd, + originalCommand: bytes.Buffer{}, + } + aliasArgs.originalCommand.WriteString(alias.Command) } - break - } - if aliasArgs == nil { - // pursue with the current behavior. - // This might be a built-in command, external plugin, etc. - return args, nil - } - - rootCmd.AddCommand(aliasArgs.command) - - foundAliasCmd, _, err := rootCmd.Find([]string{commandName}) - if err != nil { - return args, nil - } - - // This function triggers merging the persistent flags in the parent commands. - _ = foundAliasCmd.InheritedFlags() - - allShorthands := make(map[string]struct{}) - foundAliasCmd.Flags().VisitAll(func(flag *pflag.Flag) { - if flag.Shorthand != "" { - allShorthands[flag.Shorthand] = struct{}{} + if aliasArgs == nil { + // pursue with the current behavior. + // This might be a built-in command, external plugin, etc. + return args, nil } - }) - for _, fl := range aliasArgs.flags { - existingFlag := foundAliasCmd.Flag(fl.Name) - if existingFlag == nil { - return args, fmt.Errorf("invalid alias flag %s in alias %s", fl.Name, args[0]) - } - if searchInArgs(existingFlag.Name, existingFlag.Shorthand, allShorthands, args) { - // Don't modify the value implicitly, if it is passed in args explicitly - continue - } - err = foundAliasCmd.Flags().Set(fl.Name, fl.Default) + rootCmd.AddCommand(aliasArgs.command) + + foundAliasCmd, _, err := rootCmd.Find([]string{commandName}) if err != nil { - return args, fmt.Errorf("could not apply value %s to flag %s in alias %s err: %w", fl.Default, fl.Name, args[0], err) + return args, nil } + + // This function triggers merging the persistent flags in the parent commands. + _ = foundAliasCmd.InheritedFlags() + + allShorthands := make(map[string]struct{}) + foundAliasCmd.Flags().VisitAll(func(flag *pflag.Flag) { + if flag.Shorthand != "" { + allShorthands[flag.Shorthand] = struct{}{} + } + }) + + for _, fl := range aliasArgs.flags { + existingFlag := foundAliasCmd.Flag(fl.Name) + if existingFlag == nil { + return args, fmt.Errorf("invalid alias flag %s in alias %s", fl.Name, args[0]) + } + if searchInArgs(existingFlag.Name, existingFlag.Shorthand, allShorthands, args) { + // Don't modify the value implicitly, if it is passed in args explicitly + continue + } + err = foundAliasCmd.Flags().Set(fl.Name, fl.Default) + if err != nil { + return args, fmt.Errorf("could not apply value %s to flag %s in alias %s err: %w", fl.Default, fl.Name, args[0], err) + } + aliasArgs.originalCommand.WriteString(fmt.Sprintf(" --%s=%s", fl.Name, fl.Default)) + } + + if len(aliasArgs.prependArgs) > 0 { + // prependArgs defined in kuberc should be inserted after the alias name. + if commandIndex+1 >= len(args) { + // command is the last item, we simply append just like appendArgs + args = append(args, aliasArgs.prependArgs...) + } else { + args = append(args[:commandIndex+1], append(aliasArgs.prependArgs, args[commandIndex+1:]...)...) + } + } + if len(aliasArgs.appendArgs) > 0 { + // appendArgs defined in kuberc should be appended to actual args. + args = append(args, aliasArgs.appendArgs...) + } + // Cobra (command.go#L1078) appends only root command's args into the actual args and ignores the others. + // We are appending the additional args defined in kuberc in here and + // expect that it will be passed along to the actual command. + rootCmd.SetArgs(args[1:]) + // Remove alias arg to add the remaining arguments that are not flags nor part of the preferences + sanitizedArgs := strings.ReplaceAll(strings.Join(args[1:], " "), foundAliasCmd.Name(), "") + if len(sanitizedArgs) > 0 { + aliasArgs.originalCommand.WriteString(fmt.Sprintf(" %s", sanitizedArgs)) + } + // Add annotation to trace back command built without aliases applied + if aliasArgs.command.Annotations == nil { + aliasArgs.command.Annotations = make(map[string]string, 1) + } + aliasArgs.command.Annotations[KubeRCOriginalCommandAnnotation] = aliasArgs.originalCommand.String() + aliasArgs.originalCommand.Reset() } - if len(aliasArgs.prependArgs) > 0 { - // prependArgs defined in kuberc should be inserted after the alias name. - if commandIndex+1 >= len(args) { - // command is the last item, we simply append just like appendArgs - args = append(args, aliasArgs.prependArgs...) - } else { - args = append(args[:commandIndex+1], append(aliasArgs.prependArgs, args[commandIndex+1:]...)...) - } - } - if len(aliasArgs.appendArgs) > 0 { - // appendArgs defined in kuberc should be appended to actual args. - args = append(args, aliasArgs.appendArgs...) - } - // Cobra (command.go#L1078) appends only root command's args into the actual args and ignores the others. - // We are appending the additional args defined in kuberc in here and - // expect that it will be passed along to the actual command. - rootCmd.SetArgs(args[1:]) return args, nil } diff --git a/pkg/kuberc/kuberc_test.go b/pkg/kuberc/kuberc_test.go index 134d7dbc6..e26d0da5d 100644 --- a/pkg/kuberc/kuberc_test.go +++ b/pkg/kuberc/kuberc_test.go @@ -23,6 +23,7 @@ import ( "os" "path/filepath" "strconv" + "strings" "testing" "github.com/spf13/cobra" @@ -803,55 +804,7 @@ func TestApplyOverride(t *testing.T) { }, }, }, - { - name: "alias ignores command override", - nestedCmds: []fakeCmds[string]{ - { - name: "command1", - flags: []fakeFlag[string]{ - { - name: "firstflag", - value: "test", - }, - }, - }, - }, - args: []string{ - "root", - "alias", - }, - getPreferencesFunc: func(kuberc string, errOut io.Writer) (*config.Preference, error) { - return &config.Preference{ - TypeMeta: metav1.TypeMeta{ - Kind: "Preference", - APIVersion: "kubectl.config.k8s.io/v1alpha1", - }, - Defaults: []config.CommandDefaults{ - { - Command: "command1", - Options: []config.CommandOptionDefault{ - { - Name: "firstflag", - Default: "changed", - }, - }, - }, - }, - Aliases: []config.AliasOverride{ - { - Name: "alias", - Command: "command1", - }, - }, - }, nil - }, - expectedFlags: []fakeFlag[string]{ - { - name: "firstflag", - value: "test", - }, - }, - }, + { name: "alias command override", nestedCmds: []fakeCmds[string]{ @@ -912,10 +865,12 @@ func TestApplyOverride(t *testing.T) { addCommands(rootCmd, test.nestedCmds) pref.getPreferencesFunc = test.getPreferencesFunc errWriter := &bytes.Buffer{} + _, err := pref.Apply(rootCmd, test.args, errWriter) if test.expectedErr == nil && err != nil { t.Fatalf("unexpected error %v\n", err) } + if test.expectedErr != nil { if test.expectedErr.Error() != err.Error() { t.Fatalf("error %s expected but actual is %s", test.expectedErr, err) @@ -936,18 +891,25 @@ func TestApplyOverride(t *testing.T) { if errWriter.String() != "" { t.Fatalf("unexpected error message %s\n", errWriter.String()) } - + // Verify annotation and the original command + originalCommand := actualCmd.Annotations[KubeRCOriginalCommandAnnotation] + if !strings.Contains(originalCommand, actualCmd.Name()) { + t.Fatalf("missing command '%s' in original command '%s'", originalCommand, actualCmd.Name()) + } for _, expectedFlag := range test.expectedFlags { actualFlag := actualCmd.Flag(expectedFlag.name) if actualFlag.Value.String() != expectedFlag.value { t.Fatalf("unexpected flag value expected %s actual %s", expectedFlag.value, actualFlag.Value.String()) } + if !strings.Contains(originalCommand, expectedFlag.value) { + t.Fatalf("missing flag '%s' in original command '%s'", expectedFlag.value, originalCommand) + } } }) } } -func TestApplOverrideBool(t *testing.T) { +func TestApplyOverrideBool(t *testing.T) { tests := []testApplyOverride[bool]{ { name: "command override", @@ -1160,16 +1122,17 @@ func TestApplOverrideBool(t *testing.T) { if err != nil { t.Fatalf("unable to find the command %v\n", err) } - err = actualCmd.ParseFlags(test.args[1:]) if err != nil { t.Fatalf("unexpected error %v\n", err) } - if errWriter.String() != "" { t.Fatalf("unexpected error message %s\n", errWriter.String()) } - + originalCommand := actualCmd.Annotations[KubeRCOriginalCommandAnnotation] + if !strings.Contains(originalCommand, actualCmd.Name()) { + t.Fatalf("missing command '%s' in original command '%s'", actualCmd.Name(), originalCommand) + } for _, expectedFlag := range test.expectedFlags { actualFlag := actualCmd.Flag(expectedFlag.name) actualValue, err := strconv.ParseBool(actualFlag.Value.String()) @@ -1179,6 +1142,9 @@ func TestApplOverrideBool(t *testing.T) { if actualValue != expectedFlag.value { t.Fatalf("unexpected flag value expected %t actual %s", expectedFlag.value, actualFlag.Value.String()) } + if !strings.Contains(originalCommand, actualFlag.Shorthand) { + t.Fatalf("missing flag '%s' in original command '%s'", actualFlag.Name, originalCommand) + } } }) } @@ -1465,7 +1431,7 @@ func TestApplyAliasBool(t *testing.T) { if test.expectedCmd != actualCmd.Name() { t.Fatalf("unexpected command expected %s actual %s", test.expectedCmd, actualCmd.Name()) } - + var originalCommand string for _, expectedFlag := range test.expectedFlags { actualFlag := actualCmd.Flag(expectedFlag.name) actualValue, err := strconv.ParseBool(actualFlag.Value.String()) @@ -1475,6 +1441,10 @@ func TestApplyAliasBool(t *testing.T) { if actualValue != expectedFlag.value { t.Fatalf("unexpected flag value expected %t actual %s", expectedFlag.value, actualFlag.Value.String()) } + originalCommand = actualCmd.Annotations[KubeRCOriginalCommandAnnotation] + if !strings.Contains(originalCommand, expectedFlag.shorthand) { + t.Fatalf("missing command '%s' in original command '%s'", expectedFlag.shorthand, originalCommand) + } } for _, expectedArg := range test.expectedArgs { @@ -1488,6 +1458,9 @@ func TestApplyAliasBool(t *testing.T) { if !found { t.Fatalf("expected arg %s can not be found", expectedArg) } + if !strings.Contains(originalCommand, expectedArg) { + t.Fatalf("missing command '%s' in original command '%s'", expectedArg, originalCommand) + } } }) } @@ -1779,7 +1752,7 @@ func TestApplyAlias(t *testing.T) { }, }, { - name: "command override prependArgs with appendArgs with args with flagas", + name: "command override prependArgs with appendArgs with args with flags", nestedCmds: []fakeCmds[string]{ { name: "command1", @@ -2564,6 +2537,59 @@ func TestApplyAlias(t *testing.T) { "nodes", }, }, + { + name: "alias ignores command override", + nestedCmds: []fakeCmds[string]{ + { + name: "command1", + flags: []fakeFlag[string]{ + { + name: "firstflag", + value: "test", + }, + }, + }, + }, + args: []string{ + "root", + "alias", + "--firstflag", + "test", + }, + getPreferencesFunc: func(kuberc string, errOut io.Writer) (*config.Preference, error) { + return &config.Preference{ + TypeMeta: metav1.TypeMeta{ + Kind: "Preference", + APIVersion: "kubectl.config.k8s.io/v1alpha1", + }, + Defaults: []config.CommandDefaults{ + { + Command: "command1", + Options: []config.CommandOptionDefault{ + { + Name: "firstflag", + Default: "changed", + }, + }, + }, + }, + Aliases: []config.AliasOverride{ + { + Name: "alias", + Command: "command1", + }, + }, + }, nil + }, + expectedFlags: []fakeFlag[string]{ + { + name: "firstflag", + value: "test", + }, + }, + expectedCmd: "alias", + expectedArgs: []string{}, + }, } for _, test := range tests { @@ -2608,12 +2634,16 @@ func TestApplyAlias(t *testing.T) { if test.expectedCmd != actualCmd.Name() { t.Fatalf("unexpected command expected %s actual %s", test.expectedCmd, actualCmd.Name()) } - + var originalCommand string for _, expectedFlag := range test.expectedFlags { actualFlag := actualCmd.Flag(expectedFlag.name) if actualFlag.Value.String() != expectedFlag.value { t.Fatalf("unexpected flag value expected %s actual %s", expectedFlag.value, actualFlag.Value.String()) } + originalCommand = actualCmd.Annotations[KubeRCOriginalCommandAnnotation] + if !strings.Contains(originalCommand, actualFlag.Value.String()) { + t.Fatalf("missing flag '%s' in original command '%s'", actualFlag.Value.String(), originalCommand) + } } for _, expectedArg := range test.expectedArgs { @@ -2627,6 +2657,9 @@ func TestApplyAlias(t *testing.T) { if !found { t.Fatalf("expected arg %s can not be found", expectedArg) } + if !strings.Contains(originalCommand, expectedArg) { + t.Fatalf("missing command '%s' in original command '%s'", expectedArg, originalCommand) + } } }) } @@ -2709,7 +2742,6 @@ func addCommands[T supportedTypes](rootCmd *cobra.Command, commands []fakeCmds[T subCmd.Flags().Bool(flg.name, v, "") } } - } rootCmd.AddCommand(subCmd)