github.com/cloudfoundry/cli@v7.1.0+incompatible/plugin/plugin_examples/README.md (about)

     1  If you have any questions about developing a CLI plugin, ask away on the [cf-dev mailing list](https://lists.cloudfoundry.org/archives/list/cf-dev@lists.cloudfoundry.org/) (many plugin developers there!) or the #cli channel in our Slack community.
     2  
     3  # Future updates to the architecture
     4  Our current plugin architecture currently requires a review and possible large overhaul. Until then, there are no current plans to update our existing architecture. Feel free to provide input [here](https://www.pivotaltracker.com/story/show/157201049) where you can provide feedback.
     5  
     6  # Changes in v6.25.0
     7  - `GetApp` now returns `Path` and `Port` information.
     8  
     9  # Changes in v6.24.0
    10  - API `LoggregatorEndpoint()` is deprecated and now always returns the empty string. Use `DopplerEndpoint()` instead to obtain logs.
    11  
    12  # Changes in v6.17.0
    13  - `-v` is now a global flag to enable verbose logging of API calls, equivalent to `CF_TRACE=true`. This means that the `-v` flag will no longer be passed to plugins.
    14  
    15  # Changes in v6.14.0
    16  - API `AccessToken()` now provides a refreshed o-auth token.
    17  - [Examples](https://github.com/cloudfoundry/cli/tree/master/plugin/plugin_examples#test-driven-development-tdd) on how to use fake `CliConnection` and test RPC server for TDD development.
    18  - Fix Plugin API file descriptors leakage.
    19  - Fix bug where some CLI versions does not respect `PluginMetadata.MinCliVersion`.
    20  - The field `PackageUpdatedAt` returned by `GetApp()` API is now populated.
    21  
    22  [Complete change log ...](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/CHANGELOG.md)
    23  
    24  # Developing a Plugin
    25  [Go here for documentation of the plugin API](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/DOC.md)
    26  
    27  This README discusses how to develop a cf CLI plugin.
    28  For user-focused documentation, see [Using the cf CLI](http://docs.cloudfoundry.org/cf-cli/use-cli-plugins.html).
    29  
    30  *If you wish to share your plugin with the community, see [here](http://github.com/cloudfoundry/cli-plugin-repo) for plugin submission.
    31  
    32  
    33  ## Development Requirements
    34  
    35  - [GoLang installed](https://golang.org/doc/install)
    36  - Tagged version of CLI release source code that supports plugins; cf CLI v.6.7.0 and above
    37  ```
    38  mkdir -p "${GOPATH}/src/code.cloudfoundry.org"
    39  cd "${GOPATH}/src/code.cloudfoundry.org"
    40  git clone "https://github.com/cloudfoundry/cli"
    41  ```
    42  (Optionally specify `--depth 1` to `git clone` for a faster download without any commit history)
    43  
    44  ## Architecture Overview
    45  
    46  The cf CLI plugin architecture model follows the remote procedure call (RPC) model.
    47  The cf CLI invokes each plugin, runs it as an independent executable, and handles all start, stop, and clean up tasks for plugin executable resources.
    48  
    49  Here is an illustration of the work flow when a plugin command is being invoked.
    50  
    51  1: CLI launches 2 processes, the rpc server and the independent plugin executable
    52  <p align="center">
    53  <img src="https://raw.githubusercontent.com/cloudfoundry/cli/master/plugin/plugin_examples/images/rpc_flow1.png" alt="workflow 1" width="400px">
    54  </p>
    55  
    56  2: Plugin establishes a connection to the RPC server, the connection is used to invoke core cli commands.
    57  <p align="center">
    58  <img src="https://raw.githubusercontent.com/cloudfoundry/cli/master/plugin/plugin_examples/images/rpc_flow2.png" alt="workflow 1" width="400px">
    59  </p>
    60  
    61  3: When a plugin invokes a cli command, it talks to the rpc server, and the rpc server interacts with cf cli to perform the command. The result is passed back to the plugin through the rpc server.
    62  <p align="center">
    63  <img src="https://raw.githubusercontent.com/cloudfoundry/cli/master/plugin/plugin_examples/images/rpc_flow3.png" alt="workflow 1" width="400px">
    64  </p>
    65  
    66  - Plugins that you develop for the cf CLI must conform to a predefined plugin interface that we discuss below.
    67  
    68  ## Writing a Plugin
    69  
    70  [Go here for documentation of the plugin API](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/DOC.md)
    71  
    72  To write a plugin for the cf CLI, implement the [predefined plugin interface](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin.go).
    73  
    74  The interface uses a `Run(...)` method as the main entry point between the CLI and a plugin. This method receives the following arguments:
    75  
    76    - A struct `plugin.CliConnection` that contains methods for invoking cf CLI commands
    77    - A string array that contains the arguments passed from the `cf` process
    78  
    79  The `GetMetadata()` function informs the CLI of the name of a plugin, plugin version (optional), minimum CLI version required (optional), the commands it implements, and help text for each command that users can display with `cf help`.
    80  
    81  Plugin names with spaces must be enclosed in quotes when installed and uninstalled (e.g.: `cf install-plugin "my plugin"`). We recommend that plugin names not contain spaces to prevent the command shell from interpreting the name as multiple words.
    82  
    83    To initialize a plugin, call `plugin.Start(new(MyPluginStruct))` from within the `main()` method of your plugin. The `plugin.Start(...)` function requires a new reference to the struct that implements the defined interface.
    84  
    85  This repo contains a basic plugin example [here](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/basic_plugin.go).<br>
    86  To see more examples, go [here](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/).
    87  
    88  ### Uninstalling A Plugin
    89  Uninstall of the plugin needs to be explicitly handled. When a user calls the `cf uninstall-plugin` command, CLI notifies the plugin via a call with `CLI-MESSAGE-UNINSTALL` as the first item in `[]args` from within the plugin's `Run(...)` method.
    90  
    91  ### Test Driven Development (TDD)
    92  An example which was developed using TDD is available:
    93  - `Test RPC server`: an RPC server to be used as a back-end for the plugin. It allows the plugin to be tested as a stand alone binary without replying on CLI as a back-end. [See example](https://github.com/cloudfoundry/cli/tree/master/plugin/plugin_examples/test_rpc_server_example)
    94  
    95  ### Using Command Line Arguments
    96  
    97  The `Run(...)` method accepts the command line arguments and flags that you define for a plugin.
    98  
    99    See the [command line arguments example] (https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/echo.go) included in this repo.
   100  
   101  #### Global Flags
   102  There are several global flags that will not be passed to the plugin. These are:
   103  - `-v`: equivalent to `CF_TRACE=true`, will display any API calls/responses to the user
   104  - `-h`: will process the return from the plugin's `GetMetadata` function to produce a help display
   105  
   106  ### Calling CLI Commands
   107  
   108  You can invoke CLI commands with `cliConnection.CliCommand([]args)` from within a plugin's `Run(...)` method. The `Run(...)` method receives the `cliConnection` as its first argument.
   109  
   110  The `cliConnection.CliCommand([]args)` returns the output printed by the command and an error. The output is returned as a slice of strings. The error will be present if the call to the CLI command fails.
   111  
   112  See the [test plugin example](https://github.com/cloudfoundry/cli/blob/master/integration/assets/test_plugin/test_plugin.go) included in this repo.
   113  
   114  ### Creating Interactive Plugins
   115  
   116  Because a plugin has access to stdin during a call to the `Run(...)` method, you can create interactive plugins. See the [interactive plugin example](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/interactive.go) included in this repo.
   117  
   118  ### Creating Plugins with multiple commands
   119  
   120  A single plugin binary can have more than one command, and each command can have it's own help text defined. For an example of multi-command plugins, see the [multiple commands example](https://github.com/cloudfoundry/cli/blob/master/plugin/plugin_examples/multiple_commands.go)
   121  
   122  ### Enforcing a minimum CLI version required for the plugin.
   123  
   124  ```go
   125  func (c *cmd) GetMetadata() plugin.PluginMetadata {
   126  	return plugin.PluginMetadata{
   127  		Name: "Test1",
   128  		MinCliVersion: plugin.VersionType{
   129  			Major: 6,
   130  			Minor: 12,
   131  			Build: 0,
   132  		},
   133  	}
   134  }
   135  ```
   136  
   137  ### Debugging plugin code
   138  
   139  The recommended approach to debugging plugin code is to print to stdout, or set CF_TRACE to /dev/stderr or a file.
   140  
   141  ## Compiling Plugin Source Code
   142  
   143  The cf CLI requires an executable file to install the plugin. You must compile the source code with the `go build` command before distributing the plugin, or instruct your users to compile the plugin source code before installing the plugin. For information about compiling Go source code, see [Compile packages and dependencies](https://golang.org/cmd/go/).
   144  
   145  ## Using Plugins
   146  
   147  After you compile a plugin, use the following commands to install and manage the plugin.
   148  
   149  ### Installing Plugins
   150  
   151  To install a plugin, run:
   152  
   153  `cf install-plugin PATH_TO_PLUGIN_BINARY`
   154  
   155  ### Listing Plugins
   156  
   157  To display a list of installed plugins and the commands available from each plugin, run:
   158  
   159  `cf plugins`
   160  
   161  ### Uninstalling Plugins
   162  
   163  To remove a plugin, run:
   164  
   165  `cf uninstall-plugin PLUGIN_NAME`
   166  
   167  ## Known Issues
   168  
   169  - When invoking a CLI command using `cliConnection.CliCommand([]args)` a plugin will not receive output generated by the cli package. This includes usage failures when executing a cli command, `cf help`, or `cli SOME-COMMAND -h`.
   170  - When invoking a CLI command using `cliConnection.CliCommand([]args)` and `CF_TRACE=true/cf -v` a plugin will receive all the output, including the trace in the returned string array. This may cause problem while trying to debug output with `CF_TRACE`. As work around, if a plugin is running `cf curl` via `CliCommand`, the following can be used to help with debugging (when the `CF_DEBUG_CURL=true`):
   171  ```go
   172    func RunCurl(cliConnection plugin.CliConnection, args []string) ([]string, error) {
   173      output, err := cliConnection.CliCommand("curl", args...)
   174      if os.Getenv("CF_DEBUG_CURL") == "true" {
   175        fmt.Println(strings.Join(output, "\n"))
   176      }
   177      return output, err
   178    }
   179  ```
   180  - Due to architectural limitations, calling CLI core commands is not concurrency-safe. The correct execution of concurrent commands is not guaranteed. An architecture restructuring is in the works to fix this in the near future.
   181  
   182  - Due to our legacy code, the plugin architecture does not currently work with newer CLI features. See a more verbose explanation [here](https://github.com/cloudfoundry/cli/issues/1399#issuecomment-409061226).
   183  
   184  - Due to our legacy code, when you `CF_TRACE=true`, the refresh_token in the request body is not sanitized.