allow re-usability of plugin handler, allow support for multiple valid plugin filename prefixes

pull/564/head
juanvallejo 2019-02-01 11:24:55 -05:00
parent 556c0b8593
commit 0257a2bcee
No known key found for this signature in database
GPG Key ID: 7D2C958002D6448D
3 changed files with 58 additions and 21 deletions

View File

@ -293,7 +293,7 @@ var (
// NewDefaultKubectlCommand creates the `kubectl` command with default arguments
func NewDefaultKubectlCommand() *cobra.Command {
return NewDefaultKubectlCommandWithArgs(&defaultPluginHandler{}, os.Args, os.Stdin, os.Stdout, os.Stderr)
return NewDefaultKubectlCommandWithArgs(NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args, os.Stdin, os.Stdout, os.Stderr)
}
// NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments
@ -310,7 +310,7 @@ 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 := handleEndpointExtensions(pluginHandler, cmdPathPieces); err != nil {
if err := HandlePluginCommand(pluginHandler, cmdPathPieces); err != nil {
fmt.Fprintf(errout, "%v\n", err)
os.Exit(1)
}
@ -324,29 +324,51 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string
// and performing executable filename lookups to search
// for valid plugin files, and execute found plugins.
type PluginHandler interface {
// Lookup receives a potential filename and returns
// a full or relative path to an executable, if one
// exists at the given filename, or an error.
Lookup(filename string) (string, error)
// exists at the given filename, or a boolean false.
// Lookup will iterate over a list of given prefixes
// in order to recognize valid plugin filenames.
// The first filepath to match a prefix is returned.
Lookup(filename string) (string, bool)
// Execute receives an executable's filepath, a slice
// of arguments, and a slice of environment variables
// to relay to the executable.
Execute(executablePath string, cmdArgs, environment []string) error
}
type defaultPluginHandler struct{}
// DefaultPluginHandler implements PluginHandler
type DefaultPluginHandler struct {
ValidPrefixes []string
}
// NewDefaultPluginHandler instantiates the DefaultPluginHandler with a list of
// given filename prefixes used to identify valid plugin filenames.
func NewDefaultPluginHandler(validPrefixes []string) *DefaultPluginHandler {
return &DefaultPluginHandler{
ValidPrefixes: validPrefixes,
}
}
// Lookup implements PluginHandler
func (h *defaultPluginHandler) Lookup(filename string) (string, error) {
return exec.LookPath(filename)
func (h *DefaultPluginHandler) Lookup(filename string) (string, bool) {
for _, prefix := range h.ValidPrefixes {
path, err := exec.LookPath(fmt.Sprintf("%s-%s", prefix, filename))
if err != nil || len(path) == 0 {
continue
}
return path, true
}
return "", false
}
// Execute implements PluginHandler
func (h *defaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error {
func (h *DefaultPluginHandler) Execute(executablePath string, cmdArgs, environment []string) error {
return syscall.Exec(executablePath, cmdArgs, environment)
}
func handleEndpointExtensions(pluginHandler PluginHandler, cmdArgs []string) error {
// HandlePluginCommand receives a pluginHandler and command-line arguments and attempts to find
// a plugin executable on the PATH that satisfies the given arguments.
func HandlePluginCommand(pluginHandler PluginHandler, cmdArgs []string) error {
remainingArgs := []string{} // all "non-flag" arguments
for idx := range cmdArgs {
@ -360,8 +382,8 @@ func handleEndpointExtensions(pluginHandler PluginHandler, cmdArgs []string) err
// attempt to find binary, starting at longest possible name with given cmdArgs
for len(remainingArgs) > 0 {
path, err := pluginHandler.Lookup(fmt.Sprintf("kubectl-%s", strings.Join(remainingArgs, "-")))
if err != nil || len(path) == 0 {
path, found := pluginHandler.Lookup(strings.Join(remainingArgs, "-"))
if !found {
remainingArgs = remainingArgs[:len(remainingArgs)-1]
continue
}

View File

@ -175,32 +175,35 @@ type testPluginHandler struct {
err error
}
func (h *testPluginHandler) Lookup(filename string) (string, error) {
func (h *testPluginHandler) Lookup(filename string) (string, bool) {
// append supported plugin prefix to the filename
filename = fmt.Sprintf("%s-%s", "kubectl", filename)
dir, err := os.Stat(h.pluginsDirectory)
if err != nil {
h.err = err
return "", err
return "", false
}
if !dir.IsDir() {
h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
return "", h.err
return "", false
}
plugins, err := ioutil.ReadDir(h.pluginsDirectory)
if err != nil {
h.err = err
return "", err
return "", false
}
for _, p := range plugins {
if p.Name() == filename {
return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), nil
return fmt.Sprintf("%s/%s", h.pluginsDirectory, p.Name()), true
}
}
h.err = fmt.Errorf("unable to find a plugin executable %q", filename)
return "", h.err
return "", false
}
func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string) error {

View File

@ -49,6 +49,8 @@ var (
- anywhere on the user's PATH
- begin with "kubectl-"
`)
ValidPluginFilenamePrefixes = []string{"kubectl"}
)
func NewCmdPlugin(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
@ -127,12 +129,12 @@ func (o *PluginListOptions) Run() error {
if f.IsDir() {
continue
}
if !strings.HasPrefix(f.Name(), "kubectl-") {
if !hasValidPrefix(f.Name(), ValidPluginFilenamePrefixes) {
continue
}
if isFirstFile {
fmt.Fprintf(o.ErrOut, "The following kubectl-compatible plugins are available:\n\n")
fmt.Fprintf(o.ErrOut, "The following compatible plugins are available:\n\n")
pluginsFound = true
isFirstFile = false
}
@ -262,3 +264,13 @@ func uniquePathsList(paths []string) []string {
}
return newPaths
}
func hasValidPrefix(filepath string, validPrefixes []string) bool {
for _, prefix := range validPrefixes {
if !strings.HasPrefix(filepath, prefix+"-") {
continue
}
return true
}
return false
}