Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for command-level remapping #736

Merged
merged 3 commits into from
Apr 19, 2024

Conversation

vuil
Copy link
Contributor

@vuil vuil commented Apr 17, 2024

What this PR does / why we need it

    Add support for command-level remapping

    This is done via processing any CommandMapEntry's specified the plugin's
    'info' output

    The CommandMapEntry enables mapping of the plugin's root command or one
    of its sub-command to another location in the CLI's command tree.

    Also: enables the override of an existing plugin with the
    Overrides field of the CommandMapEntry. It is uncommon for this to be
    needed; it is useful when deploying a plugin that needs to explicitly
    remove certain commands from the CLI even though the plugin itself does
    not provide commands of the same names.

    For any command created via the commandMap, details about the invocation
    of said command will be communicated to the plugin binary via the following
    environment variables:

    - TANZU_CLI_INVOKED_GROUP:  a space-delimited portion of the Tanzu CLI
    command invocation between the CLI binary and the command name itself.
    Empty when invoking a top-level command. e.g. "tanzu apply"

    - TANZU_CLI_INVOKED_COMMAND: the name of the command in a Tanzu CLI
    command invocation

    - TANZU_CLI_COMMAND_MAPPED_FROM: a space-delimited path relative to the
    plugin's root command of the command being invoked. The value is empty
    unless the CLI command invoked is mapped to a non-root command of the plugin.

    Note: The complete CLI command invocation can thus be reconstructed with
    tanzu_cli_binary_name "$TANZU_CLI_INVOKED_GROUP" "$TANZU_CLI_INVOKED_COMMAND" args*

    Plugins employing any form of command mapping are expected to make use
    of these values to adjust any command invocation references in their
    commands' outputs (e.g. help, examples)

Notes to documenters:
Much of the information in this PR and additional Markdown changes are relevant for plugin developers, and should have little relevance to user-facing documentation.

Which issue(s) this PR fixes

Fixes #

Describe testing done for PR

See unit tests added.

In addition, built and installed a sample plugin modified with additional command and command map.

Baseline

Use a sample plugin with some trivial commands in this branch as baseline:
https://github.com/vuil/tanzu-cli/blob/sample-plugin-with-remap/test/sample-plugin/cmd/plugin/sample-plugin/main.go

Running CLI when sample plugin is installed:

~> tz
Usage:
  tanzu [command]

Available command groups:

  Admin
    builder                 Build Tanzu components
    test                    Test the CLI
  Build
    apps                    Applications on Kubernetes
    space                   Tanzu space, space profile, and space trait management
    supplychain             supplychain management
  Manage
    project                 View, list and use Tanzu Projects.
    sample                  sample plugin to test e2e framework
...
  Target
    kubernetes              Commands that interact with a Kubernetes endpoint
    mission-control         Commands that provide functionality for Tanzu Mission Control
    operations              Commands that support Kubernetes operations for Tanzu Application Platform

Flags:
  -h, --help   help for tanzu

Use "tanzu [command] --help" for more information about a command.

~> tz sample deeper
deeper commands

Usage:
  tanzu sample deeper [command]

Aliases:
  deeper, deep

Available Commands:
  yell        yell something

Flags:
  -h, --help   help for deeper

Use "tanzu sample deeper [command] --help" for more information about a command.

~> tz sample deeper yell hello
hello weeeee!!!!!

~> tz sample shout hey
hey!!
Invocation Context:
(*plugin.InvocationContext)(nil)


~> tz sample echo foo
foo

Test behavior of CLI by reinstalling above plugin built with the following command maping customizations:

1. elevate command
--- a/test/sample-plugin/cmd/plugin/sample-plugin/main.go
+++ b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
@@ -24,3 +24,9 @@ var descriptor = plugin.PluginDescriptor{
        Group:       plugin.ManageCmdGroup,
-       CommandMap:  []plugin.CommandMapEntry{},
+       CommandMap: []plugin.CommandMapEntry{
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "echo",
+                       DestinationCommandPath: "echo",
+                       Description:            "the echo command elevated to the top level",
+               },
+       },
 }
