mirror of https://github.com/k3s-io/k3s
allow re-usability of plugin handler, allow support for multiple valid plugin filename prefixes
parent
556c0b8593
commit
0257a2bcee
|
@ -293,7 +293,7 @@ var (
|
||||||
|
|
||||||
// NewDefaultKubectlCommand creates the `kubectl` command with default arguments
|
// NewDefaultKubectlCommand creates the `kubectl` command with default arguments
|
||||||
func NewDefaultKubectlCommand() *cobra.Command {
|
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
|
// NewDefaultKubectlCommandWithArgs creates the `kubectl` command with arguments
|
||||||
|
@ -310,7 +310,7 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string
|
||||||
// only look for suitable extension executables if
|
// only look for suitable extension executables if
|
||||||
// the specified command does not already exist
|
// the specified command does not already exist
|
||||||
if _, _, err := cmd.Find(cmdPathPieces); err != nil {
|
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)
|
fmt.Fprintf(errout, "%v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -324,29 +324,51 @@ func NewDefaultKubectlCommandWithArgs(pluginHandler PluginHandler, args []string
|
||||||
// and performing executable filename lookups to search
|
// and performing executable filename lookups to search
|
||||||
// for valid plugin files, and execute found plugins.
|
// for valid plugin files, and execute found plugins.
|
||||||
type PluginHandler interface {
|
type PluginHandler interface {
|
||||||
// Lookup receives a potential filename and returns
|
// exists at the given filename, or a boolean false.
|
||||||
// a full or relative path to an executable, if one
|
// Lookup will iterate over a list of given prefixes
|
||||||
// exists at the given filename, or an error.
|
// in order to recognize valid plugin filenames.
|
||||||
Lookup(filename string) (string, error)
|
// The first filepath to match a prefix is returned.
|
||||||
|
Lookup(filename string) (string, bool)
|
||||||
// Execute receives an executable's filepath, a slice
|
// Execute receives an executable's filepath, a slice
|
||||||
// of arguments, and a slice of environment variables
|
// of arguments, and a slice of environment variables
|
||||||
// to relay to the executable.
|
// to relay to the executable.
|
||||||
Execute(executablePath string, cmdArgs, environment []string) error
|
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
|
// Lookup implements PluginHandler
|
||||||
func (h *defaultPluginHandler) Lookup(filename string) (string, error) {
|
func (h *DefaultPluginHandler) Lookup(filename string) (string, bool) {
|
||||||
return exec.LookPath(filename)
|
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
|
// 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)
|
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
|
remainingArgs := []string{} // all "non-flag" arguments
|
||||||
|
|
||||||
for idx := range cmdArgs {
|
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
|
// attempt to find binary, starting at longest possible name with given cmdArgs
|
||||||
for len(remainingArgs) > 0 {
|
for len(remainingArgs) > 0 {
|
||||||
path, err := pluginHandler.Lookup(fmt.Sprintf("kubectl-%s", strings.Join(remainingArgs, "-")))
|
path, found := pluginHandler.Lookup(strings.Join(remainingArgs, "-"))
|
||||||
if err != nil || len(path) == 0 {
|
if !found {
|
||||||
remainingArgs = remainingArgs[:len(remainingArgs)-1]
|
remainingArgs = remainingArgs[:len(remainingArgs)-1]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,32 +175,35 @@ type testPluginHandler struct {
|
||||||
err error
|
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)
|
dir, err := os.Stat(h.pluginsDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.err = err
|
h.err = err
|
||||||
return "", err
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dir.IsDir() {
|
if !dir.IsDir() {
|
||||||
h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
|
h.err = fmt.Errorf("expected %q to be a directory", h.pluginsDirectory)
|
||||||
return "", h.err
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins, err := ioutil.ReadDir(h.pluginsDirectory)
|
plugins, err := ioutil.ReadDir(h.pluginsDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.err = err
|
h.err = err
|
||||||
return "", err
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range plugins {
|
for _, p := range plugins {
|
||||||
if p.Name() == filename {
|
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)
|
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 {
|
func (h *testPluginHandler) Execute(executablePath string, cmdArgs, env []string) error {
|
||||||
|
|
|
@ -49,6 +49,8 @@ var (
|
||||||
- anywhere on the user's PATH
|
- anywhere on the user's PATH
|
||||||
- begin with "kubectl-"
|
- begin with "kubectl-"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
ValidPluginFilenamePrefixes = []string{"kubectl"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCmdPlugin(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
|
func NewCmdPlugin(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
|
||||||
|
@ -127,12 +129,12 @@ func (o *PluginListOptions) Run() error {
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(f.Name(), "kubectl-") {
|
if !hasValidPrefix(f.Name(), ValidPluginFilenamePrefixes) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFirstFile {
|
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
|
pluginsFound = true
|
||||||
isFirstFile = false
|
isFirstFile = false
|
||||||
}
|
}
|
||||||
|
@ -262,3 +264,13 @@ func uniquePathsList(paths []string) []string {
|
||||||
}
|
}
|
||||||
return newPaths
|
return newPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasValidPrefix(filepath string, validPrefixes []string) bool {
|
||||||
|
for _, prefix := range validPrefixes {
|
||||||
|
if !strings.HasPrefix(filepath, prefix+"-") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue