初始化go项目

1
2
3
4
5
6
7
8
9
10
# 创建并进入项目目录
mkdir code-generator-demo && cd code-generator-demo

# 初始化项目
go mod init code-generator-demo

# 获取依赖
go get k8s.io/apimachinery@v0.0.0-20190425132440-17f84483f500
go get k8s.io/client-go@v0.0.0-20190425172711-65184652c889
go get k8s.io/code-generator@v0.0.0-20190419212335-ff26e7842f9d

编写doc.go

1
2
3
4
5
// +k8s:deepcopy-gen=package
// +groupName=appcontroller.k8s.io

// Package v1alpha1 v1alpha1版本的api包
package v1alpha1

编写types.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
package v1alpha1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type Application struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ApplicationSpec `json:"spec"`
Status ApplicationStatus `json:"status"`
}

type ApplicationSpec struct {
DeploymentName string `json:"deploymentName"`
Replicas *int32 `json:"replicas"`
}

type ApplicationStatus struct {
AvailableReplicas int32 `json:"availableReplicas"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

type ApplicationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`

Items []Application `json:"items"`
}

编写register.go

  • 注意,这是appcontroller目录下的register.go,并非某个版本目录下的,版本目录下的,再使用 code-generator 脚本后,再进行编写
    1
    2
    3
    4
    5
    package appcontroller

    const (
    GroupName = "appcontroller.k8s.io"
    )

编写boilerplate.go.txt

  • 该文件是文件开头统一的注释,会在使用 code-generator 脚本时,指定 boilerplate.go.txt 文件的所在目录
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /*
    Copyright 2019 The Kubernetes Authors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

    @Time : 2024/1
    @Author : grahamzhu
    @Software: GoLand
    */

编写tools.go

  • 我们要使用 code-generator,可代码中还没有任何位置 导入过 code-generator 的包,所以我们需要一个类,专门用于将 code-generator 的包导入。一般使用tools.go来做这件事
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //go:build tools
    // +build tools

    /*
    Copyright 2019 The Kubernetes Authors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    */

    // This package imports things required by build scripts, to force `go mod` to see them as dependencies
    package tools

    import _ "k8s.io/code-generator"

编写使用code-generator的脚本update-codegen.sh

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

# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# 设置脚本在执行过程中遇到任何错误时立即退出
set -o errexit
# 设置脚本在使用未定义的变量时立即退出
set -o nounset
# 设置脚本在管道命令中任意一条命令失败时立即退出
set -o pipefail

# 对generate-groups.sh 脚本的调用
../vendor/k8s.io/code-generator/generate-groups.sh \
"deepcopy,client,informer,lister" \
code-generator-demo/pkg/generated \
code-generator-demo/pkg/apis \
appcontroller:v1alpha1 \
--go-header-file $(pwd)/boilerplate.go.txt \
--output-base $(pwd)/../../

解释每个参数和选项的含义:

  • “deepcopy,client,informer,lister”:指定要生成的代码类型,这里包括深拷贝方法、客户端、informer 和 lister。
  • code-generator-demo/pkg/generated:指定生成的代码的输出目录。
  • code-generator-demo/pkg/apis:指定包含 API 定义的目录。
  • appcontroller:v1alpha1:指定要生成的 API 版本和组。
  • –go-header-file $(pwd)/boilerplate.go.txt:指定用于生成的代码文件头部的文本文件,这里使用了当前目录下的 boilerplate.go.txt 文件。
  • –output-base $(pwd)/../../:指定生成的代码的基础目录,即输出目录的上一级目录。

总结起来,该命令的作用是调用 Kubernetes 代码生成器脚本,根据指定的 API 定义和选项生成深拷贝方法、客户端、informer 和 lister 等代码,并将生成的代码输出到指定的目录中。

执行脚本生成代码

  • 将代码上传至linux环境下,依次执行如下命令:
    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
    50
    51
    52
    53
    54
    55
    go mod tidy

    # 生成vendor文件夹
    go mod vendor

    # 为vendor中的code-generator赋予权限
    chmod -R 777 vendor

    # 为hack中的update-codegen.sh脚本赋予权限
    chmod -R 777 hack

    # 调用脚本生成代码
    $ cd hack && ./update-codegen.sh
    Generating deepcopy funcs
    Generating clientset for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/clientset
    Generating listers for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/listers
    Generating informers for appcontroller:v1alpha1 at code-generator-demo/pkg/generated/informers

    #此时目录变为如下情况
    $ cd ../ && tree -L 5
    .
    ├── go.mod
    ├── go.sum
    ├── hack
    │ ├── boilerplate.go.txt
    │ ├── tools.go
    │ └── update-codegen.sh
    ├── pkg
    │ ├── apis
    │ │ └── appcontroller
    │ │ ├── register.go
    │ │ └── v1alpha1
    │ │ ├── doc.go
    │ │ ├── types.go
    │ │ └── zz_generated.deepcopy.go
    │ └── generated
    │ ├── clientset
    │ │ └── versioned
    │ │ ├── clientset.go
    │ │ ├── doc.go
    │ │ ├── fake
    │ │ ├── scheme
    │ │ └── typed
    │ ├── informers
    │ │ └── externalversions
    │ │ ├── appcontroller
    │ │ ├── factory.go
    │ │ ├── generic.go
    │ │ └── internalinterfaces
    │ └── listers
    │ └── appcontroller
    │ └── v1alpha1
    └── vendor
    ├── github.com

手动注册版本v1alpha1的CRD资源

  • 在生成了客户端代码后,我们还需要手动注册版本v1alpha1的CRD资源,才能真正使用这个client,不然在编译时会出现 undefined: v1alpha1.AddToScheme 错误、undefined: v1alpha1.Resource 错误。
  • v1alpha1.AddToScheme、v1alpha1.Resource 这两个是用于 client 注册的
  • 编写 v1alpha1/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
    34
    35
    36
    37
    38
    package v1alpha1

    import (
    "code-generator-demo/pkg/apis/appcontroller"
    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: appcontroller.GroupName, Version: "v1alpha1"}

    // 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,
    &Application{},
    &ApplicationList{},
    )
    metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
    return nil
    }

测试CRD资源的使用

  • 在项目根目录下,创建一个cmd目录,里面创建一个main.go文件
  • 下面我们演示如何使用 code-generator 为 Application 的 v1alpha1 生成的客户端 AppcontrollerV1alpha1Client
  • 编写main.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
    package main

    import (
    "code-generator-demo/pkg/generated/clientset/versioned/typed/appcontroller/v1alpha1"
    "fmt"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/tools/clientcmd"
    )

    func main() {
    config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
    if err != nil {
    panic(err.Error())
    }

    // 在 pkg/generated/clientset/versioned/typed/appcontroller/v1alpha1/appcontroller_client.go 中
    // 自动生成的 AppcontrollerV1alpha1Client,用于操作这种GVR
    appClient, err := v1alpha1.NewForConfig(config)
    if err != nil {
    panic(err.Error())
    }

    // 获取default命名空间下的所有Application
    appList, err := appClient.Applications("default").List(metav1.ListOptions{})
    if err != nil {
    panic(err.Error())
    }

    for _, app := range appList.Items {
    fmt.Println(app.Name)
    }
    }