1. 前言
转载请说明原文出处, 尊重他人劳动成果!
源码位置: https://github.com/nicktming/kubernetes/tree/tming-v1.13/staging/src/k8s.io/code-generator
分支: tming-v1.13 (基于v1.13版本)
由于
crd
是需要生成代码的, 当然如果自己把那段代码自己写上也不会有任何问题, 只是这些代码重复性很高, 所以规律性就很强, 只需要把某些字段改成自己的字段即可. 所以本文将分析如何使用生成代码并分析其原理.
2. 例子
2.1 准备crd
参考 Kubernetes Deep Dive: Code Generation for CustomResources 以及 [极客时间深入剖析Kubernetes] (推荐学习)
文件内容以及结构如下:
crd.png
doc.go 和 types.go
===> pkg/apis/example.com/v1/doc.go
// +k8s:deepcopy-gen=package,register
// +groupName=nicktming.example.com
package v1
===> pkg/apis/example.com/v1/types.go
package v1
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Database describes a database.
type Database struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DatabaseSpec `json:"spec"`
}
// DatabaseSpec is the spec for a Foo resource
type DatabaseSpec struct {
User string `json:"user"`
Password string `json:"password"`
Encoding string `json:"encoding,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DatabaseList is a list of Database resources
type DatabaseList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Database `json:"items"`
}
Global Tags
1.
// +k8s:deepcopy-gen=package,register
表示为该package
下面所有的Type
创建DeepCopy
方法. (关于什么是Type
后面源码分析部分会解释)
It tells deepcopy-gen to create deepcopy methods by default for every
type in that package.
If you have types where deepcopy is not necessary or not desired, you
can opt-out for such a type with a local tag // +k8s:deepcopy-gen=false.
If you do not enable package-wide deepcopy, you have to opt-in
to deepcopy for each desired type via // +k8s:deepcopy-gen=true
2.
// +groupName=nicktming.example.com
表示group
的名字是nicktming.example.com
, 后面在注册schema
的时候会用到.
Local Tags
3.
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
表示该Type
需要实现k8s.io/apimachinery/pkg/runtime.Object
这个借口的两个方法GetObjectKind
和DeepCopyObject
. (因为在调用clientset
的时候会有decode
, 这个时候会用到)
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
Client-gen Tags
4.
// +genclient
表示为该Type
创建client
. (clientset
会为Type
创建client
)
// +genclient:noStatus
this type is not using spec-status separation via the /status subresource.
The resulting client will not have the UpdateStatus method
(client-gen would generate that blindly otherwise as soon as
it finds a Status field in your struct)
register.go
package v1
import (
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: "nicktming.example.com", Version: "v1"}
// 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,
&Database{},
&DatabaseList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
该文件主要为了向
schema
注册apiGroupVersion
用的, 在生成客户端中decode
阶段会用到.
说白了就是在解析的时候GroupVersion=nicktming.example.com/v1
并且Kind=Database
的时候可以知道将结果解析成Database
结构, 所以需要提前注册信息.
2.2 运行
执行参数如下:
./generate-groups.sh all github.com/nicktming/k8s-crd-controller/pkg/client github.com/nicktming/k8s-crd-controller/pkg/apis example.com:v1
all: 代表deepcopy-gen
,client-gen
, 和lister-gen
和informer-gen
都要执行. 如果只想执行deepcopy-gen
, 把all
换成deepcopy-gen
即可.
[root@master k8s-crd-controller]# pwd
/root/go/src/github.com/nicktming/k8s-crd-controller
[root@master k8s-crd-controller]# tree
.
├── pkg
│ └── apis
│ └── example.com
│ └── v1
│ ├── doc.go
│ └── types.go
└── README.md
[root@master code-generator]# pwd
/root/go/src/k8s.io/kubernetes/staging/src/k8s.io/code-generator
[root@master code-generator]# ./generate-groups.sh all github.com/nicktming/k8s-crd-controller/pkg/client github.com/nicktming/k8s-crd-controller/pkg/apis example.com:v1
Generating deepcopy funcs
Generating clientset for example.com:v1 at github.com/nicktming/k8s-crd-controller/pkg/client/clientset
Generating listers for example.com:v1 at github.com/nicktming/k8s-crd-controller/pkg/client/listers
Generating informers for example.com:v1 at github.com/nicktming/k8s-crd-controller/pkg/client/informers
[root@master k8s-crd-controller]# pwd
/root/go/src/github.com/nicktming/k8s-crd-controller
[root@master k8s-crd-controller]# tree
.
├── pkg
│ ├── apis
│ │ └── example.com
│ │ └── v1
│ │ ├── doc.go
│ │ ├── register.go
│ │ ├── types.go
│ │ └── zz_generated.deepcopy.go
│ └── client
│ ├── clientset
│ │ └── versioned
│ │ ├── clientset.go
│ │ ├── doc.go
│ │ ├── fake
│ │ │ ├── clientset_generated.go
│ │ │ ├── doc.go
│ │ │ └── register.go
│ │ ├── scheme
│ │ │ ├── doc.go
│ │ │ └── register.go
│ │ └── typed
│ │ └── example.com
│ │ └── v1
│ │ ├── database.go
│ │ ├── doc.go
│ │ ├── example.com_client.go
│ │ ├── fake
│ │ │ ├── doc.go
│ │ │ ├── fake_database.go
│ │ │ └── fake_example.com_client.go
│ │ └── generated_expansion.go
│ ├── informers
│ │ └── externalversions
│ │ ├── example.com
│ │ │ ├── interface.go
│ │ │ └── v1
│ │ │ ├── database.go
│ │ │ └── interface.go
│ │ ├── factory.go
│ │ ├── generic.go
│ │ └── internalinterfaces
│ │ └── factory_interfaces.go
│ └── listers
│ └── example.com
│ └── v1
│ ├── database.go
│ └── expansion_generated.go
└── README.md
21 directories, 27 files
deepcopy-gen: 生成了
github.com/nicktming/k8s-crd-controller/pkg/apis/example.com/v1/zz_generated.deepcopy.go
文件.
client-gen: 生成了nicktming/k8s-crd-controller/pkg/client/clientset
文件夹及其下属所有文件. (主要与api-server
打交道代码)
lister-gen: 生成了nicktming/k8s-crd-controller/pkg/client/listers
文件夹及其下属所有文件. (informer
体系相关代码)
informers: 生成了nicktming/k8s-crd-controller/pkg/client/informers
文件夹及其下属所有文件.(从本地缓存中list
该元素)
client
文件下生成的这些文件在 [k8s源码分析][client-go] informer之SharedInformerFactory 和 [k8s源码分析][client-go] client之clientset 中已经分析过了.
关于
zz_generated.deepcopy.go
文件中的内容主要是生成对象拷贝方法.
3. 创建crd
crd.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: databases.nicktming.example.com
spec:
group: nicktming.example.com
version: v1
names:
kind: Database
plural: databases
scope: Namespaced
my-database.yaml
apiVersion: nicktming.example.com/v1
kind: Database
metadata:
name: my-database
spec:
username: "nicktming"
password: "123456"
encoding: "json"
运行结果如下:
[root@master kubectl]# ./kubectl apply -f crd/crd.yaml
customresourcedefinition.apiextensions.k8s.io/databases.nicktming.example.com created
[root@master kubectl]# ./kubectl get crd
NAME CREATED AT
databases.nicktming.example.com 2019-10-29T13:30:04Z
[root@master kubectl]# ./kubectl apply -f crd/my-database.yaml
database.nicktming.example.com/my-database created
[root@master kubectl]# ./kubectl get database
NAME AGE
my-database 58s
[root@master kubectl]# ./kubectl describe database my-database
Name: my-database
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"nicktming.example.com/v1","kind":"Database","metadata":{"annotations":{},"name":"my-database","namespace":"default"},"spec"...
API Version: nicktming.example.com/v1
Kind: Database
Metadata:
Creation Timestamp: 2019-10-29T13:30:51Z
Generation: 1
Resource Version: 100743
Self Link: /apis/nicktming.example.com/v1/namespaces/default/databases/my-database
UID: 53787ca0-fa50-11e9-8739-525400d54f7e
Spec:
Encoding: json
Password: 123456
Username: nicktming
Events: <none>
通过
selflink
访问curl http://localhost:8080/apis/nicktming.example.com/v1/namespaces/default/databases/my-database
browser.png
可以看到看到可以通过
api-server
访问所创建的资源. 那么接下来看一下如何使用代码进行访问.
3.1 代码访问
前面生成的代码就是为了用代码可以访问, 之所有可以用代码访问
pod
,service
这些kubernetes
的资源, 这是因为client-go
已经实现了这部分代码, 然而对于自定义的资源,kubernetes
无法提前写好, 但是由于基本结构差不多, 所以用代码直接生成了.
package main
import (
"fmt"
nicktmingv1 "github.com/nicktming/k8s-crd-controller/pkg/apis/example.com/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/rest"
nicktmingclientset "github.com/nicktming/k8s-crd-controller/pkg/client/clientset/versioned"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
nicktminginformers "github.com/nicktming/k8s-crd-controller/pkg/client/informers/externalversions"
"k8s.io/client-go/tools/cache"
)
func main() {
config := &rest.Config{
Host: "http://172.21.0.16:8080",
}
// 生成client
databaseClient, _ := nicktmingclientset.NewForConfig(config)
// 从api-server中获取
myDatabase, _ := databaseClient.NicktmingV1().Databases("default").Get("my-database", metav1.GetOptions{})
fmt.Printf("===>Database Name:%v(%v,%v,%v)\n", myDatabase.Name, myDatabase.Spec.User, myDatabase.Spec.Password, myDatabase.Spec.Encoding)
factory := nicktminginformers.NewSharedInformerFactory(databaseClient, 10)
// 添加event handler
databaseInformer := factory.Nicktming().V1().Databases()
databaseInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {fmt.Printf("add: %v\n", obj.(*nicktmingv1.Database).Name)},
UpdateFunc: func(oldObj, newObj interface{}) {fmt.Printf("update: %v\n", newObj.(*nicktmingv1.Database).Name)},
DeleteFunc: func(obj interface{}){fmt.Printf("delete: %v\n", obj.(*nicktmingv1.Database).Name)},
})
// 启动
stopCh := make(chan struct{})
factory.Start(stopCh)
factory.WaitForCacheSync(stopCh)
// 从本地缓存中获取元素
databaseLister := databaseInformer.Lister()
allDatabases, _ := databaseLister.List(labels.Everything())
for _, p := range allDatabases {
fmt.Printf("list database: %v\n", p.Name)
}
<- stopCh
}
如果报错的话把
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
改成config.NegotiatedSerializer = scheme.Codecs
, 因为版本问题,apimachinery
中的版本不存在DirectCodecFactory
.
网友评论