编写operator通常会依赖一些框架来生成代码, 主要用到的是operator-sdk, 新版的operator-sdk内部用的是kubebuilder来生成脚手架. 但是kubebuilder有个问题就是生成的CRD controller使用的是controller-runtime提供的client来访问k8s, 给controller用倒是没啥问题, 当外部应用希望通过clientset对CRD进行CRUD操作时会发现kubebuilder不支持生成clientset.
一种方法是自己直接通过code-generator来为kubebuilder创建出来的CRD单独生成一套clientset, 例如:
${GOPATH}/k8s.io/code-generator/generate-groups.sh client,informer,lister github.com/xxx-controller/pkg/client githubc.com/xxx-controller/api GROUP:VERSION
但是这样会遇到一个问题, kubebuilder生成的代码, 类型定义所在文件的路径是xxx-controller/api/v1, 而code-generator默认类型定义文件的路径是xxx-controller/api/GROUP/VERSION, 其中”xxx-controller/api”这部分可以通过参数指定, 但是整个路径无法指定, 于是就会报错”cannot find module providing package”. 一个想法是将GROUP设置成”.”, 这样最终code-generator就会去找xxx-controller/api/./v1这么一个路径, 满足我们的需求. 然而实际运行后会发现informer和lister并没有生成, 也没有任何报错.
研究了code-generator的相关代码, 大致上来说, code-generator初始化过程中会将指定目录里的go模块解析, 并将路径存在一个map里, 然后遍历input-dir, 从map里取出对应的pkg, 进行代码生成, 然而我们传入的input-dir参数是”xxx-controller/api/./v1″, 而code-generator在初始化过程中, 先是使用”xxx-controller/api/./v1″这个路径导入pkg, 然后取得是pkg.ImportPath做为map的key, 而这个ImportPath是”xxx-controller/api/v1″. 因此会找不到对应的pkg.
我们要做的是修改code-generator的代码, 让code-generator遍历input-dir的时候, 将”./”去掉:
修改k8s.io/code-generator/cmd/lister-gen/generators/lister.go以及k8s.io/code-generator/cmd/informer-gen/generators/informer.go, 找到Packages这个函数, 将
for _, inputDir := range arguments.InputDirs {
改成
for _, inputDir := range arguments.InputDirs { inputDir = strings.Replace(inputDir, “.”, “/”, -1)
然后重新执行即可:
${GOPATH}/k8s.io/code-generator/generate-groups.sh client,informer,lister github.com/xxx-controller/pkg/client githubc.com/xxx-controller/api GROUP:VERSION
generate-groups.sh会重新编译lister-gen及informer-gen并调用.