|
| 1 | +# Extending the CLI and Scaffolds |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +You can extend Kubebuilder to allow your project to have the same CLI features and provide the plugins scaffolds. |
| 6 | + |
| 7 | +## CLI system |
| 8 | + |
| 9 | +Plugins are run using a [`CLI`][cli] object, which maps a plugin type to a subcommand and calls that plugin's methods. |
| 10 | +For example, writing a program that injects an `Init` plugin into a `CLI` then calling `CLI.Run()` will call the |
| 11 | +plugin's [SubcommandMetadata][plugin-sub-command], [UpdatesMetadata][plugin-update-meta] and `Run` methods with information a user has passed to the |
| 12 | +program in `kubebuilder init`. Following an example: |
| 13 | + |
| 14 | +```go |
| 15 | +package cli |
| 16 | + |
| 17 | +import ( |
| 18 | + log "github.com/sirupsen/logrus" |
| 19 | + "github.com/spf13/cobra" |
| 20 | + |
| 21 | + "sigs.k8s.io/kubebuilder/v3/pkg/cli" |
| 22 | + cfgv3 "sigs.k8s.io/kubebuilder/v3/pkg/config/v3" |
| 23 | + "sigs.k8s.io/kubebuilder/v3/pkg/plugin" |
| 24 | + kustomizecommonv1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/common/kustomize/v1" |
| 25 | + "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang" |
| 26 | + declarativev1 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/declarative/v1" |
| 27 | + golangv3 "sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3" |
| 28 | + |
| 29 | +) |
| 30 | + |
| 31 | +var ( |
| 32 | + // The following is an example of the commands |
| 33 | + // that you might have in your own binary |
| 34 | + commands = []*cobra.Command{ |
| 35 | + myExampleCommand.NewCmd(), |
| 36 | + } |
| 37 | + alphaCommands = []*cobra.Command{ |
| 38 | + myExampleAlphaCommand.NewCmd(), |
| 39 | + } |
| 40 | +) |
| 41 | + |
| 42 | +// GetPluginsCLI returns the plugins based CLI configured to be used in your CLI binary |
| 43 | +func GetPluginsCLI() (*cli.CLI) { |
| 44 | + // Bundle plugin which built the golang projects scaffold by Kubebuilder go/v3 |
| 45 | + gov3Bundle, _ := plugin.NewBundle(golang.DefaultNameQualifier, plugin.Version{Number: 3}, |
| 46 | + kustomizecommonv1.Plugin{}, |
| 47 | + golangv3.Plugin{}, |
| 48 | + ) |
| 49 | + |
| 50 | + |
| 51 | + c, err := cli.New( |
| 52 | + // Add the name of your CLI binary |
| 53 | + cli.WithCommandName("example-cli"), |
| 54 | + |
| 55 | + // Add the version of your CLI binary |
| 56 | + cli.WithVersion(versionString()), |
| 57 | + |
| 58 | + // Register the plugins options which can be used to do the scaffolds via your CLI tool. See that we are using as example here the plugins which are implemented and provided by Kubebuilder |
| 59 | + cli.WithPlugins( |
| 60 | + gov3Bundle, |
| 61 | + &declarativev1.Plugin{}, |
| 62 | + ), |
| 63 | + |
| 64 | + // Defines what will be the default plugin used by your binary. It means that will be the plugin used if no info be provided such as when the user runs `kubebuilder init` |
| 65 | + cli.WithDefaultPlugins(cfgv3.Version, gov3Bundle), |
| 66 | + |
| 67 | + // Define the default project configuration version which will be used by the CLI when none is informed by --project-version flag. |
| 68 | + cli.WithDefaultProjectVersion(cfgv3.Version), |
| 69 | + |
| 70 | + // Adds your own commands to the CLI |
| 71 | + cli.WithExtraCommands(commands...), |
| 72 | + |
| 73 | + // Add your own alpha commands to the CLI |
| 74 | + cli.WithExtraAlphaCommands(alphaCommands...), |
| 75 | + |
| 76 | + // Adds the completion option for your CLI |
| 77 | + cli.WithCompletion(), |
| 78 | + ) |
| 79 | + if err != nil { |
| 80 | + log.Fatal(err) |
| 81 | + } |
| 82 | + |
| 83 | + return c |
| 84 | +} |
| 85 | + |
| 86 | +// versionString returns the CLI version |
| 87 | +func versionString() string { |
| 88 | + // return your binary project version |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +This program can then be built and run in the following ways: |
| 93 | + |
| 94 | +Default behavior: |
| 95 | + |
| 96 | +```sh |
| 97 | +# Initialize a project with the default Init plugin, "go.example.com/v1". |
| 98 | +# This key is automatically written to a PROJECT config file. |
| 99 | +$ my-bin-builder init |
| 100 | +# Create an API and webhook with "go.example.com/v1" CreateAPI and |
| 101 | +# CreateWebhook plugin methods. This key was read from the config file. |
| 102 | +$ my-bin-builder create api [flags] |
| 103 | +$ my-bin-builder create webhook [flags] |
| 104 | +``` |
| 105 | + |
| 106 | +Selecting a plugin using `--plugins`: |
| 107 | + |
| 108 | +```sh |
| 109 | +# Initialize a project with the "ansible.example.com/v1" Init plugin. |
| 110 | +# Like above, this key is written to a config file. |
| 111 | +$ my-bin-builder init --plugins ansible |
| 112 | +# Create an API and webhook with "ansible.example.com/v1" CreateAPI |
| 113 | +# and CreateWebhook plugin methods. This key was read from the config file. |
| 114 | +$ my-bin-builder create api [flags] |
| 115 | +$ my-bin-builder create webhook [flags] |
| 116 | +``` |
| 117 | + |
| 118 | +### CLI manages the PROJECT file |
| 119 | + |
| 120 | +The CLI is responsible for managing the [PROJECT file config][project-file-config], representing the configuration of the projects that are scaffold by the CLI tool. |
| 121 | + |
| 122 | +## Plugins |
| 123 | + |
| 124 | +Kubebuilder provides scaffolding options via plugins. Plugins are responsible for implementing the code that will be executed when the sub-commands are called. You can create a new plugin by implementing the [Plugin interface][plugin-interface]. |
| 125 | + |
| 126 | +On top of being a `Base`, a plugin should also implement the [`SubcommandMetadata`][plugin-subc] interface so it can be run with a CLI. It optionally to set custom help text for the target command; this method can be a no-op, which will preserve the default help text set by the [cobra][cobra] command constructors. |
| 127 | + |
| 128 | +Kubebuilder CLI plugins wrap scaffolding and CLI features in conveniently packaged Go types that are executed by the |
| 129 | +`kubebuilder` binary, or any binary which imports them. More specifically, a plugin configures the execution of one |
| 130 | +of the following CLI commands: |
| 131 | + |
| 132 | +- `init`: project initialization. |
| 133 | +- `create api`: scaffold Kubernetes API definitions. |
| 134 | +- `create webhook`: scaffold Kubernetes webhooks. |
| 135 | + |
| 136 | +Plugins are identified by a key of the form `<name>/<version>`. There are two ways to specify a plugin to run: |
| 137 | + |
| 138 | +- Setting `kubebuilder init --plugins=<plugin key>`, which will initialize a project configured for plugin with key |
| 139 | + `<plugin key>`. |
| 140 | + |
| 141 | +- A `layout: <plugin key>` in the scaffolded [PROJECT configuration file][project-file]. Commands (except for `init`, which scaffolds this file) will look at this value before running to choose which plugin to run. |
| 142 | + |
| 143 | +By default, `<plugin key>` will be `go.kubebuilder.io/vX`, where `X` is some integer. |
| 144 | + |
| 145 | +For a full implementation example, check out Kubebuilder's native [`go.kubebuilder.io`][kb-go-plugin] plugin. |
| 146 | + |
| 147 | +### Plugin naming |
| 148 | + |
| 149 | +Plugin names must be DNS1123 labels and should be fully qualified, i.e. they have a suffix like |
| 150 | +`.example.com`. For example, the base Go scaffold used with `kubebuilder` commands has name `go.kubebuilder.io`. |
| 151 | +Qualified names prevent conflicts between plugin names; both `go.kubebuilder.io` and `go.example.com` can both scaffold |
| 152 | +Go code and can be specified by a user. |
| 153 | + |
| 154 | +### Plugin versioning |
| 155 | + |
| 156 | +A plugin's `Version()` method returns a [`plugin.Version`][plugin-version-type] object containing an integer value |
| 157 | +and optionally a stage string of either "alpha" or "beta". The integer denotes the current version of a plugin. |
| 158 | +Two different integer values between versions of plugins indicate that the two plugins are incompatible. The stage |
| 159 | +string denotes plugin stability: |
| 160 | + |
| 161 | +- `alpha`: should be used for plugins that are frequently changed and may break between uses. |
| 162 | +- `beta`: should be used for plugins that are only changed in minor ways, ex. bug fixes. |
| 163 | + |
| 164 | +### Breaking changes |
| 165 | + |
| 166 | +Any change that will break a project scaffolded by the previous plugin version is a breaking change. |
| 167 | + |
| 168 | +### Plugins Deprecation |
| 169 | + |
| 170 | +Once a plugin is deprecated, have it implement a [Deprecated][deprecate-plugin-doc] interface so a deprecation warning will be printed when it is used. |
| 171 | + |
| 172 | +## Bundle Plugins |
| 173 | + |
| 174 | +[Bundle Plugins][bundle-plugin-doc] allow you to create a plugin that is a composition of many plugins: |
| 175 | + |
| 176 | +```go |
| 177 | + // see that will be like myplugin.example/v1` |
| 178 | + myPluginBundle, _ := plugin.NewBundle(`<plugin-name>`,`<plugin-version>`, |
| 179 | + pluginA.Plugin{}, |
| 180 | + pluginB.Plugin{}, |
| 181 | + pluginC.Plugin{}, |
| 182 | + ) |
| 183 | + |
| 184 | +``` |
| 185 | + |
| 186 | +Note that it means that when a user of your CLI calls this plugin, the execution of the sub-commands will be sorted by the order to which they were added in a chain: |
| 187 | + |
| 188 | + |
| 189 | +> sub-command of plugin A -> sub-command of plugin B -> sub-command of plugin C |
| 190 | +
|
| 191 | +Then, to initialize using this "Plugin Bundle" which will run the chain of plugins: |
| 192 | + |
| 193 | +``` |
| 194 | +kubebuider init --plugins=myplugin.example/v1 |
| 195 | +``` |
| 196 | + |
| 197 | +- Runs init sub-command of the plugin A |
| 198 | +- And then, runs init sub-command of the plugin B |
| 199 | +- And then, runs init sub-command of the plugin C |
| 200 | + |
| 201 | +[project-file-config]: ../reference/project-config.md |
| 202 | +[plugin-interface]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Plugin |
| 203 | +[go-dev-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3 |
| 204 | +[plugin-sub-command]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Subcommand |
| 205 | +[project-file]: ../reference/project-config.md |
| 206 | +[plugin-subc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Subcommand |
| 207 | +[cobra]:https://pkg.go.dev/github.com/spf13/cobra |
| 208 | +[kb-go-plugin]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugins/golang/v3 |
| 209 | +[bundle-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Bundle |
| 210 | +[deprecate-plugin-doc]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Deprecated |
| 211 | +[plugin-update-meta]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#UpdatesMetadata |
| 212 | +[cli]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/cli |
| 213 | +[plugin-version-type]: https://pkg.go.dev/sigs.k8s.io/kubebuilder/v3/pkg/plugin#Version |
0 commit comments