云原生学习路线导航页(持续更新中)
- 本文是 Kubernetes operator学习 系列第三篇,主要对 使用 code-generator 进行 CRD 自动代码生成进行学习
- Kubernetes operator学习系列 快捷链接
- Kubernetes operator(一)client-go篇
- Kubernetes operator(二)CRD篇
- Kubernetes operator(三)code-generator 篇
- Kubernetes operator(四)controller-tools 篇
kubernetes的开发中,有很多代码是相似而重复的,开发起来耗时耗力,因此就希望制作成了自动化工具,提高代码的可维护性和一致性。code-generator应运而生。
code-generator 是一个 代码生成工具集合,内部包含很多gen工具,用于自动生成与 Kubernetes 相关的客户端库、API 服务器(API server)和其他与 Kubernetes 相关的代码。
code-generator 位于 Kubernetes 代码库中的 staging/src/k8s.io/code-generator
包中,github地址为:https://github.com/kubernetes/code-generator
code-generator 的主要功能包括:
versioned client
、informer
、lister
以及其他工具方法internal
和 versioned
类型的转换defaulters
、internal
和 versioned
的clients
和informers
我们这里主要学习CRD编写自定义controller的场景
Global tags
: 全局的tag,放在具体版本的doc.go文件中Local tags
: 本地的tag,放在types.go文件中的具体的struct上// +tag-name
或
// +tag-name=value
告诉代码生成器不生成该对象的深拷贝方法
// +k8s:deepcopy-gen=false
告诉代码生成器生成该对象的深拷贝方法
// +k8s:deepcopy-gen=true
指定生成的代码中实现的接口,这里指定了实现 k8s.io/apimachinery/pkg/runtime.Object 接口的代码
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
告诉代码生成器生成整个包的对象深拷贝方法。这意味着生成的代码将包含用于深拷贝包中所有对象的相关代码
// +k8s:deepcopy-gen=package
指定自定义 API 组的名称。这个注释用于定义自定义 API 资源的 API 组,使其能够与其他资源进行区分
// +groupName=foo.example.com
package v1
指示代码生成器生成用于客户端库的代码
// +genclient
不要为自定义资源生成状态字段相关的代码
// +genclient:noStatus
表示这是一个集群级别的资源
// +genclient:nonNamespaced
不要生成默认的 HTTP 动词(verbs)相关的代码,这可能是因为该资源的操作方式与常规的 RESTful 操作并不完全相同
// +genclient:noVerbs
指示代码生成器只生成用于创建和删除操作的代码
// +genclient:onlyVerbs=create,delete
告诉代码生成器跳过指定的动词相关的代码生成,这表明生成的客户端库将不包含与这些操作相关的代码
// +genclient:skipVerbs=get,list,create,update,patch,delete,deleteCollection,watch
为特定的方法指定生成代码的细节,这里指定了当执行创建操作时所使用的方法以及其返回结果
// +genclient:method=Create,verb=create,result=k8s.io/apimachinery/pkg/apis/meta/v1.Status
使用informer-gen必须要写这一行
// +k8s:informers
指定生成的 Informer 代码对应的 API 组和版本
// +k8s:informers:groupVersion=${group}/${version}
指定生成的 Informer 代码对应的内部 API 版本
// +k8s:informers:internalVersion=internal/version
指定生成的 Informer 代码是否需要为版本化的客户端集(Versioned ClientSet)生成代码
// +k8s:informers:versionedClientSet=false
指定生成的 Informer 代码是否需要使用缓存
// +k8s:informers:cache
generate-groups.sh
和 generate-internal-groups.sh
kube_codegen.sh
types.go
:编写crd的资源结构,code-generator 的注释 tag一般写在这边里面doc.go
:声明了按照 package 维度,为所有 structs 提供生成的声明register.go
:提供注册到 runtime.Scheme 的函数。code-generator 的 register-gen 可以读取 doc.go 中的 // +groupName
,并生成 register.go# 创建并进入项目目录
mkdir code-generator-demo && cd code-generator-demo
# 初始化项目
go mod init code-generator-demo
# 获取依赖
go get k8s.io/[email protected]
go get k8s.io/[email protected]
go get k8s.io/[email protected]
// +k8s:deepcopy-gen=package
// +groupName=appcontroller.k8s.io
// Package v1alpha1 v1alpha1版本的api包
package v1alpha1
package v1alpha1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Application struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ApplicationSpec `json:"spec"`
Status ApplicationStatus `json:"status"`
}
type ApplicationSpec struct {
DeploymentName string `json:"deploymentName"`
Replicas *int32 `json:"replicas"`
}
type ApplicationStatus struct {
AvailableReplicas int32 `json:"availableReplicas"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ApplicationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Application `json:"items"`
}
package appcontroller
const (
GroupName = "appcontroller.k8s.io"
)
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@Time : 2024/1
@Author : grahamzhu
@Software: GoLand
*/
//go:build tools
// +build tools
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// This package imports things required by build scripts, to force `go mod` to see them as dependencies
package tools
import _ "k8s.io/code-generator"
#!/usr/bin/env bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# 设置脚本在执行过程中遇到任何错误时立即退出
set -o errexit
# 设置脚本在使用未定义的变量时立即退出
set -o nounset
# 设置脚本在管道命令中任意一条命令失败时立即退出
set -o pipefail
# 对generate-groups.sh 脚本的调用
../vendor/k8s.io/code-generator/generate-groups.sh \
"deepcopy,client,informer,lister" \
code-generator-demo/pkg/generated \
code-generator-demo/pkg/apis \
appcontroller:v1alpha1 \
--go-header-file $(pwd)/boilerplate.go.txt \
--output-base $(pwd)/../../
解释每个参数和选项的含义:
"deepcopy,client,informer,lister"
:指定要生成的代码类型,这里包括深拷贝方法、客户端、informer 和 lister。code-generator-demo/pkg/generated
:指定生成的代码的输出目录。code-generator-demo/pkg/apis
:指定包含 API 定义的目录。appcontroller:v1alpha1
:指定要生成的 API 版本和组。--go-header-file $(pwd)/boilerplate.go.txt
:指定用于生成的代码文件头部的文本文件,这里使用了当前目录下的 boilerplate.go.txt 文件。--output-base $(pwd)/../../
:指定生成的代码的基础目录,即输出目录的上一级目录。总结起来,该命令的作用是调用 Kubernetes 代码生成器脚本,根据指定的 API 定义和选项生成深拷贝方法、客户端、informer 和 lister 等代码,并将生成的代码输出到指定的目录中。
go mode tidy
# 生成vendor文件夹
go mod vendor
# 为vendor中的code-generator赋予权限
chmod -R 777 vendor
# 为hack中的update-codegen.sh脚本赋予权限
chmod -R 777 hack
# 调用脚本生成代码
$ cd hack && ./update-codegen.sh
Generating deepcopy funcs
Generating clientset for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/clientset
Generating listers for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/listers
Generating informers for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/informers
#此时目录变为如下情况
$ cd ../ && tree -L 5
.
├── go.mod
├── go.sum
├── hack
│ ├── boilerplate.go.txt
│ ├── tools.go
│ └── update-codegen.sh
├── pkg
│ ├── apis
│ │ └── appcontroller
│ │ ├── register.go
│ │ └── v1alpha1
│ │ ├── doc.go
│ │ ├── types.go
│ │ └── zz_generated.deepcopy.go
│ └── generated
│ ├── clientset
│ │ └── versioned
│ │ ├── clientset.go
│ │ ├── doc.go
│ │ ├── fake
│ │ ├── scheme
│ │ └── typed
│ ├── informers
│ │ └── externalversions
│ │ ├── appcontroller
│ │ ├── factory.go
│ │ ├── generic.go
│ │ └── internalinterfaces
│ └── listers
│ └── appcontroller
│ └── v1alpha1
└── vendor
├── github.com
undefined: v1alpha1.AddToScheme
错误、undefined: v1alpha1.Resource
错误。v1alpha1.AddToScheme
、v1alpha1.Resource
这两个是用于 client 注册的package v1alpha1
import (
"code-generator-demo/pkg/apis/appcontroller"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: appcontroller.GroupName, Version: "v1alpha1"}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder initializes a scheme builder
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = SchemeBuilder.AddToScheme
)
// Adds the list of known types to Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Application{},
&ApplicationList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
package main
import (
"code-generator-demo/pkg/generated/clientset/versioned/typed/appcontroller/v1alpha1"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
if err != nil {
panic(err.Error())
}
// 在 pkg/generated/clientset/versioned/typed/appcontroller/v1alpha1/appcontroller_client.go 中
// 自动生成的 AppcontrollerV1alpha1Client,用于操作这种GVR
appClient, err := v1alpha1.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// 获取default命名空间下的所有Application
appList, err := appClient.Applications("default").List(metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
for _, app := range appList.Items {
fmt.Println(app.Name)
}
}
上面的项目,存在的问题:
解决方法: