# Plugin Support ## Table of Contents * [Compile-In Plugins](#compile-in-plugins) * [Fine Tuning a Build](#fine-tuning-a-build) * [Command Selection](#command-selection) * [Accessing `airshipctl` settings](#accessing-airshipctl-settings) Our requirements for `airshipctl` contain two very conflicting concepts. One, we'd like to assert that `airshipctl` is a statically linked executable, such that it can be easily distributed. Two, we'd like to have plugin support. These requirements can't coincide within the same project under the standard definition of a plugin. Our solution is to provide a more refined definition of what a plugin actually is. ## Compile-In Plugins In order to support plugins to an independent binary file, we use the concept of "compile-in plugins". A "compile-in plugin" is an add-on that is built into the main application at compile time, as opposed to runtime. This means that while `airshipctl` is a standalone application, it also acts as a sort of library. In fact, taking a deeper look at `airshipctl` reveals that the base application is incredibly simple. At its core, `airshipctl` provides exactly 2 commands: `version` and `help`. Take a look at the following snippet to see what this looks like: ```go package main import ( "os" "opendev.org/airship/airshipctl/cmd" ) func main() { rootCmd, _, err := cmd.NewRootCmd(os.Stdout) if err != nil { panic(err) } rootCmd.Execute() } ``` Compiling and running the above gives the following output: ``` $ ./airshipctl airshipctl is a unified entrypoint to various airship components Usage: airshipctl [command] Available Commands: bootstrap Bootstrap ephemeral Kubernetes cluster completion Generate autocompletions script for the specified shell (bash or zsh) config Modify airshipctl config files document manages deployment documents help Help about any command kubectl kubectl controls the Kubernetes cluster manager version Show the version number of airshipctl Flags: --airshipconf string Path to file for airshipctl configuration. (default "$HOME/.airship/config") --debug enable verbose output -h, --help help for airshipctl --kubeconfig string Path to kubeconfig associated with airshipctl configuration. (default "$HOME/.airship/kubeconfig") Additional help topics: airshipctl cluster Control Kubernetes cluster Use "airshipctl [command] --help" for more information about a command. ``` Every other command is treated as a plugin. Changing `main` to the following adds the default commands, or "plugins", to the `airshipctl` tool: ```go func main() { rootCmd, settings, err := cmd.NewRootCmd(os.Stdout) if err != nil { panic(err) } cmd.AddDefaultAirshipCTLCommands(rootCmd, settings) rootCmd.Execute() } ``` Compiling and running now provides the following commands: ``` Available Commands: bootstrap Bootstrap ephemeral Kubernetes cluster completion Generate autocompletions script for the specified shell (bash or zsh) config Modify airshipctl config files document manages deployment documents help Help about any command kubectl kubectl controls the Kubernetes cluster manager version Show the version number of airshipctl ------ more commands TBD ------ ``` Downloading and building the main `airshipctl` project will default to providing the builtin commands (such as `bootstrap`), much like the above. A plugin author wishing to use `airshipctl` can then use the `rootCmd` as the first of a series of building blocks. The following demonstrates the addition of a new command, `hello`: ```go package main import ( "fmt" "os" "opendev.org/airship/airshipctl/cmd" "github.com/spf13/cobra" ) func main() { rootCmd, settings, err := cmd.NewRootCmd(os.Stdout) if err != nil { panic(err) } cmd.AddDefaultAirshipCTLCommands(rootCmd, settings) helloCmd := &cobra.Command{ Use: "hello", Short: "Prints a friendly message to the screen", Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello World!") }, } rootCmd.AddCommand(helloCmd) rootCmd.Execute() } ``` ## Fine Tuning a Build There are a couple of ways in which a plugin author can fine tune their version of `airshipctl`. These manifest as an ability to pick and choose various plugins (including the defaults), and capabilities for accessing the same settings as other `airshipctl` commands. ### Command Selection In the previous section, we introduced the `AddDefaultAirshipCTLCommands` function. That command will simply dump all of the builtin commands onto the root. But a plugin author might not need all of the builtins. To deal with this, the author can pick and choose specific commands to add to their `airshipctl`, much like the following: ```go package main import ( "os" "opendev.org/airship/airshipctl/cmd" "opendev.org/airship/airshipctl/cmd/bootstrap" ) func main() { rootCmd, settings, err := cmd.NewRootCmd(os.Stdout) if err != nil { panic(err) } rootCmd.AddCommand(bootstrap.NewBootstrapCommand(settings)) rootCmd.Execute() } ``` This variant of `airshipctl` will have the `bootstrap` command, but will not have any other builtins. This can be particularly useful if a plugin author desires to "override" a specific functionality provided by a builtin command. For example, you might write your own `bootstrap` command and use it in place of the builtin. ### Accessing `airshipctl` settings The `airshipctl` will contain several settings which may be useful to a plugin author. The following snippet demonstrates how to use the `debug` flag, provided by `airshipctl`, as well as a custom `alt-message` flag, provided by the plugin. ```go package main import ( "fmt" "os" "opendev.org/airship/airshipctl/cmd" "opendev.org/airship/airshipctl/pkg/environment" "github.com/spf13/cobra" ) type Settings struct { *environment.AirshipCTLSettings AltMessage bool } func main() { rootCmd, rootSettings, err := cmd.NewRootCmd(os.Stdout) if err != nil { panic(err) } settings := Settings{AirshipCTLSettings: rootSettings} helloCmd := &cobra.Command{ Use: "hello", Short: "Prints a friendly message to the screen", Run: func(cmd *cobra.Command, args []string) { if settings.Debug { fmt.Println("DEBUG: a debugging message") } if settings.AltMessage { fmt.Println("Goodbye World!") } else { fmt.Println("Hello World!") } }, } helloCmd.PersistentFlags().BoolVar(&settings.AltMessage, "alt-message", false, "display an alternate message") rootCmd.AddCommand(helloCmd) rootCmd.Execute() } ``` The `AirshipCTLSettings` object can be found [here](../../pkg/environment/settings.go). Future documentation TBD.