背景
kubebuilder生成的代码没有clientset、informer、listers。因此无法像client-go一样可以通过代码来调用我们自己开发自定义资源。
KubeBuilderVersion: 4.5.0
code-generator
安装
创建一个go mod,然后安装code-generator到机器上
go mod init test
go get k8s.io/code-generator@v0.32.0
进入code-generator的go mod路径,然后安装
cd /root/go/pkg/mod/k8s.io/code-generator@v0.32.0
go install ./cmd/{client-gen,deepcopy-gen,informer-gen,lister-gen}
配置kubebuilder使用code-generator
进入开发好的kubebuilder项目,将code-generator拷贝的kubebuilder项目根目录下。
cd /data/codeForge
cp -r /root/go/pkg/mod/k8s.io/code-generator@v0.32.0 code-generator
可以删除code-generator中的代码,除了保留kube_codegen.sh
在kubebuilder项目中的hack目录下创建脚本,update-generator.sh
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG="/data/codeForge/code-generator"
source "${CODEGEN_PKG}/kube_codegen.sh"
THIS_PKG="github.com/hjjzs/devfroge-user" # 就是go mod init <name> 添加写的name
# Generate deepcopy, defaults, and conversion functions
kube::codegen::gen_helpers \
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
"${SCRIPT_ROOT}/pkg/apis"
# Generate clientset, informers, and listers
kube::codegen::gen_client \
--with-watch \
--output-dir "${SCRIPT_ROOT}/pkg/generated" \
--output-pkg "${THIS_PKG}/pkg/generated" \
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
"${SCRIPT_ROOT}/pkg/apis"
配置apis目录
新版本kubebuilder的api创建在项目的根目录下名为api
。具体路径为api//。我们需要对其改造。
mkdir -p pkg/apis/<group>/<version>/ # kubebuilder项目的group、version
cp -r api/<version>/* pkg/apis/<group>/<version>
参考,下图,我开发的kubebuilder项目的group为user,version为v1alpha1。api目录改造如红框所示。
接下来所有对api types文件的操作都在pkg/apis// 下面进行
添加client-gen 注解
对所以api type进行注解,如我创建的自定义资源为user,scope为cluster。
- crd为cluster访问而非namesapce的需要添加注解
+genclient:nonNamespaced
- 需要为自定义资源类型 和 list类型添加注解,如果图片中的 user struct 和 userlist struct 添加注解‘
- 需要为所有
<crd_name>_types.go
的api添加注解。
// +genclient
// +genclient:nonNamespaced # 如何资源为namespace范围的则不要添加该注解
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
添加doc.go文件
/*
Copyright 2025.
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.
*/
// Package user contains the user API group.
// +k8s:deepcopy-gen=package
// +groupName=user.devfroge.io # 这里的group需要添加domain
package v1alpha1
添加Kind和Resource函数
在pkg/apis/<group>/<version>
路径下添加register.go文件。
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = GroupVersion
// 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()
}
添加完成后效果如下:
必须包含以下文件:
- doc.go
- groupversion_info.go
- <crd_name>_types.go # 如图我的kubebuilder项目又5个crd资源因此又5个xxx_types.go文件。
- zz_generated.deepcopy.go
- register.go
生成clientset等代码
在项目根目录运行hack中的update-generator.sh脚本开始生成clientset等代码。
bash hack/update-generator.sh
最终效果
编程测试示例
package main
import (
"context"
"fmt"
"log"
"time"
userv1alpha1 "github.com/hjjzs/devfroge-user/pkg/apis/user/v1alpha1"
clientset "github.com/hjjzs/devfroge-user/pkg/generated/clientset/versioned"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
// 加载 kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
log.Fatalf("Error building kubeconfig: %v", err)
}
// 创建 clientset
client, err := clientset.NewForConfig(config)
if err != nil {
log.Fatalf("Error creating clientset: %v", err)
}
// 创建一个 User 资源
user := &userv1alpha1.User{
ObjectMeta: metav1.ObjectMeta{
Name: "example-user",
},
Spec: userv1alpha1.UserSpec{
Type: userv1alpha1.RegularUser,
DisplayName: "lzc",
Email: "example@example.com",
Description: "This is an example user",
Enabled: true,
Password: "password123",
},
}
// 创建 User
createdUser, err := client.UserV1alpha1().Users().Create(context.TODO(), user, metav1.CreateOptions{})
if err != nil {
log.Fatalf("Error creating user: %v", err)
}
fmt.Printf("Created user: %s\n", createdUser.Name)
time.Sleep(5 * time.Second)
// 获取 User
getUser, err := client.UserV1alpha1().Users().Get(context.TODO(), "example-user", metav1.GetOptions{})
if err != nil {
log.Fatalf("Error getting user: %v", err)
}
fmt.Printf("Got user: %s, Email: %s\n", getUser.Name, getUser.Spec.Email)
// 列出所有 Users
userList, err := client.UserV1alpha1().Users().List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatalf("Error listing users: %v", err)
}
fmt.Printf("Found %d users\n", len(userList.Items))
// 更新 User
getUser.Spec.Description = "Updated description"
updatedUser, err := client.UserV1alpha1().Users().Update(context.TODO(), getUser, metav1.UpdateOptions{})
if err != nil {
log.Fatalf("Error updating user: %v", err)
}
fmt.Printf("Updated user description: %s\n", updatedUser.Spec.Description)
// 删除 User
err = client.UserV1alpha1().Users().Delete(context.TODO(), "example-user", metav1.DeleteOptions{})
if err != nil {
log.Fatalf("Error deleting user: %v", err)
}
fmt.Println("User deleted successfully")
}
运行结果:
图片显示找到3个用户时因为我自己手动创建了2个用户,加上测试代码创建的一共3个。