中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

go語言生成器code?generator怎么使用

發布時間:2022-05-16 09:20:33 來源:億速云 閱讀:305 作者:iii 欄目:開發技術

這篇文章主要介紹“go語言生成器code generator怎么使用”,在日常操作中,相信很多人在go語言生成器code generator怎么使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”go語言生成器code generator怎么使用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    代碼生成器介紹

    client-go為每種k8s內置資源提供了對應的clientsetinformer。那么我們要監聽和操作自定義資源對象,應該如何做呢?

    方式一:使用client-go提供的dynamicClient來操作自定義資源對象,當然由于dynamicClient是基于RESTClient實現的,所以我們可以使用RESTClient來達到同樣的目的。

    方式二: 使用conde-generator來幫我們生成我們需要的代碼,這樣我們就可以像使用client-go為k8s內置資源對象提供的方式監聽和操作自定義資源了。

    code-generator

    code-generator 就是 Kubernetes 提供的一個用于代碼生成的項目,它提供了以下工具為 Kubernetes 中的資源生成代碼:

    • deepcopy-gen: 生成深度拷貝方法,為每個 T 類型生成 func (t* T) DeepCopy() *T 方法,API 類型都需要實現深拷貝

    • client-gen: 為資源生成標準的 clientset

    • informer-gen: 生成 informer,提供事件機制來響應資源的事件

    • lister-gen: 生成 Lister,為 get 和 list 請求提供只讀緩存層(通過 indexer 獲取)

    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目錄下,就會看到我們上面介紹的工具:

    go語言生成器code?generator怎么使用

    接著我們對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:

    go語言生成器code?generator怎么使用

    當然通常情況下我們不會去單獨的使用某一個工具。

    接下來我們來創建我們的項目,此處我們可以仿照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來標記一些供生成器使用的屬性。這些tag主要分為兩類:

    • doc.go的package語句智商提供的全局tag

    • 在需要被處理的類型上提供局部tag

    tag的使用方法如下所示:

    // +tag-name
    // 或者
    // +tag-name=value

    我們可以看到 tag 是通過注釋的形式存在的,另外需要注意的是 tag 的位置非常重要,很多 tag 必須直接位于 type 或 package 語句的上一行,另外一些則必須和 go 語句隔開至少一行空白。

    全局tag

    必須在目標包的doc.go文件中聲明,一般路徑為pkg/apis/<apigroup>/<version>/doc.go,如下所示:

    // 為包中任何類型生成深拷貝方法,可以在局部 tag 覆蓋此默認行為
    // +k8s:deepcopy-gen=package
     
    // groupName 指定 API 組的全限定名
    // 此 API 組的 v1 版本,放在同一個包中
    // +groupName=crd.example.com
    package v1

    注意:空行不能省略

    局部tag

    局部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怎么使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    go
    AI

    浏阳市| 化州市| 离岛区| 赣榆县| 开原市| 清苑县| 西平县| 东丽区| 南岸区| 错那县| 鄂伦春自治旗| 沙河市| 日照市| 瓮安县| 石渠县| 达拉特旗| 景洪市| 承德市| 扎赉特旗| 达孜县| 长白| 宣化县| 安康市| 旬邑县| 临高县| 昔阳县| 精河县| 潮安县| 凉城县| 科技| 广州市| 偏关县| 内江市| 全南县| 明星| 清新县| 杨浦区| 重庆市| 阿拉善盟| 济宁市| 翼城县|