mirror of
https://github.com/kubernetes/kubectl.git
synced 2026-06-13 10:50:17 -04:00
print the current kubectl command encapsulated by kuberc on V(1)
Signed-off-by: rxinui <rainui.ly@gmail.com> Kubernetes-commit: cbb7b29bc24f3c1d90f88602855ee3ca0b74d10a
This commit is contained in:
parent
0b7306837f
commit
6f08f37cb1
3 changed files with 188 additions and 122 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue