<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
client-go
为每种k8s内建资源提供了对应的clientset
和informer
。那么我们要监听和操作自定义资源物件,应该如何做呢?
方式一:使用client-go
提供的dynamicClient
来操作自定义资源物件,当然由于dynamicClient
是基于RESTClient
实现的,所以我们可以使用RESTClient
来达到同样的目的。
方式二: 使用conde-generator
来帮我们生成我们需要的程式码,这样我们就可以像使用client-go
为k8s内建资源物件提供的方式监听和操作自定义资源了。
code-generator 就是 Kubernetes 提供的一个用于程式码生成的专案,它提供了以下工具为 Kubernetes 中的资源生成程式码:
Informer 和 Lister 是构建控制器的基础,使用这4个程式码生成器可以建立全功能的、和 Kubernetes 上游控制器工作机制相同的 production-ready 的控制器。
code-generator 还包含一些其它的程式码生成器,例如 Conversion-gen 负责产生内外部型别的转换函数、Defaulter-gen 负责处理栏位预设值。
大部分的生成器支援--input-dirs引数来读取一系列输入包,处理其中的每个型别,然后生成程式码:
1、部分程式码生成到输入包所在目录,例如 deepcopy-gen 生成器,也可以使用引数--output-file-base "zz_generated.deepcopy" 来定义输出档名
2、其它程式码生成到 --output-package 指定的目录,例如 client-gen、informer-gen、lister-gen 等生成器
接来下我们使用code-generator进行实战演示:
首先我们将专案拉到本地:
$ git clone https://github.com/kubernetes/code-generator.git $ git checkout 0.23.3
然后我们进入到cmd目录下,就会看到我们上面介绍的工具:
接着我们对client-gen
,deepcopy-gen
,infromer-gen
,lister-gen
进行安装,会安装到GOPATH的bin目录下:
# 进行安装 $ go install ./cmd/{client-gen,deepcopy-gen,informer-gen,lister-gen} # 获取GOPATH路径 $ go env | grep GOPATH GOPATH="/Users/Christian/go" # 检视 ls /Users/Christian/go/bin client-gen deepcopy-gen goimports lister-gen controller-gen defaulter-gen informer-gen type-scaffold
发现我们已经成功的安装了,这时候我们就可以直接使用这些工具了,比如我们可以使用--help
命令来检视如何使用client-gen
:
当然通常情况下我们不会去单独的使用某一个工具。
接下来我们来建立我们的专案,此处我们可以仿照sample controller专案进行编写:
$ mkdir operator-test && cd operator-test $ go mod init operator-test $ mkdir -p pkg/apis/example.com/v1 ➜ operator-test tree . ├── go.mod ├── go.sum └── pkg └── apis └── example.com └── v1 ├── doc.go ├── register.go └── types.go 4 directories, 5 files
接下来我们对v1下面的三个go档案进行填充(可以直接复制sample-controller,对其进行做简单修改):
doc.go主要是用来宣告要使用deepconpy-gen
以及groupName。
// pkg/crd.example.com/v1/doc.go // +k8s:deepcopy-gen=package // +groupName=example.com package v1
types.go主要是定义crd资源对应的go中的结构。
// pkg/crd.example.com/v1/types.go package v1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Bar is a specification for a Bar resource type Bar struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec BarSpec `json:"spec"` // Status BarStatus `json:"status"` } // BarSpec is the spec for a Bar resource type BarSpec struct { DeploymentName string `json:"deploymentName"` Image string `json:"image"` Replicas *int32 `json:"replicas"` } // BarStatus is the status for a Bar resource type BarStatus struct { AvailableReplicas int32 `json:"availableReplicas"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // BarList is a list of Bar resources type BarList struct { metav1.TypeMeta `json:",inline" :"metav1.TypeMeta"` metav1.ListMeta `json:"metadata" :"metav1.ListMeta"` Items []Bar `json:"items" :"items"` }
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: "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, &Bar{}, &BarList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil }
这时候会发现&Bar{},&BarLis{}会报错,这是因为我们还没有为其实现deepcopy
方法。
由于在自动生成程式码的时候,需要指定header的资讯,所以我们为了方便,可以将code-generator
专案下的hack
包直接拷贝到我们当前专案根目录下。
接下来我们使用code-generator
来为我们自动生成程式码:
# 执行 code-generator/generate-group.sh ./../../github/code-generator/generate-groups.sh all # 指定 group 和 version,生成deeplycopy以及client operator-test/pkg/client operator-test/pkg/apis crd.example.com:v1 # 指定标头档案 --go-header-file=./hack/boilerplate.go.txt # 指定输出位置,预设为GOPATH --output-base ../ Generating deepcopy funcs Generating clientset for crd.example.com:v1 at operator-test/pkg/client/clientset Generating listers for crd.example.com:v1 at operator-test/pkg/client/listers Generating informers for crd.example.com:v1 at operator-test/pkg/client/informers
这时候我们再来检视专案结构:
➜ operator-test tree . ├── go.mod ├── go.sum ├── hack │ └── boilerplate.go.txt └── pkg ├── apis │ └── crd.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 │ └── crd.example.com │ └── v1 │ ├── bar.go │ ├── crd.example.com_client.go │ ├── doc.go │ ├── fake │ │ ├── doc.go │ │ ├── fake_bar.go │ │ └── fake_crd.example.com_client.go │ └── generated_expansion.go ├── informers │ └── externalversions │ ├── crd.example.com │ │ ├── interface.go │ │ └── v1 │ │ ├── bar.go │ │ └── interface.go │ ├── factory.go │ ├── generic.go │ └── internalinterfaces │ └── factory_interfaces.go └── listers └── crd.example.com └── v1 ├── bar.go └── expansion_generated.go 22 directories, 29 files
这时候我们就可以像操作内建资源一样,操作我们的自定义资源了。
我们先准备crd以及对应的cr,这边也是可以直接从sample-controller
专案进行拷贝,做简单的修改即可。
# manifests/example.com_bars.yaml --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: (devel) creationTimestamp: null name: bars.crd.example.com spec: group: crd.example.com names: kind: Bar listKind: BarList plural: bars singular: bar scope: Namespaced versions: - name: v1 schema: openAPIV3Schema: description: Bar is a specification for a Bar resource properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the generated submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: BarSpec is the spec for a Bar resource properties: deploymentName: type: string image: type: string replicas: format: int32 type: integer required: - deploymentName - image - replicas type: object required: - spec type: object served: true storage: true # manifests/cr.yaml --- apiVersion: crd.example.com/v1 kind: Bar metadata: name: bar-demo namespace: default spec: image: "nginx:1.17.1" deploymentName: example-bar replicas: 2
接下来我们来编写main函数,这时候我们就可以使用client-go像操作我们内建资源一样,操作crd资源了。
package main import ( "context" "fmt" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" "log" clientSet "operator-test/pkg/client/clientset/versioned" "operator-test/pkg/client/informers/externalversions" ) func main() { config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile) if err != nil { log.Fatalln(err) } clientset, err := clientSet.NewForConfig(config) if err != nil { log.Fatalln(err) } list, err := clientset.CrdV1().Bars("default").List(context.TODO(), v1.ListOptions{}) if err != nil { log.Fatalln(err) } for _, bar := range list.Items { fmt.Println(bar.Name) } factory := externalversions.NewSharedInformerFactory(clientset, 0) factory.Crd().V1().Bars().Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: nil, UpdateFunc: nil, DeleteFunc: nil, }) // todo } // ==== // 程式输出结果: bar-demo
在我们上面的范例中,我们在原始码中新增了很多tag
,我们使用这些tag
来标记一些供生成器使用的属性。这些tag
主要分为两类:
doc.go
的package语句智商提供的全域性tagtag的使用方法如下所示:
// +tag-name // 或者 // +tag-name=value
我们可以看到 tag 是通过注释的形式存在的,另外需要注意的是 tag 的位置非常重要,很多 tag 必须直接位于 type 或 package 语句的上一行,另外一些则必须和 go 语句隔开至少一行空白。
必须在目标包的doc.go
档案中宣告,一般路径为pkg/apis/<apigroup>/<version>/doc.go,如下所示:
// 为包中任何型别生成深拷贝方法,可以在区域性 tag 覆盖此预设行为 // +k8s:deepcopy-gen=package // groupName 指定 API 组的全限定名 // 此 API 组的 v1 版本,放在同一个包中 // +groupName=crd.example.com package v1
注意:空行不能省略
区域性tag要么直接宣告在型别之前,要么位于型别之前的第二个注释块中。下面的 types.go 中宣告了 CR 对应的型别:
// 为当前型别生成用户端,如果不加此注解则无法生成 lister、informer 等包 // +genclient // 提示此型别不基于 /status 子资源来实现 spec-status 分离,产生的用户端不具有 UpdateStatus 方法 // 否则,只要型别具有 Status 栏位,就会生成 UpdateStatus 方法 // +genclient:noStatus // 为每个顶级 API 型别新增,自动生成 DeepCopy 相关程式码 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // K8S 资源,资料库 type Database struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec DatabaseSpec `json:"spec"` } // 不为此型别生成深拷贝方法 // +k8s:deepcopy-gen=false // 资料库的规范 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 // 资料库列表,因为 list 获取的是列表,所以需要定义该结构 type DatabaseList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` Items []Database `json:"items"` }
在上面 CR 的定义上面就通过 tag 来新增了自动生成相关程式码的一些注释。此外对于丛集级别的资源,我们还需要提供如下所示的注释:
// +genclient:nonNamespaced // 下面的 Tag 不能少 // +genclient
另外我们还可以控制用户端提供哪些 HTTP 方法:
// +genclient:noVerbs // +genclient:onlyVerbs=create,delete // +genclient:skipVerbs=get,list,create,update,patch,delete,deleteCollection,watch // 仅仅返回 Status 而非整个资源 // +genclient:method=Create,verb=create,result=k8s.io/apimachinery/pkg/apis/meta/v1.Status // 下面的 Tag 不能少 // +genclient
使用 tag 定义完需要生成的程式码规则后,执行上面提供的程式码生成指令码即可自动生成对应的程式码了。
除了上面介绍的程式码生成方式,我们还可以直接使用sample-controller
专案提供的hack/update-condegen.sh指令码。
#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail 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-groups.sh <generators> <output-package> <apis-package> <groups-versions> # 使用哪些生成器,可选值 deepcopy,defaulter,client,lister,informer,逗号分隔,all表示全部使用 # 输出包的汇入路径 # CR 定义所在路径 # API 组和版本 bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" k8s.io/sample-controller/pkg/generated k8s.io/sample-controller/pkg/apis samplecontroller:v1alpha1 --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt # 自动生成的原始码头部附加的内容: # --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt
执行上面的指令码后,所有 API 程式码会生成在 pkg/apis
目录下,clientsets、informers、listers 则生成在 pkg/generated
目录下。不过从指令码可以看出需要将 code-generator 的包放置到 vendor 目录下面,现在我们都是使用 go modules
来管理依赖保,我们可以通过执行 go mod vendor
命令将依赖包放置到 vendor 目录下面来。
我们还可以进一步提供 hack/verify-codegen.sh 指令码,用于判断生成的程式码是否 up-to-date:
#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail # 先呼叫 update-codegen.sh 生成一份新程式码 # 然后对比新老程式码是否一样 SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. DIFFROOT="${SCRIPT_ROOT}/pkg" TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" _tmp="${SCRIPT_ROOT}/_tmp" cleanup() { rm -rf "${_tmp}" } trap "cleanup" EXIT SIGINT cleanup mkdir -p "${TMP_DIFFROOT}" cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" "${SCRIPT_ROOT}/hack/update-codegen.sh" echo "diffing ${DIFFROOT} against freshly generated codegen" ret=0 diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" if [[ $ret -eq 0 ]] then echo "${DIFFROOT} up to date." else echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" exit 1 fi
以上就是go语言程式码生成器code generator简单介绍的详细内容,更多关于go语言code generator的资料请关注it145.com其它相关文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45