<aside> 🤡 本文主要介绍如何使用代码生成器为 CRD 资源自动生成代码

</aside>

介绍

上节课我们介绍了 CRD 的使用,了解到 CRD 仅仅是一种资源的定义而已,需要一个对应的控制器去监听 CRD 的各种事件来添加自己的业务逻辑才有实际意义,接下来我们就来介绍如何为 CRD 创建一个自定义控制器。

代码生成器

要实现自己的控制器原理比较简单,前面我们也介绍过如何编写控制器,最重要的就是要去实现 ListAndWatch 操作、获取资源的 Informer 和 Indexer、以及通过一个 workqueue 去接收事件来进行处理,所以我们就要想办法来编写我们自定义的 CRD 资源对应的 Informer、ClientSet 这些工具,前面我们已经了解了对于内置的 Kubernetes 资源对象这些都是已经内置到源码中了,对于我们自己的 CRD 资源肯定不会内置到源码中的,所以就需要我们自己去实现,比如要为 CronTab 这个资源对象实现一个 DeepCopyObject 函数,这样才会将我们自定义的对象转换成 runtime.Object,系统才能够识别,但是客户端相关的操作实现又非常多,而且实现方式基本上都是一致的,所以 Kubernetes 就为我们提供了代码生成器这样的工具,我们可以来自动生成客户端访问的一些代码,比如 Informer、ClientSet 等等。

code-generator

code-generator 就是 Kubernetes 提供的一个用于代码生成的项目,它提供了以下工具为 Kubernetes 中的资源生成代码:

Informer 和 Lister 是构建控制器的基础,使用这4个代码生成器可以创建全功能的、和 Kubernetes 上游控制器工作机制相同的 production-ready 的控制器。

code-generator 还包含一些其它的代码生成器,例如 Conversion-gen 负责产生内外部类型的转换函数、Defaulter-gen 负责处理字段默认值。大部分的生成器支持--input-dirs参数来读取一系列输入包,处理其中的每个类型,然后生成代码:

  1. 部分代码生成到输入包所在目录,例如 deepcopy-gen 生成器,也可以使用参数--output-file-base "zz_generated.deepcopy" 来定义输出文件名
  2. 其它代码生成到 --output-package 指定的目录,例如 client-geninformer-genlister-gen 等生成器

在开发 CRD 的控制器的时候,我们可以编写一个脚本来统一调用生成器生成代码,我们可以直接使用 sample-controller 仓库中提供的 hack/update-codegen.sh 脚本。

#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
# 代码生成器包的位置
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}

# generate-groups.sh <generators> <output-package> <apis-package> <groups-versions>
#                    使用哪些生成器,可选值 deepcopy,defaulter,client,lister,informer,逗号分隔,all表示全部使用
#                    输出包的导入路径  
#                    CR 定义所在路径
#                    API 组和版本
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \\
  k8s.io/sample-controller/pkg/generated k8s.io/sample-controller/pkg/apis \\
  samplecontroller:v1alpha1 \\
  --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \\
  --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt

# 自动生成的源码头部附加的内容:
#   --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt

执行上面的脚本后,所有 API 代码会生成在 pkg/apis 目录下,clientsets、informers、listers 则生成在 pkg/generated 目录下。不过从脚本可以看出需要将 code-generator 的包放置到 vendor 目录下面,现在我们都是使用 go modules 来管理依赖保,我们可以通过执行 go mod vendor 命令将依赖包放置到 vendor 目录下面来。

我们还可以进一步提供 hack/verify-codegen.sh 脚本,用于判断生成的代码是否 up-to-date: