Repository: knative/client
Pull request #902 (2020-06-23 06:39:53)

Add support for internal plugins
This PR adds the possibility for internal plugins. This works by allowing code to add to a global plugin slice `plugins.InternalPlugins`. By default this slice is empty, but for a custom assembly process (outside of this project), this could be leverage to add plugins directly in the code.

The changes are minimal and well tested, but the PR was created a bit in a hurry so apologies for not having an associated issue yet (but I will create on tomorrow).

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/902#" title="Author self-approved">rhuss</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fclient).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/client/blob/master/OWNERS)~~ [rhuss]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1275317809068380162/artifacts/line-cov.html#file1) | 84.1% | 86.9% | 2.8

knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-build-tests | 8b9e26a3544187cc95bced13ef50393911f42ce9 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-build-tests/1275317809068380160) | `/test pull-knative-client-build-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1275322339063500800/artifacts/line-cov.html#file1) | 84.1% | 86.9% | 2.8

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1275846795322200067/artifacts/line-cov.html#file1) | 84.1% | 86.9% | 2.8

rhuss: > Any chance to update the description or link to an issue?
> This is listed as chore. Missing the context I guess.

Ah, sorry. It's kind of an habit to use the [convention commit](https://www.conventionalcommits.org/en/v1.0.0/) spec for doing my commit message, and while creating the PR with `hub` it picked up this message as title by default.

Going to adapt the title, but there is no issue yet. Do you want me to create one or could we discuss the change on this PR ?
rhuss: /hold

Do not merge until we have discussed the topic on an issue (which I will create tomorrow).
knative-prow-robot: @rhuss: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1282562168771842051/artifacts/line-cov.html#file1) | 84.1% | 86.9% | 2.8

rhuss: I'm going to rebase this PR as soon as #929 is merged, but its already ready to review.

Please see also the proposal at #928 that I have opened.

/unhold
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1283023332580528128/artifacts/line-cov.html#file1) | 84.1% | 86.9% | 2.8

knative-prow-robot: @rhuss: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1283695369011597312/artifacts/line-cov.html#file1) | 87.1% | 83.8% | -3.2

knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | b28cf16c11fc63cdd9fc359d85d3eafafdb4f71c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-integration-tests/1283695369015791618) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
maximilien: /test pull-knative-client-integration-tests
maximilien: @rhuss I am not able to checkout your PR. I get the following:\

```bash

➜  client git:(master) gh pr checkout 902
From https://github.com/knative/client
 ! [rejected]          refs/pull/902/head -> pr/internal-plugins  (non-fast-forward)