@@ -47,3 +53,3 @@ func newEchoCmd() *cobra.Command {
                Args:   cobra.ExactArgs(1),
-               Hidden: false,
+               Hidden: true,
                RunE: func(cmd *cobra.Command, args []string) error {
~
~> tz
Usage:
  tanzu [command]

Available command groups:

  Admin
    builder                 Build Tanzu components
    test                    Test the CLI
  Build
    apps                    Applications on Kubernetes
    space                   Tanzu space, space profile, and space trait management
    supplychain             supplychain management
  Manage
    echo                    the echo command elevated to the top level
    project                 View, list and use Tanzu Projects.
    sample                  sample plugin to test e2e framework
  Run
...

Flags:
  -h, --help   help for tanzu

Use "tanzu [command] --help" for more information about a command.
~> tz echo foo
foo
~> tz echo --help
echo something

Usage:
  tanzu echo

Flags:
  -h, --help   help for echo
~> tz sample
sample plugin to test e2e framework

Usage:
  tanzu sample [command]

Available Commands:
  deeper        deeper commands
  shout         shout something

Flags:
  -h, --help   help for sample

Use "tanzu sample [command] --help" for more information about a command.
-----------
2.plugin level mapping to second level of CLI command tree
+       CommandMap: []plugin.CommandMapEntry{
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "echo",
+                       DestinationCommandPath: "echo",
+                       Description:            "the echo command elevated to the top level",
+               },
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "",
+                       DestinationCommandPath: "operations sample",
+               },
+       },
 }
