使用k8s.io/code-generator编写CRD。
本项目代码在 https://github.com/chanjarster/k8s-code-gen-how-to
概览
k8s.io/code-generator是一个代码生成工具,用于为你的CRD生成kubernetes-style API实现,这样你就可以管理K8S上你的自定义CRD资源了。
第一步:初始化项目
1
2
|
MODULE=example.com/foo-controller
go mod init $MODULE
|
第二步:定义API
新建目录api/<group>/<version>
,这个目录下得有以下几个文件:
1
2
3
4
5
6
7
|
.
└── api
└── foo
└── v1alpha1
├── doc.go
├── register.go
└── types.go
|
执行命令:
1
2
3
|
GROUP=foo
VERSION=v1alpha1
mkdir -p api/$GROUP/$VERSION
|
编写文件api/<group>/<version>/doc.go
,注意//+groupName=foo.example.com
,你需要视情况修改:
1
2
3
4
5
|
// +k8s:deepcopy-gen=package
// +groupName=foo.example.com
// Package v1alpha1 is the v1alpha1 version of the API.
package v1alpha1
|
编写文件api/<group>/<version>/types.go
,注意视情况修改类名以及相关常量,Status
字段并非必须的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// These const variables are used in our custom controller.
const (
GroupName string = "foo.example.com"
Kind string = "Foo"
Version string = "v1alpha1"
Plural string = "foos"
Singluar string = "foo"
ShortName string = "foo"
Name string = Plural + "." + GroupName
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Foo is a specification for a Foo resource
type Foo struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec FooSpec `json:"spec"`
Status FooStatus `json:"status"`
}
// FooSpec is the spec for a Foo resource
type FooSpec struct {
DeploymentName string `json:"deploymentName"`
Replicas *int32 `json:"replicas"`
}
// FooStatus is the status for a Foo resource
type FooStatus struct {
AvailableReplicas int32 `json:"availableReplicas"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// FooList is a list of Foo resources
type FooList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Foo `json:"items"`
}
|
编写文件api/<group>/register.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
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
)
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{
Group: GroupName,
Version: Version,
}
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Foo{},
&FooList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
|
然后添加依赖:
1
2
|
K8S_VERSION=v0.18.5
go get k8s.io/apimachinery@$K8S_VERSION
|
第三步:代码生成
1)准备脚本
新建目录hack,我们需要在hack目录下有以下几个文件:
1
2
3
4
5
6
|
.
└── hack
├── boilerplate.go.txt
├── tools.go
├── update-codegen.sh
└── verify-codegen.sh
|
执行命令:
1
2
|
mkdir hack
touch hack/boilerplate.go.txt
|
新建hack/tools.go
文件:
1
2
3
4
5
|
// +build tools
package tools
import _ "k8s.io/code-generator"
|
新建hack/update-codegen.sh
,注意修改几个变量:
MODULE
和go.mod
保持一致
API_PKG=api
,和我们的目录保持一致
OUTPUT_PKG
GROUP_VERSION=foo.v1alpha1
而不是foo.example.com:v1alpha1
,因为code generator会读取我们之前写的api/<group>/<version>
下的代码,因此得要对应上这个路径:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
# corresponding to go mod init <module>
MODULE=example.com/foo-controller
# api package
APIS_PKG=api
# generated output package
OUTPUT_PKG=generated
# group-version such as foo:v1alpha1
GROUP_VERSION=foo:v1alpha1
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 the code with:
# --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
bash "${CODEGEN_PKG}"/generate-groups.sh "all" \
${MODULE}/${OUTPUT_PKG} ${MODULE}/${APIS_PKG} \
${GROUP_VERSION} \
--go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt \
--output-base "${SCRIPT_ROOT}"
# --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \
# To use your own boilerplate text append:
# --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt
|
新建hack/verify-codegen.sh
(文件内容请看github项目)。
2)下载code-generator
先把code-generator下载下来,注意这里的K8S版本号,得和前面是一致的:
1
2
3
|
K8S_VERSION=v0.18.5
go get k8s.io/code-generator@$K8S_VERSION
go mod vendor
|
然后给generate-groups.sh
添加可执行权限:
1
|
chmod +x vendor/k8s.io/code-generator/generate-groups.sh
|
3)生成代码
执行hack/update-codegen.sh
:
1
|
./hack/update-codegen.sh
|
代码会生成在example.com/foo-controller
目录下(回忆前面的MODULE=example.com/foo-controller
和OUTPUT_PKG=generated
参数):
1
2
3
4
5
6
7
8
9
10
|
example.com
└── foo-controller
├── api
│ └── foo
│ └── v1alpha1
│ └── zz_generated.deepcopy.go
└── generated
├── clientset
├── informers
└── listers
|
移动文件:
zz_generated.deepcopy.go
移动到api/<group>/<version>
下
example.com/foo-controller/generated
直接移出来,放到项目根下面generated
- 如果后面你修改了
types.go
,重新执行./hack/update-codegen.sh
就行了。
对于生成代码的说明:
clientset
:用于操作foos.foo.example.com
CRD资源
informers
:Informer接收来自服务器的CRD的变更事件
listers
:Lister提供只读的cache layer for GET和LIST请求
关于Controller
controller代码可以看项目的main.go。
本例子里controller读取环境变量KUBECONFIG
来启动Clientset以及和K8S通信,这个也符合k8s.io/client-go out-of-cluster example。在实际生产环境中,可以参考k8s.io/client-go in-cluster example。
如果你想要更灵活的做法,比如当提供了--kubeconfig
的时候采用out-of-cluster模式,否则则尝试in-cluster模式(看/var/run/secrets/kubernetes.io/serviceaccount
),可以参考prometheus-operator k8sutil.go的做法
参考文档
评论