Skip to content

Commit

Permalink
Implements Kn plugins re-using some code from kubectl plugins.
Browse files Browse the repository at this point in the history
This version contains the following:

1. wraps the main root Kn command to support plugin
2. plugins are any executable in kn's config new pluginDir
   variable which defaults to $PATH
3. plugins must have name kn-*
4. 'kn plugin list' sub-command to list found kn plugins
5. skips any kn plugins found with name that match core
   commands, e.g., kn-service would be ignored
6. can execute any valid kn plugins found, e.g.,
   `kn valid` where the plugin file `kn-valid` is in path
   specified in 2.
7. unit tests (using gotest.tools)

And is missing:

1. integration tests
2. plugin install command
3. plugin repository command
4. plugin / Knative server version negotiation
5. anything else we agree on in plugin req doc

I plan to create issues for the things missing so we don't
end up with an even bigger PR. It's already big as is but is a
good MVP as per plugins requirement doc.
  • Loading branch information
maximilien committed Jul 9, 2019
1 parent 2ae037f commit 179a9d3
Show file tree
Hide file tree
Showing 31 changed files with 1,022 additions and 8 deletions.
12 changes: 11 additions & 1 deletion cmd/kn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,22 @@ import (
"os"

"github.com/knative/client/pkg/kn/core"
"github.com/spf13/viper"
)

func init() {
core.InitializeConfig()
}

func main() {
err := core.NewKnCommand().Execute()
defer cleanup()
err := core.NewDefaultKnCommand().Execute()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

func cleanup() {
viper.WriteConfig()
}
2 changes: 2 additions & 0 deletions docs/cmd/kn.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ Eventing: Manage event subscriptions and channels. Connect up event sources.
--config string config file (default is $HOME/.kn/config.yaml)
-h, --help help for kn
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO

* [kn completion](kn_completion.md) - Output shell completion code (default Bash)
* [kn plugin](kn_plugin.md) - Plugin command group
* [kn revision](kn_revision.md) - Revision command group
* [kn route](kn_route.md) - Route command group
* [kn service](kn_service.md) - Service command group
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kn completion [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
34 changes: 34 additions & 0 deletions docs/cmd/kn_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## kn plugin

Plugin command group

### Synopsis

Provides utilities for interacting with kn plugins.

Plugins provide extended functionality that is not part of the major command-line distribution.
Please refer to the documentation and examples for more information about how write your own plugins.

```
kn plugin [flags]
```

### Options

```
-h, --help help for plugin
```

### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO

* [kn](kn.md) - Knative client
* [kn plugin list](kn_plugin_list.md) - List all visible plugin executables on a user's PATH

36 changes: 36 additions & 0 deletions docs/cmd/kn_plugin_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## kn plugin list

List all visible plugin executables on a user's PATH

### Synopsis

List all visible plugin executables on a user's PATH.

Available plugin files are those that are:
- executable
- anywhere on the user's PATH
- begin with "kn-

```
kn plugin list [flags]
```

### Options

```
-h, --help help for list
--name-only If true, display only the binary name of each plugin, rather than its full path
```

### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO

* [kn plugin](kn_plugin.md) - Plugin command group

1 change: 1 addition & 0 deletions docs/cmd/kn_revision.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ kn revision [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_revision_delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ kn revision delete NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_revision_describe.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ kn revision describe NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_revision_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ kn revision list [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_route.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ kn route [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_route_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ kn route list NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ kn service [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ kn service create NAME --image IMAGE [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ kn service delete NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_describe.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ kn service describe NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ kn service list [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ kn service update NAME [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_version.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ kn version [flags]
```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--plugin-dir string kn plugin directory (default is value in kn config or $PATH)
```

### SEE ALSO
Expand Down
102 changes: 102 additions & 0 deletions pkg/kn/commands/plugin/path_verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright © 2018 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugin

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/spf13/cobra"
)

// PathVerifier receives a path and determines if it is valid or not
type PathVerifier interface {
// Verify determines if a given path is valid
Verify(path string) []error
}

// CommandOverrideVerifier verifies that existing kn commands are not overriden
type CommandOverrideVerifier struct {
Root *cobra.Command
SeenPlugins map[string]string
}

// Verify implements PathVerifier and determines if a given path
// is valid depending on whether or not it overwrites an existing
// kn command path, or a previously seen plugin.
func (v *CommandOverrideVerifier) Verify(path string) []error {
if v.Root == nil {
return []error{fmt.Errorf("unable to verify path with nil root")}
}

// extract the plugin binary name
segs := strings.Split(path, "/")
binName := segs[len(segs)-1]

cmdPath := strings.Split(binName, "-")
if len(cmdPath) > 1 {
// the first argument is always "kn" for a plugin binary
cmdPath = cmdPath[1:]
}

errors := []error{}
isExec, err := isExecutable(path)
if err == nil && !isExec {
errors = append(errors, fmt.Errorf("warning: %s identified as a kn plugin, but it is not executable", path))
} else if err != nil {
errors = append(errors, fmt.Errorf("error: unable to identify %s as an executable file: %v", path, err))
}

if existingPath, ok := v.SeenPlugins[binName]; ok {
errors = append(errors, fmt.Errorf("warning: %s is overshadowed by a similarly named plugin: %s", path, existingPath))
} else {
v.SeenPlugins[binName] = path
}

cmd, _, err := v.Root.Find(cmdPath)
if err == nil {
errors = append(errors, fmt.Errorf("warning: %s overwrites existing command: %q", binName, cmd.CommandPath()))
}

return errors
}

// Private functions

func isExecutable(fullPath string) (bool, error) {
info, err := os.Stat(fullPath)
if err != nil {
return false, err
}

if runtime.GOOS == "windows" {
fileExt := strings.ToLower(filepath.Ext(fullPath))

switch fileExt {
case ".bat", ".cmd", ".com", ".exe", ".ps1":
return true, nil
}
return false, nil
}

if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
return true, nil
}

return false, nil
}
Loading

0 comments on commit 179a9d3

Please sign in to comment.