@@ -47,3 +57,3 @@ func newEchoCmd() *cobra.Command {
                Args:   cobra.ExactArgs(1),
-               Hidden: false,
+               Hidden: true,
                RunE: func(cmd *cobra.Command, args []string) error {
@@ -63,3 +73,4 @@ func newShoutCmd() *cobra.Command {
                RunE: func(cmd *cobra.Command, args []string) error {
-                       fmt.Printf("%s!!\n", args[0])
+                       ic := plugin.GetInvocationContext()
+                       fmt.Printf("%s!!\nInvocation Context:\n% #v\n", args[0], ic)
                        return nil
~
~> tz sample
[x] : unknown command "sample" for "tanzu"
~> tz operations
Commands that support Kubernetes operations for Tanzu Application Platform

Usage:
  tanzu operations [command]

Aliases:
  operations, ops

Available command groups:

  Manage
    clustergroup            A group of Kubernetes clusters
    sample                  sample plugin to test e2e framework

Flags:
  -h, --help   help for operations

Use "tanzu operations [command] --help" for more information about a command.

~> tz operations sample
sample plugin to test e2e framework

Usage:
  tanzu operations sample [command]

Available Commands:
  deeper        deeper commands
  shout         shout something

Flags:
  -h, --help   help for sample

Use "tanzu operations sample [command] --help" for more information about a command.

~> tz operations sample shout --help
shout something

Usage:
  tanzu operations sample shout MESSAGE [flags]

Flags:
  -h, --help   help for shout
  
~> tz echo "the echo command still works"
the echo command still works

~> tz operations sample shout hey
hey!!
Invocation Context:
&plugin.InvocationContext{invokedGroup:"operations", invokedCommand:"sample", sourceCommandPath:""}
3. Lone command elevated
~/tanzu-cli-cln2/test/sample-plugin ~
diff --git a/test/sample-plugin/cmd/plugin/sample-plugin/main.go b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
index b3b6dc15..51245c21 100644
--- a/test/sample-plugin/cmd/plugin/sample-plugin/main.go
+++ b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
@@ -24,3 +24,12 @@ var descriptor = plugin.PluginDescriptor{
        Group:       plugin.ManageCmdGroup,
-       CommandMap:  []plugin.CommandMapEntry{},
+       CommandMap: []plugin.CommandMapEntry{
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "",
+                       DestinationCommandPath: "",
+               },
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "shout",
+                       DestinationCommandPath: "shout",
+               },
+       },
 }
@@ -33,5 +42,5 @@ func main() {
        p.AddCommands(
-               newEchoCmd(),
+               //newEchoCmd(),
                newShoutCmd(),
-               newDeeperCmd(),
+               //newDeeperCmd(),
        )
@@ -61,3 +70,3 @@ func newShoutCmd() *cobra.Command {
                Args:   cobra.ExactArgs(1),
-               Hidden: false,
+               Hidden: true,
                RunE: func(cmd *cobra.Command, args []string) error {
~
~> tz
Usage:
  tanzu [command]

Available command groups:

  Admin
    builder                 Build Tanzu components
    test                    Test the CLI
  Build
    apps                    Applications on Kubernetes
    space                   Tanzu space, space profile, and space trait management
    supplychain             supplychain management
  Manage
    project                 View, list and use Tanzu Projects.
    shout                   sample plugin to test command mapping shout functionality
  Run
    telemetry               configure cluster-wide settings for vmware tanzu telemetry
  System
    completion              Output shell completion code
    config                  Configuration for the CLI
    context                 Configure and manage contexts for the Tanzu CLI
    init                    Initialize the CLI
    login                   Login to Tanzu Application Platform
    plugin                  Manage CLI plugins
    version                 Version information
  Target
    kubernetes              Commands that interact with a Kubernetes endpoint
    mission-control         Commands that provide functionality for Tanzu Mission Control
    operations              Commands that support Kubernetes operations for Tanzu Application Platform

Flags:
  -h, --help   help for tanzu

Use "tanzu [command] --help" for more information about a command.
~> tz shout hey
hey!!
Invocation Context:
&plugin.InvocationContext{invokedGroup:"", invokedCommand:"shout", sourceCommandPath:"shout"}
~> tz sample
[x] : unknown command "sample" for "tanzu"
4. Plugin level remap with overrides
diff --git a/test/sample-plugin/cmd/plugin/sample-plugin/main.go b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
index b3b6dc15..cf47702e 100644
--- a/test/sample-plugin/cmd/plugin/sample-plugin/main.go
+++ b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
@@ -24,3 +24,9 @@ var descriptor = plugin.PluginDescriptor{
        Group:       plugin.ManageCmdGroup,
-       CommandMap:  []plugin.CommandMapEntry{},
+       CommandMap: []plugin.CommandMapEntry{
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "",
+                       DestinationCommandPath: "sound",
+                       Overrides:              "mission-control clustergroup",
+               },
+       },
 }
~
~> tz mission-control
Commands that provide functionality for Tanzu Mission Control

Usage:
  tanzu mission-control [command]

Aliases:
  mission-control, tmc

Available command groups:

  Manage
    policy                  Policy management for resources

Flags:
  -h, --help   help for mission-control

Use "tanzu mission-control [command] --help" for more information about a command.
~> tz sound shout hey
hey!!
Invocation Context:
&plugin.InvocationContext{invokedGroup:"", invokedCommand:"sound", sourceCommandPath:""}
5. Command level aliases and autocomplete
--- a/test/sample-plugin/cmd/plugin/sample-plugin/main.go
+++ b/test/sample-plugin/cmd/plugin/sample-plugin/main.go
@@ -22,7 +22,12 @@ var descriptor = plugin.PluginDescriptor{
        Version:     buildinfo.Version,
        BuildSHA:    buildinfo.SHA,
        Group:       plugin.ManageCmdGroup,
-       CommandMap:  []plugin.CommandMapEntry{},
+       CommandMap: []plugin.CommandMapEntry{
+               plugin.CommandMapEntry{
+                       SourceCommandPath:      "shout",
+                       DestinationCommandPath: "shout",
+               },
+       },
 }

 func main() {
@@ -52,20 +57,27 @@ func newEchoCmd() *cobra.Command {
                },
        }
        return cmd
+
 }

 func newShoutCmd() *cobra.Command {
+       var value string
+
        cmd := &cobra.Command{
-               Use:    "shout MESSAGE",
-               Short:  "shout something",
-               Args:   cobra.ExactArgs(1),
-               Hidden: false,
+               Use:     "shout MESSAGE",
+               Short:   "shout something",
+               Aliases: []string{"sh", "sht"},
+               Args:    cobra.ExactArgs(1),
+               Hidden:  false,
                RunE: func(cmd *cobra.Command, args []string) error {
                        ic := plugin.GetInvocationContext()
-                       fmt.Printf("%s!!\nInvocation Context:\n% #v\n", args[0], ic)
+                       fmt.Printf("%s %s!!\nInvocation Context:\n% #v\n", value, args[0], ic)
                        return nil
                },
        }
+
+       cmd.Flags().StringVarP(&value, "value", "v", "", "value to add to shout")
+
        return cmd
 }
> tz sht --value "stuff"  works
stuff works!!
Invocation Context:
&plugin.InvocationContext{invokedGroup:"", invokedCommand:"shout", sourceCommandPath:"shout"}


> tz __complete shout foo --va
--value value to add to shout
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

Release note

Add support for mapping of a plugin at a command-level

Additional information

Special notes for your reviewer

Testing with a plugin with mapping configuration such as the sample plugin requires that the built plugin be installed using a Tanzu CLI updated with the new plugin-runtime updates (under review in vmware-tanzu/tanzu-plugin-runtime#176)

@vuil vuil requested a review from a team as a code owner April 17, 2024 17:06
@vuil vuil force-pushed the remap_withmapentry_autoaliasdesc branch from db23641 to 680de4d Compare April 17, 2024 19:20
@vuil vuil changed the title Command-level Mapping following to support Aliases Add support for command-level remapping Apr 17, 2024
@vuil vuil force-pushed the remap_withmapentry_autoaliasdesc branch 2 times, most recently from 379a474 to 50c02cb Compare April 18, 2024 22:01
@vuil vuil added the docs-impact issues with documentation impact label Apr 18, 2024
Copy link
Contributor

@marckhouzam marckhouzam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things that impact the help text

pkg/cli/plugin_cmd.go Outdated Show resolved Hide resolved
// Information about if command invocation is via a mapped command
numParts := len(dstHierarchy)
if numParts > 0 {
env["TANZU_CLI_INVOKED_COMMAND"] = dstHierarchy[numParts-1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tried to figure out the problem but this does not work if I'm invoking a sub-command of a mapped command.
For example, I have mapped tanzu builder cli to tanzu cli. If I run tanzu cli -h things are good but not if I run tanzu cli add-plugin -h:

$ tz cli add-plugin -h
Add a plugin to a repository

Usage:
  tanzu cli

I tested and the variables are:

TANZU_CLI_INVOKED_GROUP:
TANZU_CLI_INVOKED_COMMAND: cli
TANZU_CLI_COMMAND_MAPPED_FROM: cli

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an issue with the usage generation. I will address it in a followup.

vuil added 3 commits April 19, 2024 14:33
Will update once the runtime changes are merged
This is done via processing any CommandMapEntry's specified the plugin's
'info' output

The CommandMapEntry enables mapping of the plugin's root command or one
of its sub-command to another location in the CLI's command tree.

Also: enables the override of an existing plugin with the
Overrides field of the CommandMapEntry. It is uncommon for this to be
needed; it is useful when deploying a plugin that needs to explicitly
remove certain commands from the CLI even though the plugin itself does
not provide commands of the same names.

For any command created via the commandMap, details about the invocation
of said command be communicated to the plugin binary via the following
environment variables:

TANZU_CLI_INVOKED_GROUP:  a space-delimited portion of the Tanzu CLI
command invocation between the CLI binary and the command name itself.
Empty when invoking a top-level command. e.g. "tanzu apply"

TANZU_CLI_INVOKED_COMMAND: the name of the command in a Tanzu CLI
command invocation

TANZU_CLI_COMMAND_MAPPED_FROM: a space-delimited path relative to the
plugin's root command of the command being invoked. The value is empty
unless the CLI command invoked is mapped to a non-root command of the plugin.

Note: The complete CLI command invocation can thus be reconstructed with
tanzu_cli_binary_name "$TANZU_CLI_INVOKED_GROUP" "$TANZU_CLI_INVOKED_COMMAND" args*

Plugins employing any form of command mapping are expected to make use
of these values to adjust any command invocation references in their
commands' outputs (e.g. help, examples)
@vuil vuil force-pushed the remap_withmapentry_autoaliasdesc branch from 38437b3 to d2756a8 Compare April 19, 2024 21:34
Copy link
Contributor

@anujc25 anujc25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. This is really great. 🚀

Let's try to address any critical pending comments as a follow-up.

@vuil vuil merged commit f379199 into vmware-tanzu:main Apr 19, 2024
7 checks passed
@marckhouzam marckhouzam added this to the v1.3.0 milestone Apr 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla-not-required docs-impact issues with documentation impact
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants