code-generatorとは
code-generatorは、Kubernetes のAPI typeを実装するために、必要なコードを生成するツール。
このツールで生成できるものの基本は以下の4種類:
1. deepcopy:
KubernetesのObjectはすべてruntime.Object を実装する必要がありそれに必要なDeepCopyObject
生成する
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
Object interface must be supported by all API types registered with Scheme. Since objects in a scheme are expected to be serialized to the wire, the interface an Object must provide to the Scheme allows serializers to set the kind, version, and group the object is represented as. An Object may choose to return a no-op ObjectKindAccessor in cases where it is not expected to be serialized.
2. client
Kubernetes APIにアクセスするクライアント。
Kubernetesのbuilt-inリソースの場合は、clientsetを使ってアクセスする。API groupごとに一つのversionを持つ。
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
admissionregistrationV1 *admissionregistrationv1.AdmissionregistrationV1Client
admissionregistrationV1beta1 *admissionregistrationv1beta1.AdmissionregistrationV1beta1Client
internalV1alpha1 *internalv1alpha1.InternalV1alpha1Client
appsV1 *appsv1.AppsV1Client
appsV1beta1 *appsv1beta1.AppsV1beta1Client
appsV1beta2 *appsv1beta2.AppsV1beta2Client
...
}
自分でカスタムリソースを作成する場合には、clientsetは自分で作成する必要があるので、その場合にcode-generatorを使って生成する。
// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
*discovery.DiscoveryClient
exampleV1alpha1 *examplev1alpha1.ExampleV1alpha1Client
}
3. lister
in-memoryのcache.Indexerからオブジェクトをリストする
// FooLister helps list Foos.
// All objects returned here must be treated as read-only.
type FooLister interface {
// List lists all Foos in the indexer.
// Objects returned here must be treated as read-only.
List(selector labels.Selector) (ret []*v1alpha1.Foo, err error)
// Foos returns an object that can list and get Foos.
Foos(namespace string) FooNamespaceLister
FooListerExpansion
}
// fooLister implements the FooLister interface.
type fooLister struct {
indexer cache.Indexer
}
4. informer
対象となるKubernetes Objectの変更を検知して、Create, Update, Deleteに対応する処理などをする。Custom Resource作成時にcode-generatorで生成し、Custom Controller内で使われる。
// FooInformer provides access to a shared informer and lister for
// Foos.
type FooInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1alpha1.FooLister
}
type fooInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
生成 (Step by Step)
1. Foo
というAPI typeを定義する
mkdir code-generator-training && cd code-generator-training
git init
go mod init code-generator-training
go get k8s.io/apimachinery@v0.24.2
go get k8s.io/client-go@v0.24.2
go get k8s.io/code-generator@v0.24.2
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// 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"`
}
// FooList is a list of Foo resources
type FooList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Foo `json:"items"`
}
2. code-generatorでdeepcopyを生成する
2.1. DeepCopyとDeepCopyInfoを生成するためのコメントをつける
Packageに対して生成する場合: 以下のコメントを doc.go
につける
// +k8s:deepcopy-gen=package
typeごとに生成する場合: 以下のコメントをtypeにつける
// +k8s:deepcopy-gen=true
今回は、packageに対して生成
// +k8s:deepcopy-gen=package
// +groupName=example.com
package v1alpha1
2.2. DeepCopyInterfaceNameを生成するためのコメントをつける (Optional)
+k8s:deepcopy-gen:interfaces=<interface>
をtypeにつけることで、DeepCopyInterfaceNameを生成できる
例.
// +k8s:deepcopy-gen:interfaces=k8s.io/kubernetes/runtime.Object,k8s.io/kubernetes/runtime.List
この場合 DeepCopyObject
とDeepCopyList
が、返り値に指定したinterfaceを返すように生成される。
もう一つは、 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
をFooとFooListの上に追加する (typeのコメントから1行開けておく)
...
// +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"`
}
...
// +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"`
}
2.3. 生成する
https://github.com/kubernetes/code-generator のgenerate-groups.sh
を使って生成する。
git clone https://github.com/kubernetes/code-generator
generate-groups.shを使って生成する:
generate-groups.sh
Usage: generate-groups.sh <generators> <output-package> <apis-package> <groups-versions> ...
<generators> the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all".
<output-package> the output package name (e.g. github.com/example/project/pkg/generated).
<apis-package> the external types dir (e.g. github.com/example/api or github.com/example/project/pkg/apis).
<groups-versions> the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative
to <api-package>.
... arbitrary flags passed to all generator binaries.
Examples:
generate-groups.sh all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
generate-groups.sh deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
生成:
code-generator/generate-groups.sh deepcopy code-generator-training/pkg/client code-generator-training/pkg/api example.com:v1alpha1 --go-header-file code-generator/hack/boilerplate.go.txt --output-base $(pwd)/..
pkg/api/example.com/v1alpha1/zz_generated.deepcopy.go
が作成される
3. clientを生成
3.1. // +genclient
コメントをつける
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
...
3.2. 生成する
code-generator/generate-groups.sh client code-generator-training/pkg/client code-generator-training/pkg/api example.com:v1alpha1 --go-header-file code-generator/hack/boilerplate.go.txt --output-base $(pwd)/..
以下のファイルが生成される
pkg/client/clientset/versioned/
├── clientset.go
├── doc.go
├── fake
│ ├── clientset_generated.go
│ ├── doc.go
│ └── register.go
├── scheme
│ ├── doc.go
│ └── register.go
└── typed
└── example.com
└── v1alpha1
├── doc.go
├── example.com_client.go
├── fake
│ ├── doc.go
│ ├── fake_example.com_client.go
│ └── fake_foo.go
├── foo.go
└── generated_expansion.go
6 directories, 14 files
4. listerの生成
code-generator/generate-groups.sh lister code-generator-training/pkg/client code-generator-training/pkg/api example.com:v1alpha1 --go-header-file code-generator/hack/boilerplate.go.txt --output-base $(pwd)/..
以下のファイルが生成される:
tree pkg/client/listers
pkg/client/listers
└── example.com
└── v1alpha1
├── expansion_generated.go
└── foo.go
2 directories, 2 files
5. informerの生成
※informerはlisterに依存しているので、informerを生成するときはlisterを生成しておく必要がある
code-generator/generate-groups.sh informer code-generator-training/pkg/client code-generator-training/pkg/api example.com:v1alpha1 --go-header-file code-generator/hack/boilerplate.go.txt --output-base $(pwd)/..
以下のファイルが生成される
tree pkg/client/informers
pkg/client/informers
└── externalversions
├── example.com
│ ├── interface.go
│ └── v1alpha1
│ ├── foo.go
│ └── interface.go
├── factory.go
├── generic.go
└── internalinterfaces
└── factory_interfaces.go
4 directories, 6 files
生成 (まとめて)
1. types.goを作成
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +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"`
}
2. doc.goを作成
// +k8s:deepcopy-gen=package
// +groupName=example.com
package v1alpha1
3. deepcopy,client,lister,informerすべてを生成
code-generator/generate-groups.sh all code-generator-training/pkg/client code-generator-training/pkg/api example.com:v1alpha1 --go-header-file code-generator/hack/boilerplate.go.txt --output-base $(pwd)/..
生成されたコードの使い方
生成されたdeepcopy,client,lister,informerの使い方は https://github.com/nakamasato/code-generator-training で試したので参考にしていただければと。
大体Custom Controller作成するときに出てくるので、https://github.com/kubernetes/sample-controller も参考になる。