exit status 1
```

Could it be a need to rebase? OR maybe I need to do something on my side...  not sure. Let me digg some more
rhuss: @maximilien  looks strange. Ok, let me try (but I'm typically not using `gh` but standard `git`)
rhuss: Hmm, I tried it with standard git commands, no issue. Maybe its a problem of `gh` ?

```
git clone https://github.com/knative/client.git
cd client
git remote add rhuss https://github.com/rhuss/knative-client.git
git fetch rhuss
git checkout -b internal-plugins rhuss/pr/internal-plugins
```
rhuss: I was even able to checkout with `gh`. @maximilien maybe you already have a branch `pr/internal-plugins` ? If so I sugges to remove it first with `git branch -D pr/internal-plugins` and then try again ....
maximilien: > git branch -D pr/internal-plugins

Worked! Thank you. Will complete my review today. Best.
rhuss: @maximilien @daisy-ycguo Thanks for the review ! Unfortunately, I wasn't able to add the remaining tests before my PTO (sorry, was just to busy ...) We can either wait until I'm back (then, I probably will have to resolve some conflicts), or we could merge now (as long as it is still mergable) and I will add the missing tests later (promised :)

See ya in 2 weeks ...
maximilien: Enjoy your PTO @rhuss! Talk when back. Cheers 🍻 🏖️ 
rhuss: ... back in town, contiue soon on this one so that it gets included in 0.17 (due next Tuesday)
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1297802491315884034/artifacts/line-cov.html#file1) | 87.1% | 89.8% | 2.8

knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 157968f42c6a090053f9654293486d667b4c185e | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-integration-tests/1297802491315884032) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
rhuss: /retest
knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 157968f42c6a090053f9654293486d667b4c185e | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-integration-tests/1297841373038252034) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
rhuss: /retest

knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 157968f42c6a090053f9654293486d667b4c185e | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-integration-tests/1297901518913015808) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
rhuss: /retest

rhuss: The integration test error is not a flake but because of a change in eventing 0.17. We need to fix this before releasing kn 0.17 regardless of whether we get this PR in or not.
knative-prow-robot: @rhuss: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 157968f42c6a090053f9654293486d667b4c185e | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-integration-tests/1297957765750198272) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/902). [Your PR dashboard](https://gubernator.knative.dev/pr/rhuss).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
rhuss: @maximilien knative now depends on go 1.14, so you need to upgrade your go installation
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-client-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/kn/plugin/manager.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_client/902/pull-knative-client-go-coverage/1298178594400374787/artifacts/line-cov.html#file1) | 87.1% | 89.8% | 2.8

rhuss: @dsimansk @navidshaikh @maximilien I think we are good to merge. I adressed all PR comments, and added some extra testing. Also the CI is fixed (will also fix CI issues of other PRs).

Can I have a lgtm please ? (if there are no objections)
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/902#pullrequestreview-474386341" title="LGTM">navidshaikh</a>*, *<a href="https://github.com/knative/client/pull/902#" title="Author self-approved">rhuss</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fclient).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/client/blob/master/OWNERS)~~ [navidshaikh,rhuss]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 1e77fb07..2c501576 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -73,6 +73,10 @@
 | Add sugar controller to E2E tests
 | https://github.com/knative/client/pull/920[#920]
 
+| 🎁
+| Add support for internal plugins
+| https://github.com/knative/client/pull/880[#880]
+
 | 🎁
 | Add "url" output format to return service url in service describe
 | https://github.com/knative/client/pull/916[#916]
diff --git a/docs/plugins/README.md b/docs/plugins/README.md
index 4f0d1ab4..ccdcfd0d 100644
--- a/docs/plugins/README.md
+++ b/docs/plugins/README.md
@@ -21,3 +21,39 @@ Please refer to the documentation and examples for more information on how to
 write your own plugins.
 
 - [kn plugin](../cmd/kn_plugin.md) - Plugin command group
+
+
+## Plugin Inlining
+
+It is possible to inline plugins that are written in golang.
+The following steps are required:
+
+* In your plugin project, create a implementation of the `Plugin` interface and add it to the global `plugin.InternalPlugins` slice in your `init()` method, like in this example:
+
+```go
+package plugin
+
+import (
+	"knative.dev/client/pkg/kn/plugin"
+)
+
+func init() {
+	plugin.InternalPlugins = append(plugin.InternalPlugins, &myPlugin{})
+}
+```
+
+* In your fork of the kn client, add a file `plugin_register.go` to the root package directory which imports your plugin's implementation package:
+
+```go
+package root
+
+import (
+        _ "github.com/rhuss/myplugin/plugin"
+)
+
+// RegisterInlinePlugins is an empty function which however forces the
+// compiler to run all init() methods of the registered imports
+func RegisterInlinePlugins() {}
+```
+
+* Update you `go.mod` file with the new dependency and build your custom distribution of `kn`
diff --git a/go.sum b/go.sum
index 90f765ec..41691c4a 100644
--- a/go.sum
+++ b/go.sum
@@ -360,6 +360,7 @@ github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi
 github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
 github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@@ -809,6 +810,7 @@ github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qo
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
@@ -818,6 +820,7 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
diff --git a/pkg/kn/config/config.go b/pkg/kn/config/config.go
index 48088e25..22e1fe39 100644
--- a/pkg/kn/config/config.go
+++ b/pkg/kn/config/config.go
@@ -129,7 +129,7 @@ func BootstrapConfig() error {
 	viper.SetConfigFile(GlobalConfig.ConfigFile())
 	viper.AutomaticEnv() // read in environment variables that match
 
-	// Defaults are taken from the parsed flags, which in turn have bootstrapDefaults
+	// Defaults are taken from the parsed flags, which in turn have bootstrap defaults
 	// TODO: Re-enable when legacy handling for plugin config has been removed
 	// For now default handling is happening directly in the getter of GlobalConfig
 	// viper.SetDefault(keyPluginsDirectory, bootstrapDefaults.pluginsDir)
diff --git a/pkg/kn/plugin/manager.go b/pkg/kn/plugin/manager.go
index a136c33f..a4f09cf4 100644
--- a/pkg/kn/plugin/manager.go
+++ b/pkg/kn/plugin/manager.go
@@ -30,6 +30,9 @@ import (
 	"github.com/spf13/cobra"
 )
 
+// Allow plugins to register to this slice for inlining
+var InternalPlugins PluginList
+
 // Interface describing a plugin
 type Plugin interface {
 	// Get the name of the plugin (the file name without extensions)
@@ -99,13 +102,19 @@ func (manager *Manager) FindPlugin(parts []string) (Plugin, error) {
 		return nil, nil
 	}
 
+	// Try to find internal plugin fist
+	plugin := lookupInternalPlugin(parts)
+	if plugin != nil {
+		return plugin, nil
+	}
+
 	// Try to find plugin in pluginsDir
 	pluginDir, err := homedir.Expand(manager.pluginsDir)
 	if err != nil {
 		return nil, err
 	}
 
-	return findMostSpecificPlugin(pluginDir, parts, manager.lookupInPath)
+	return findMostSpecificPluginInPath(pluginDir, parts, manager.lookupInPath)
 }
 
 // ListPlugins lists all plugins that can be found in the plugin directory or in the path (if configured)
@@ -116,7 +125,9 @@ func (manager *Manager) ListPlugins() (PluginList, error) {
 // ListPluginsForCommandGroup lists all plugins that can be found in the plugin directory or in the path (if configured),
 // and which fits to a command group
 func (manager *Manager) ListPluginsForCommandGroup(commandGroupParts []string) (PluginList, error) {
-	var plugins []Plugin
+
+	// Initialize with list of internal plugins
+	var plugins = append([]Plugin{}, filterPluginsByCommandGroup(InternalPlugins, commandGroupParts)...)
 
 	dirs, err := manager.pluginLookupDirectories()
 	if err != nil {
@@ -125,6 +136,9 @@ func (manager *Manager) ListPluginsForCommandGroup(commandGroupParts []string) (
 
 	// Examine all files in possible plugin directories
 	hasSeen := make(map[string]bool)
+	for _, pl := range plugins {
+		hasSeen[pl.Name()] = true
+	}
 	for _, dir := range dirs {
 		files, err := ioutil.ReadDir(dir)
 
@@ -144,12 +158,12 @@ func (manager *Manager) ListPluginsForCommandGroup(commandGroupParts []string) (
 			}
 
 			// Check if plugin matches a command group
-			if !isPartOfCommandGroup(commandGroupParts, f.Name()) {
+			if !isPluginFileNamePartOfCommandGroup(commandGroupParts, f.Name()) {
 				continue
 			}
 
 			// Ignore all plugins that are shadowed
-			if _, ok := hasSeen[name]; !ok {
+			if seen, ok := hasSeen[name]; !ok || !seen {
 				plugins = append(plugins, &plugin{
 					path:         filepath.Join(dir, f.Name()),
 					name:         stripWindowsExecExtensions(f.Name()),
@@ -165,18 +179,34 @@ func (manager *Manager) ListPluginsForCommandGroup(commandGroupParts []string) (
 	return plugins, nil
 }
 
-func isPartOfCommandGroup(commandGroupParts []string, name string) bool {
+func filterPluginsByCommandGroup(plugins PluginList, commandGroupParts []string) PluginList {
+	ret := PluginList{}
+	for _, pl := range plugins {
+		if isPartOfCommandGroup(commandGroupParts, pl.CommandParts()) {
+			ret = append(ret, pl)
+		}
+	}
+	return ret
+}
+
+func isPartOfCommandGroup(commandGroupParts []string, commandParts []string) bool {
+	if len(commandParts) != len(commandGroupParts)+1 {
+		return false
+	}
+	for i := range commandGroupParts {
+		if commandParts[i] != commandGroupParts[i] {
+			return false
+		}
+	}
+	return true
+}
+
+func isPluginFileNamePartOfCommandGroup(commandGroupParts []string, pluginFileName string) bool {
 	if commandGroupParts == nil {
 		return true
 	}
 
-	commandParts := extractPluginCommandFromFileName(name)
-
-	// commandParts must be one more element then the parts of the command group
-	// it belongs to. E.g. for the command "service", "log" (2 elements) the containing
-	// group only has one element ("service"). This condition is here for
-	// shortcut and ensure that we don't run in an out-of-bound array error
-	// in the loop below.
+	commandParts := extractPluginCommandFromFileName(pluginFileName)
 	if len(commandParts) != len(commandGroupParts)+1 {
 		return false
 	}
@@ -343,7 +373,7 @@ func stripWindowsExecExtensions(name string) string {
 // Return the path and the parts building the most specific plugin in the given directory
 // If lookupInPath is true, then also the OS PATH is checked.
 // An error returned if any IO operation fails
-func findMostSpecificPlugin(dir string, parts []string, lookupInPath bool) (Plugin, error) {
+func findMostSpecificPluginInPath(dir string, parts []string, lookupInPath bool) (Plugin, error) {
 	for i := len(parts); i > 0; i-- {
 
 		// Construct plugin name to lookup
@@ -429,3 +459,31 @@ func findInDirOrPath(name string, dir string, lookupInPath bool) (string, error)
 	// Not found
 	return "", nil
 }
+
+// lookupInternalPlugin looks up internally registered plugins. Return nil if none is found.
+// Start with longest argument path first to find the most specific match
+func lookupInternalPlugin(parts []string) Plugin {
+	for i := len(parts); i > 0; i-- {
+		checkParts := parts[0:i]
+		for _, plugin := range InternalPlugins {
+			if equalsSlice(plugin.CommandParts(), checkParts) {
+				return plugin
+			}
+		}
+	}
+	return nil
+}
+
+// equalsSlice return true if two string slices contain the same elements
+func equalsSlice(a, b []string) bool {
+	if len(a) != len(b) || len(a) == 0 {
+		return false
+	}
+
+	for i := range a {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
+}


Test patch:
diff --git a/lib/test/broker.go b/lib/test/broker.go
index 8fdd5bd4..3784f176 100644
--- a/lib/test/broker.go
+++ b/lib/test/broker.go
@@ -24,7 +24,7 @@ import (
 
 // LabelNamespaceForDefaultBroker adds label 'knative-eventing-injection=enabled' to the configured namespace
 func LabelNamespaceForDefaultBroker(r *KnRunResultCollector) error {
-	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.DeprecatedInjectionAnnotation + "=enabled"}
+	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.InjectionAnnotation + "=enabled"}
 	_, err := Kubectl{}.Run(cmd...)
 
 	if err != nil {
@@ -43,7 +43,7 @@ func LabelNamespaceForDefaultBroker(r *KnRunResultCollector) error {
 
 // UnlabelNamespaceForDefaultBroker removes label 'knative-eventing-injection=enabled' from the configured namespace
 func UnlabelNamespaceForDefaultBroker(r *KnRunResultCollector) {
-	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.DeprecatedInjectionAnnotation + "-"}
+	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.InjectionAnnotation + "-"}
 	_, err := Kubectl{}.Run(cmd...)
 	if err != nil {
 		r.T().Fatalf("error executing '%s': %s", strings.Join(cmd, " "), err.Error())
diff --git a/pkg/kn/plugin/manager_test.go b/pkg/kn/plugin/manager_test.go
index 66c319b2..72e8c43e 100644
--- a/pkg/kn/plugin/manager_test.go
+++ b/pkg/kn/plugin/manager_test.go
@@ -21,6 +21,7 @@ import (
 	"path/filepath"
 	"regexp"
 	"runtime"
+	"strings"
 	"testing"
 
 	"github.com/spf13/cobra"
@@ -39,6 +40,18 @@ type testContext struct {
 	pluginManager *Manager
 }
 
+type testPlugin struct {
+	parts []string
+}
+
+func (t testPlugin) Name() string                 { return "kn-" + strings.Join(t.parts, "-") }
+func (t testPlugin) Execute(args []string) error  { return nil }
+func (t testPlugin) Description() (string, error) { return "desc: " + t.Name(), nil }
+func (t testPlugin) CommandParts() []string       { return t.parts }
+func (t testPlugin) Path() string                 { return "" }
+
+var _ Plugin = testPlugin{}
+
 func TestEmptyFind(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)
@@ -65,7 +78,7 @@ func TestLookupInPluginsDir(t *testing.T) {
 	assert.Equal(t, out, "OK \n")
 }
 
-func TestLookupWithNotFoundResult(t *testing.T) {
+func TestFindWithNotFoundResult(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)
 
@@ -74,7 +87,7 @@ func TestLookupWithNotFoundResult(t *testing.T) {
 	assert.NilError(t, err, "no error expected")
 }
 
-func TestPluginInPath(t *testing.T) {
+func TestFindPluginInPath(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)
 
@@ -90,6 +103,9 @@ func TestPluginInPath(t *testing.T) {
 	plugin, err := ctx.pluginManager.FindPlugin(pluginCommands)
 	assert.NilError(t, err)
 	assert.Assert(t, plugin != nil)
+	desc, err := plugin.Description()
+	assert.NilError(t, err)
+	assert.Assert(t, desc != "")
 	assert.Equal(t, plugin.Path(), filepath.Join(tmpPathDir, "kn-path-test"))
 	assert.DeepEqual(t, plugin.CommandParts(), pluginCommands)
 
@@ -100,6 +116,31 @@ func TestPluginInPath(t *testing.T) {
 	assert.Assert(t, plugin == nil)
 }
 
+func TestFindPluginInternally(t *testing.T) {
+	ctx := setup(t)
+	defer cleanup(t, ctx)
+
+	// Initialize registered plugins
+	defer (prepareInternalPlugins(
+		testPlugin{[]string{"a", "b"}},
+		testPlugin{[]string{"a"}}))()
+
+	data := []struct {
+		parts []string
+		name  string
+	}{
+		{[]string{"a", "b"}, "kn-a-b"},
+		{[]string{"a"}, "kn-a"},
+		{[]string{"a", "c"}, "kn-a"},
+	}
+	for _, d := range data {
+		plugin, err := ctx.pluginManager.FindPlugin(d.parts)
+		assert.NilError(t, err)
+		assert.Assert(t, plugin != nil)
+		assert.Equal(t, plugin.Name(), d.name)
+	}
+}
+
 func TestPluginExecute(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)
@@ -112,20 +153,80 @@ func TestPluginExecute(t *testing.T) {
 	assert.Equal(t, out, "OK arg1 arg2\n")
 }
 
+func TestPluginMixed(t *testing.T) {
+	ctx := setup(t)
+	defer cleanup(t, ctx)
+
+	createTestPlugin(t, "kn-external", ctx)
+	createTestPlugin(t, "kn-shadow", ctx)
+
+	// Initialize registered plugins
+	defer (prepareInternalPlugins(
+		testPlugin{[]string{"internal"}},
+		testPlugin{[]string{"shadow"}},
+	))()
+
+	data := []struct {
+		path       []string
+		name       string
+		isInternal bool
+	}{
+		{[]string{"external"}, "kn-external", false},
+		{[]string{"internal"}, "kn-internal", true},
+		{[]string{"shadow"}, "kn-shadow", true},
+	}
+	for _, d := range data {
+		plugin, err := ctx.pluginManager.FindPlugin(d.path)
+		assert.NilError(t, err)
+		assert.Assert(t, plugin != nil)
+		assert.Equal(t, plugin.Name(), d.name)
+		_, ok := plugin.(testPlugin)
+		assert.Equal(t, d.isInternal, ok)
+	}
+}
+
+func prepareInternalPlugins(plugins ...Plugin) func() {
+	oldPlugins := InternalPlugins
+	InternalPlugins = plugins
+	return func() {
+		InternalPlugins = oldPlugins
+	}
+}
+
 func TestPluginListForCommandGroup(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)
-	createTestPlugin(t, "kn-service-log_2", ctx)
+	createTestPlugin(t, "kn-service-external", ctx)
+	createTestPlugin(t, "kn-foo-bar", ctx)
+	createTestPlugin(t, "kn-service-shadow", ctx)
+
+	// Internal plugin should be filtered out if not belong to the service group
+	defer (prepareInternalPlugins(
+		testPlugin{[]string{"service", "internal"}},
+		testPlugin{[]string{"service", "shadow"}},
+		testPlugin{[]string{"bla", "blub"}},
+		testPlugin{[]string{"bla", "blub", "longer"}}))()
 
 	pluginList, err := ctx.pluginManager.ListPluginsForCommandGroup([]string{"service"})
 	assert.NilError(t, err)
-	assert.Assert(t, pluginList.Len() == 1)
-	assert.Equal(t, pluginList[0].Name(), "kn-service-log_2")
+	assert.Assert(t, pluginList.Len() == 3)
+	assert.Assert(t, containsPluginWithName(pluginList, "kn-service-internal"))
+	assert.Assert(t, containsPluginWithName(pluginList, "kn-service-external"))
+	assert.Assert(t, containsPluginWithName(pluginList, "kn-service-shadow"))
 	pluginList, err = ctx.pluginManager.ListPluginsForCommandGroup([]string{})
 	assert.NilError(t, err)
 	assert.Assert(t, pluginList.Len() == 0)
 }
 
+func containsPluginWithName(plugins PluginList, name string) bool {
+	for _, pl := range plugins {
+		if pl.Name() == name {
+			return true
+		}
+	}
+	return false
+}
+
 func TestPluginHelpMessage(t *testing.T) {
 	ctx := setup(t)
 	defer cleanup(t, ctx)