diff --git a/go.mod b/go.mod index 469341aac..31d1ef096 100644 --- a/go.mod +++ b/go.mod @@ -31,11 +31,11 @@ require ( github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.0.0-20211001003357-dd4141958dfc - k8s.io/apimachinery v0.0.0-20211001003147-df63df3af3fc + k8s.io/api v0.0.0-20211005163402-965a3d4efac6 + k8s.io/apimachinery v0.0.0-20211005152320-968be710e37a k8s.io/cli-runtime v0.0.0-20211001205205-cb123082bee1 - k8s.io/client-go v0.0.0-20211001163650-f71d2554c374 - k8s.io/component-base v0.0.0-20211002003808-8ef22d17745b + k8s.io/client-go v0.0.0-20211005163711-3511ef41b1fb + k8s.io/component-base v0.0.0-20211004123803-799bb6e531c3 k8s.io/component-helpers v0.0.0-20211001004347-11b67914ce96 k8s.io/klog/v2 v2.20.0 k8s.io/kube-openapi v0.0.0-20210817084001-7fbd8d59e5b8 @@ -47,12 +47,12 @@ require ( ) replace ( - k8s.io/api => k8s.io/api v0.0.0-20211001003357-dd4141958dfc - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20211001003147-df63df3af3fc + k8s.io/api => k8s.io/api v0.0.0-20211005163402-965a3d4efac6 + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20211005152320-968be710e37a k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20211001205205-cb123082bee1 - k8s.io/client-go => k8s.io/client-go v0.0.0-20211001163650-f71d2554c374 + k8s.io/client-go => k8s.io/client-go v0.0.0-20211005163711-3511ef41b1fb k8s.io/code-generator => k8s.io/code-generator v0.0.0-20210930223515-ede4574ee351 - k8s.io/component-base => k8s.io/component-base v0.0.0-20211002003808-8ef22d17745b + k8s.io/component-base => k8s.io/component-base v0.0.0-20211004123803-799bb6e531c3 k8s.io/component-helpers => k8s.io/component-helpers v0.0.0-20211001004347-11b67914ce96 k8s.io/metrics => k8s.io/metrics v0.0.0-20211001005724-9be6d708955e ) diff --git a/go.sum b/go.sum index 3a7529463..7e0290d12 100644 --- a/go.sum +++ b/go.sum @@ -902,17 +902,17 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.0.0-20211001003357-dd4141958dfc h1:mEvk8nUryhzsFO3gvz1GOCHKAk9SwmCy1E+TfTtrPdM= -k8s.io/api v0.0.0-20211001003357-dd4141958dfc/go.mod h1:X/EFj3T8/b6pT7Vu0CQfs8hKQGD/NpK6nituBPxPypM= -k8s.io/apimachinery v0.0.0-20211001003147-df63df3af3fc h1:xWJbWTw1QtGvoHnsdF1oI10d2gEBl2yqipw/m1DQjLU= -k8s.io/apimachinery v0.0.0-20211001003147-df63df3af3fc/go.mod h1:RAdi3McqM+9tkYHOyceb4XOeJWm9BCAF4BhZki5iiok= +k8s.io/api v0.0.0-20211005163402-965a3d4efac6 h1:4nid4gaGt4QFtlvh0BuRJukvljt+Mwp7mhFhtNl5A80= +k8s.io/api v0.0.0-20211005163402-965a3d4efac6/go.mod h1:uigk499NtRe0ine9Jz9mxVwAjascSPD/Ojn9xYSyB58= +k8s.io/apimachinery v0.0.0-20211005152320-968be710e37a h1:vUbAbtv8pmpeY1rOEQZjy/ALaYwNKdUi3d6pyq9B7Os= +k8s.io/apimachinery v0.0.0-20211005152320-968be710e37a/go.mod h1:RAdi3McqM+9tkYHOyceb4XOeJWm9BCAF4BhZki5iiok= k8s.io/cli-runtime v0.0.0-20211001205205-cb123082bee1 h1:GvF0XFIO0Cuu5Ly8vnav4vMtzlWGoA5igAh33tUbDkw= k8s.io/cli-runtime v0.0.0-20211001205205-cb123082bee1/go.mod h1:qYViNs18B5H9dPd0sBerg0p7e5BNc43CFWpK4/etB3Y= -k8s.io/client-go v0.0.0-20211001163650-f71d2554c374 h1:BESUWBMp04vEvrBgB9N9WbDLm5L8T8wGu3CSkVoKFpk= -k8s.io/client-go v0.0.0-20211001163650-f71d2554c374/go.mod h1:D3oqLmrdamgyGGdeFnlQ2viGosUBar48jDD5c89k3TA= +k8s.io/client-go v0.0.0-20211005163711-3511ef41b1fb h1:nga8yAXtOwTu2rBXgL2RUbberetndy68JG90SrjS/84= +k8s.io/client-go v0.0.0-20211005163711-3511ef41b1fb/go.mod h1:dZcP7TWKqHJIMu4IsnFfjWNqT8QT8a42HCUk+yTX4uY= k8s.io/code-generator v0.0.0-20210930223515-ede4574ee351/go.mod h1:pxmv1vqS30f8CieYQvITL/Z2lV5G6+/Ze3wRjS3HXFo= -k8s.io/component-base v0.0.0-20211002003808-8ef22d17745b h1:H+/3fRk52/qS0CiBg1irHq3rdrOtTyoZAR9yfKrgDII= -k8s.io/component-base v0.0.0-20211002003808-8ef22d17745b/go.mod h1:k8WbcY75pTS2zc0mVGbSkWIvjrqL51iRgENi98nxCog= +k8s.io/component-base v0.0.0-20211004123803-799bb6e531c3 h1:fFiiTCFhhnvzK2Ijlkvu0lILqg9tedrIBAH1kMKjBAY= +k8s.io/component-base v0.0.0-20211004123803-799bb6e531c3/go.mod h1:k8WbcY75pTS2zc0mVGbSkWIvjrqL51iRgENi98nxCog= k8s.io/component-helpers v0.0.0-20211001004347-11b67914ce96 h1:5fnKBbxL2SG1vEH84wyKuN4KD1b5FrhfMCPj7miopFI= k8s.io/component-helpers v0.0.0-20211001004347-11b67914ce96/go.mod h1:BE8rv6HCr8U9iH8YWO+p/lKTjLDQM6e3Am/K0LxZy28= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index c01899c48..5c13f51fb 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -101,9 +101,25 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string // only look for suitable extension executables if // the specified command does not already exist if _, _, err := cmd.Find(cmdPathPieces); err != nil { - if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil { - fmt.Fprintf(errout, "Error: %v\n", err) - os.Exit(1) + // Also check the commands that will be added by Cobra. + // These commands are only added once rootCmd.Execute() is called, so we + // need to check them explicitly here. + var cmdName string // first "non-flag" arguments + for _, arg := range cmdPathPieces { + if !strings.HasPrefix(arg, "-") { + cmdName = arg + break + } + } + + switch cmdName { + case "help", cobra.ShellCompRequestCmd, cobra.ShellCompNoDescRequestCmd: + // Don't search for a plugin + default: + if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil { + fmt.Fprintf(errout, "Error: %v\n", err) + os.Exit(1) + } } } } diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go index d01cf04fc..f64d84365 100644 --- a/pkg/cmd/cmd_test.go +++ b/pkg/cmd/cmd_test.go @@ -78,6 +78,50 @@ func TestKubectlCommandHandlesPlugins(t *testing.T) { name: "test that a plugin does not execute over an existing command by the same name", args: []string{"kubectl", "version"}, }, + // The following tests make sure that commands added by Cobra cannot be shadowed by a plugin + // See https://github.com/kubernetes/kubectl/issues/1116 + { + name: "test that a plugin does not execute over Cobra's help command", + args: []string{"kubectl", "help"}, + }, + { + name: "test that a plugin does not execute over Cobra's __complete command", + args: []string{"kubectl", cobra.ShellCompRequestCmd}, + }, + { + name: "test that a plugin does not execute over Cobra's __completeNoDesc command", + args: []string{"kubectl", cobra.ShellCompNoDescRequestCmd}, + }, + // The following tests make sure that commands added by Cobra cannot be shadowed by a plugin + // even when a flag is specified first. This can happen when using aliases. + // See https://github.com/kubernetes/kubectl/issues/1119 + { + name: "test that a flag does not break Cobra's help command", + args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", "help"}, + }, + { + name: "test that a flag does not break Cobra's __complete command", + args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompRequestCmd}, + }, + { + name: "test that a flag does not break Cobra's __completeNoDesc command", + args: []string{"kubectl", "--kubeconfig=/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd}, + }, + // As for the previous tests, an alias could add a flag without using the = form. + // We don't support this case as parsing the flags becomes quite complicated (flags + // that take a value, versus flags that don't) + // { + // name: "test that a flag with a space does not break Cobra's help command", + // args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", "help"}, + // }, + // { + // name: "test that a flag with a space does not break Cobra's __complete command", + // args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompRequestCmd}, + // }, + // { + // name: "test that a flag with a space does not break Cobra's __completeNoDesc command", + // args: []string{"kubectl", "--kubeconfig", "/path/to/kubeconfig", cobra.ShellCompNoDescRequestCmd}, + // }, } for _, test := range tests { @@ -92,6 +136,7 @@ func TestKubectlCommandHandlesPlugins(t *testing.T) { }) root := NewDefaultKubectlCommandWithArgs(pluginsHandler, test.args, in, out, errOut) + root.SetOut(out) if err := root.Execute(); err != nil { t.Fatalf("unexpected error: %v", err) }