Skip to content

Commit

Permalink
Implements Kn plugins re-using some code from kubectl plugins. (#249)
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 authored and knative-prow-robot committed Jul 26, 2019
1 parent df816e6 commit 59b2855
Show file tree
Hide file tree
Showing 35 changed files with 1,675 additions and 44 deletions.
16 changes: 15 additions & 1 deletion cmd/kn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ import (
"os"

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

func init() {
core.InitializeConfig()
}

var err error

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() {
if err == nil {
viper.WriteConfig()
}
}
9 changes: 6 additions & 3 deletions docs/cmd/kn.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ Manage your Knative building blocks:
### Options

```
--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)
--config string kn config file (default is $HOME/.kn/config.yaml)
-h, --help help for kn
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
--lookup-plugins-in-path look for kn plugins in $PATH
--plugins-dir string kn plugins directory (default "~/.kn/plugins")
```

### SEE ALSO

* [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
35 changes: 35 additions & 0 deletions docs/cmd/kn_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## kn plugin

Plugin command group

### Synopsis

Provides utilities for interacting and managing with kn plugins.

Plugins provide extended functionality that is not part of the core kn 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
--lookup-plugins-in-path look for kn plugins in $PATH
--plugins-dir string kn plugins directory (default "~/.kn/plugins")
```

### Options inherited from parent commands

```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

### SEE ALSO

* [kn](kn.md) - Knative client
* [kn plugin list](kn_plugin_list.md) - List all visible plugin executables

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

List all visible plugin executables

### Synopsis

List all visible plugin executables.

Available plugin files are those that are:
- executable
- begin with "kn-
- anywhere on the path specified in Kn's config pluginDir variable, which:
* can be overridden with the --plugin-dir flag

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

### Options

```
-h, --help help for list
--lookup-plugins-in-path look for kn plugins in $PATH
--name-only If true, display only the binary name of each plugin, rather than its full path
--plugins-dir string kn plugins directory (default "~/.kn/plugins")
```

### Options inherited from parent commands

```
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

### SEE ALSO

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

2 changes: 1 addition & 1 deletion docs/cmd/kn_revision.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kn revision [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_revision_delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ kn revision delete NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_revision_describe.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ kn revision describe NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_revision_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ kn revision list [name] [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_route.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kn route [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_route_describe.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ kn route describe NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_route_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ kn route list NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kn service [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ kn service create NAME --image IMAGE [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service_delete.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ kn service delete NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service_describe.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ kn service describe NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ kn service list [name] [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_service_update.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ kn service update NAME [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

Expand Down
2 changes: 1 addition & 1 deletion docs/cmd/kn_version.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ kn version [flags]
### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--config string kn config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

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, string(os.PathSeparator))
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 59b2855

Please sign in to comment.