扩展命令行界面功能和插件
Kubebuilder 提供了一种可扩展的架构,用于使用插件搭建项目。这些插件允许您自定义 CLI 行为或集成新功能。
在本指南中,我们将探讨如何扩展 CLI 功能、创建自定义插件以及打包多个插件。
创建自定义插件
要创建自定义插件,您需要实现 Kubebuilder 插件接口。
此接口允许您的插件挂钩到 Kubebuilder 的命令(init
、create api
、create webhook
等),并添加自定义逻辑。
自定义插件示例
您可以创建一个插件,它生成特定语言的框架和所需的配置文件,使用 捆绑插件。此示例演示如何将 Go 语言插件与 Kustomize 插件结合使用:
import (
kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2"
golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
)
mylanguagev1Bundle, _ := plugin.NewBundle(
plugin.WithName("mylanguage.kubebuilder.io"),
plugin.WithVersion(plugin.Version{Number: 1}),
plugin.WithPlugins(kustomizecommonv2.Plugin{}, mylanguagev1.Plugin{}),
)
这个结构使您能够通过 Kustomize 建立一个通用的配置基础,并通过 mylanguagev1
处理特定于语言的文件。
您还可以使用您的插件来构建特定的资源,例如 CRD 和控制器,使用 create api
和 create webhook
子命令。
插件子命令
插件负责实现当子命令被调用时将执行的代码。您可以通过实现 插件接口 来创建一个新的插件。
除了作为 Base
之外,插件还应实现 SubcommandMetadata
接口,以便可以通过 CLI 运行。可选地,可以为目标命令设置自定义帮助文本;此方法可以是无操作的,这将保留由 cobra 命令构造函数设置的默认帮助文本。
Kubebuilder CLI 插件将脚手架和 CLI 功能包装在方便的 Go 类型中,这些类型由 kubebuilder
二进制文件或任何导入它们的二进制文件执行。更具体地说,一个插件配置以下 CLI 命令之一的执行:
init
:初始化项目结构。create api
:生成一个新的 API 和控制器。创建 webhook
: 建立一个新的 webhook。编辑
: 编辑项目结构。
以下是使用 init
子命令与自定义插件的示例:
kubebuilder init --plugins=mylanguage.kubebuilder.io/v1
这将使用 mylanguage
插件初始化一个项目。
插件密钥
插件通过 <name>/<version>
形式的键来识别。指定要运行的插件的方式有两种:
-
设置
kubebuilder init --plugins=<plugin key>
,将初始化一个为键为<plugin key>
的插件配置的项目。 -
在生成的 项目配置文件 中的
layout: <插件键>
。在运行命令时(除了初始化命令,它会生成此文件),命令会查看该值以选择要运行的插件。
默认情况下,<plugin key>
将为 go.kubebuilder.io/vX
,其中 X
是某个整数。
要查看完整的实现示例,请查看 Kubebuilder 的原生 go.kubebuilder.io
插件。
插件命名
插件名称必须是 DNS1123 标签,并且应为完全合格的名称,即它们有一个后缀,如 .example.com
。例如,使用 kubebuilder
命令的基础 Go 脚手架名称为 go.kubebuilder.io
。合格的名称可以避免插件名称之间的冲突;go.kubebuilder.io
和 go.example.com
都可以生成 Go 代码,并且可以由用户指定。
插件版本管理
插件的 Version()
方法返回一个 plugin.Version
对象,包含一个整数值和可选的阶段字符串,它可以是“alpha“或“beta“。该整数表示插件的当前版本。两个不同的整数值表示插件版本之间的不兼容性。阶段字符串表示插件的稳定性:
alpha
:应该用于那些经常更改并可能在使用之间出现故障的插件。beta
:应适用于仅在小幅度上进行更改的插件,例如错误修复。
标准文本
Kubebuilder 内部插件使用模板来生成代码文件。Kubebuilder 通过模板化来搭建插件的文件。例如,在创建新项目时,go/v4
插件会使用其实现中定义的模板来生成 go.mod
文件。
您可以通过定义自己的模板并使用 Kubebuilder 的机械库 来生成文件,从而扩展您自定义插件的功能。该库允许您:
- Define file I/O behaviors.
- 将标记添加到搭建的文件中。
- 为您的脚手架指定模板。
示例:样板文本
例如,go/v4 通过定义一个实现了 machinery 接口 的对象来搭建 go.mod
文件。原始模板被设置为 Template.SetTemplateDefaults
方法中的 TemplateBody
字段:
/*******************************************************************************
版权所有 2022 Kubernetes 作者。
根据 Apache 许可证,版本 2.0("许可证")授权;
除非遵守许可证,否则您不得使用此文件。
您可以在以下网址获取许可证的副本:
http://www.apache.org/licenses/LICENSE-2.0
除非适用法律要求或书面达成协议,否则根据许可证分发的软件是以"原样"基础分发的,
不提供任何种类的保证或条件,明示或暗示。
有关许可证的具体语言,请参见许可证以了解管理权限和
根据许可证的限制。
******************************************************************************/
package templates
import (
"sigs.k8s.io/kubebuilder/v4/pkg/machinery"
)
var _ machinery.Template = &GoMod{}
// GoMod生成一个定义项目依赖的文件type GoMod struct {
machinery.TemplateMixin
machinery.RepositoryMixin
ControllerRuntimeVersion string
}
// SetTemplateDefaults实现了machinery.Templatefunc (f *GoMod) SetTemplateDefaults() error {
if f.Path == "" {
f.Path = "go.mod"
}
f.TemplateBody = goModTemplate
f.IfExistsAction = machinery.OverwriteFile
return nil
}
const goModTemplate = `模块 {{ .Repo }}
go 1.23.0
godebug 默认=go1.23
依赖 (
sigs.k8s.io/controller-runtime {{ .ControllerRuntimeVersion }}
)
`
实现该机械接口的对象随后将传递给脚手架的执行:
// Scaffold 实现了 cmdutil.Scaffolder
func (s *initScaffolder) Scaffold() error {
log.Println("Writing scaffold for you to edit...")
// 初始化将把样板文件写入磁盘的机器架构。
// 样板文件需要作为一个单独步骤进行搭建,因为它将被
// 其他文件使用,包括在此命令调用中搭建的文件。 scaffold := machinery.NewScaffold(s.fs,
machinery.WithConfig(s.config),
)
...
return scaffold.Execute(
...
&templates.GoMod{
ControllerRuntimeVersion: ControllerRuntimeVersion,
},
...
)
}
示例:在插件中覆盖文件
让我们假设,当调用一个子命令时,你想要覆盖一个现有的文件。
例如,要修改 Makefile
并添加自定义构建步骤,在模板的定义中可以使用以下选项:
f.IfExistsAction = machinery.OverwriteFile
通过使用这些选项,您的插件可以控制由 Kubebuilder 的默认脚手架生成的某些文件。
定制现有支架
Kubebuilder 提供了实用函数,帮助您修改默认的脚手架。通过使用 插件工具,您可以向 Kubebuilder 生成的文件插入、替换或附加内容,从而完全控制脚手架的过程。
这些工具使您能够:
- 插入内容:在文件的特定位置添加内容。
- 替换内容:搜索并替换文件的特定部分。
- 追加内容:在文件末尾添加内容,而不删除或更改现有内容。
Example
如果您需要在生成的文件中插入自定义内容,可以使用插件工具提供的 InsertCode
函数:
pluginutil.InsertCode(filename, target, code)
这种方法使您能够在构建自定义插件时扩展和修改生成的脚手架。
有关更多细节,请参考 Kubebuilder 插件工具。
捆绑插件
插件可以被打包以组合成更复杂的脚手架。插件包是多个插件的组合,这些插件按照预定义的顺序执行。例如:
myPluginBundle, _ := plugin.NewBundle(
plugin.WithName("myplugin.example.com"),
plugin.WithVersion(plugin.Version{Number: 1}),
plugin.WithPlugins(pluginA.Plugin{}, pluginB.Plugin{}, pluginC.Plugin{}),
)
该捆绑包将按照指定的顺序为每个插件执行 init
子命令:
pluginA
pluginB
- 插件C
以下命令将运行捆绑的插件:
kubebuilder init --plugins=myplugin.example.com/v1
命令行系统
插件是通过 CLI
对象运行的,该对象将插件类型映射到子命令,并调用该插件的方法。例如,编写一个将 Init
插件注入到 CLI
的程序,然后调用 CLI.Run()
将调用插件的 SubcommandMetadata、UpdatesMetadata 和 Run
方法,使用用户在 kubebuilder init
中传递给程序的信息。以下是一个示例:
package cli
import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"sigs.k8s.io/kubebuilder/v4/pkg/cli"
cfgv3 "sigs.k8s.io/kubebuilder/v4/pkg/config/v3"
"sigs.k8s.io/kubebuilder/v4/pkg/plugin"
kustomizecommonv2 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/common/kustomize/v2"
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang"
deployimage "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/deploy-image/v1alpha1"
golangv4 "sigs.k8s.io/kubebuilder/v4/pkg/plugins/golang/v4"
)
var (
// 以下是您自己的二进制文件中可能包含的命令示例
// commands = []*cobra.Command{
myExampleCommand.NewCmd(),
}
alphaCommands = []*cobra.Command{
myExampleAlphaCommand.NewCmd(),
}
)
// GetPluginsCLI 返回配置在您的 CLI 二进制文件中使用的基于插件的 CLI。func GetPluginsCLI() (*cli.CLI) {
// 打包插件,用于构建由 Kubebuilder go/v4 创建的 Golang 项目脚手架。 gov3Bundle, _ := plugin.NewBundleWithOptions(plugin.WithName(golang.DefaultNameQualifier),
plugin.WithVersion(plugin.Version{Number: 3}),
plugin.WithPlugins(kustomizecommonv2.Plugin{}, golangv4.Plugin{}),
)
c, err := cli.New(
// 添加你的 CLI 二进制文件的名称
cli.WithCommandName("example-cli"),
// 添加你的 CLI 二进制文件的版本。 cli.WithVersion(versionString()),
// 注册可以通过你的 CLI 工具使用的插件选项,以进行脚手架。请注意,我们在这里使用的示例是由 Kubebuilder 实现和提供的插件。 cli.WithPlugins(
gov3Bundle,
&deployimage.Plugin{},
),
// 定义您的二进制文件将使用的默认插件。这意味着如果没有提供信息,例如当用户运行 `kubebuilder init` 时,将使用这个插件。 cli.WithDefaultPlugins(cfgv3.Version, gov3Bundle),
// 定义默认的项目配置版本,CLI在未通过--project-version标志提供时将使用该版本。 cli.WithDefaultProjectVersion(cfgv3.Version),
// 将您自己的命令添加到命令行接口 (CLI) cli.WithExtraCommands(commands...),
// 将您自己的 alpha 命令添加到 CLI 中
cli.WithExtraAlphaCommands(alphaCommands...),
// 为你的命令行界面添加补全选项
cli.WithCompletion(),
)
if err != nil {
log.Fatal(err)
}
return c
}
// versionString 返回 CLI 版本func versionString() string {
// 返回您的二进制项目版本}
该程序可以通过以下方式构建和运行:
默认行为:
# 使用默认的 Init 插件"go.example.com/v1"初始化项目。
# 此密钥会自动写入 PROJECT 配置文件中。
$ my-bin-builder init
# 使用 "go.example.com/v1" 的 CreateAPI 和 CreateWebhook 插件方法创建 API 和 webhook。此密钥是从配置文件中读取的。$ my-bin-builder create api [flags]
$ my-bin-builder create webhook [flags]
使用 --plugins
选择一个插件:
# 使用 "ansible.example.com/v1" 初始化插件初始化项目。
# 和上面一样,这个密钥会写入配置文件中。
$ my-bin-builder init --plugins ansible
# 使用 "ansible.example.com/v1" 的 CreateAPI 和 CreateWebhook 插件方法创建 API 和 Webhook。此密钥是从配置文件中读取的。$ my-bin-builder create api [flags]
$ my-bin-builder create webhook [flags]
输入应在项目文件中跟踪。
CLI 负责管理 项目文件配置,该配置表示由 CLI 工具搭建的项目的配置。
在扩展 Kubebuilder 时,建议确保您的工具或 外部插件 正确使用 PROJECT 文件 来跟踪相关信息。这确保了其他外部工具和插件可以与项目正确集成。它还允许工具特性帮助用户重新构建他们的项目,例如 Kubebuilder 提供的 项目升级助手,确保 PROJECT 文件中跟踪的信息可以用于多种目的。
例如,插件可以检查它们是否支持项目设置,并根据跟踪的输入重新执行命令。
Example
通过运行以下命令使用 Deploy Image 插件来搭建 API 及其控制器:
kubebyilder create api --group example.com --version v1alpha1 --kind Memcached --image=memcached:memcached:1.6.26-alpine3.19 --image-container-command="memcached,--memory-limit=64,-o,modern,-v" --image-container-port="11211" --run-as-user="1001" --plugins="deploy-image/v1-alpha" --make=false
以下条目将被添加到项目文件中:
...
plugins:
部署图像.go.kubebuilder.io/v1-alpha:
resources:
- domain: testproject.org
group: example.com
kind: Memcached
选项:
容器命令: memcached,--memory-limit=64,-o,modern,-v
容器端口: "11211"
image: memcached:memcached:1.6.26-alpine3.19
以用户身份运行: "1001"
version: v1alpha1
- domain: testproject.org
group: example.com
kind: Busybox
选项:
image: busybox:1.36.1
version: v1alpha1
...
通过检查 PROJECT 文件,可以了解插件是如何使用的以及提供了哪些输入。这不仅可以基于跟踪的数据重新执行命令,还能够创建可以依赖这些信息的功能或插件。