使用 client-go 操作 kubernetes 的 crd 资源
2021-03-12 tech go kubernetes 17 mins 2 图 6010 字
最近在开发 k8s crd 相关的操作,这里简单的记录一下通过 client-go 增删改查 k8s crd 资源的方法。
示例CRD
这里我以第三方的crd: kyverno 作为例子。
kyverno 的 crd 结构体定义在这里: pkg/api/kyverno/v1,主要是一下两个文件(为了引入尽可能少的文件,编译报错时可以把某些类型改成interface)
重点的 cluster policy 和 List,以及内部的 Rule 的结构体如下:
type ClusterPolicy struct {
metav1.TypeMeta `json:",inline,omitempty" yaml:",inline,omitempty"`
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec Spec `json:"spec" yaml:"spec"`
Status PolicyStatus `json:"status,omitempty" yaml:"status,omitempty"`
}
type ClusterPolicyList struct {
metav1.TypeMeta `json:",inline" yaml:",inline"`
metav1.ListMeta `json:"metadata" yaml:"metadata"`
Items []ClusterPolicy `json:"items" yaml:"items"`
}
type Rule struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Context []ContextEntry `json:"context,omitempty" yaml:"context,omitempty"`
MatchResources MatchResources `json:"match,omitempty" yaml:"match,omitempty"`
ExcludeResources ExcludeResources `json:"exclude,omitempty" yaml:"exclude,omitempty"`
AnyAllConditions apiextensions.JSON `json:"preconditions,omitempty" yaml:"preconditions,omitempty"`
Mutation Mutation `json:"mutate,omitempty" yaml:"mutate,omitempty"`
Validation Validation `json:"validate,omitempty" yaml:"validate,omitempty"`
Generation Generation `json:"generate,omitempty" yaml:"generate,omitempty"`
}
部署 kyverno 的 crd:
kubectl create -f https://raw.githubusercontent.com/kyverno/kyverno/main/definitions/release/install.yaml
查看 k8s 上的api资源有哪些:
kubectl api-resources
List
相当于 kubectl get xxx
命令,我们通过 k8s.io/client-go/dynamic
里的 Interface
提供的方法来操作 crd 资源。
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
// getGVR :- gets GroupVersionResource for dynamic client
func getGVR(group, version, resource string) schema.GroupVersionResource {
return schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
}
func listKyverno(client dynamic.Interface, ns string) (*ClusterPolicyList, error) {
gvr := getGVR("kyverno.io", "v1", "clusterpolicies")
if (ns == ''){
list, err := dynamicClient.Resource(gvr).List(metav1.ListOptions{})
} else {
list, err := dynamicClient.Resource(gvr).Namespace(namespace).List(metav1.ListOptions{})
}
if err != nil {
return nil, err
}
data, err := list.MarshalJSON()
if err != nil {
return nil, err
}
var list ClusterPolicyList
if err := json.Unmarshal(data, &list); err != nil {
return nil, err
}
return &list, nil
}
func main() {
kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config")
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
panic(err)
}
client, err := dynamic.NewForConfig(config)
if err != nil {
panic(err)
}
list, err := listKyverno(client, "")
if err != nil {
panic(err)
}
for _, t := range list.Items {
fmt.Printf("%s %s %s %s\n", t.Namespace, t.Name, t.Spec.CronSpec, t.Spec.Image)
}
}
Get
func getKyverno (client dynamic.Interface, namespace string, name string) (*ClusterPolicy, error) {
gvr := getGVR("kyverno.io", "v1", "clusterpolicies")
// Unstructured
utd, err := client.Resource(gvr).Namespace(namespace).Get(name, metav1.GetOptions{})
// Unstructured -> cpol
var cpol ClusterPolicy
runtime.DefaultUnstructuredConverter.FromUnstructured(utd.UnstructuredContent(), &cpol)
logrus.Info("kube kyverno cpol ", cpol.Name, ", version:", cpol.Generation)
return &cpol, nil
}
Create
前端 Post 传输 yaml 信息,传输过来实际上是字符串。
import "gopkg.in/yaml.v3"
func createKyvernoFrom(client dynamic.Interface, namespace string, yamlData string) (*ClusterPolicy, error) {
gvr := getGVR("kyverno.io", "v1", "clusterpolicies")
// yaml string -> cpol
var cpol ClusterPolicy
err := yaml.Unmarshal([]byte(yamlData), &cpol)
if err != nil {
logrus.Error(err)
return nil, err
}
// cpol -> []byte
data, err := json.Marshal(&cpol)
if err != nil {
logrus.Error(err)
return nil, err
}
// []byte -> Unstructured
obj := &unstructured.Unstructured{}
err := json.Unmarshal(data, &obj.Object)
if err != nil {
logrus.Error(err)
return nil, err
}
// cpol -> unstructed 也可以直接使用 runtime.DefaultUnstructuredConverter.FromUnstructured 类似的方法做一个反转换,这部分可以从参考我另一篇blog: https://blog.kelu.org/tech/2021/03/14/go-unstructured.html
// Unstructured create k8s
var utd *unstructured.Unstructured
utd, err = dynamicClient.Resource(gvr).Create(obj, metav1.CreateOptions{})
if err != nil {
logrus.Error(err)
return nil, err
}
// unstructured -> []byte
result, err := utd.MarshalJSON()
if err != nil {
logrus.Error(err)
return nil, err
}
// []byte -> cpol
var resultCpol ClusterPolicy
if err := json.Unmarshal(result, &resultCpol); err != nil {
logrus.Error(err)
return nil, err
}
return &resultCpol, nil
}
Update
utd, err = client.Resource(gvr).Namespace(namespace).Update(obj, metav1.UpdateOptions{})
Patch
_, err := client.Resource(gvr).Namespace(namespace).Patch(name, pt, data, metav1.PatchOptions{})
Delete
client.Resource(gvr).Namespace(namespace).Delete(name, nil)
参考资料
- 在不生成 crd client 代码的情况下通过 client-go 增删改查 k8s crd 资源
- An example of using dynamic client of k8s.io/client-go
- https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured#Unstructured.MarshalJSON
- GoLang : Dynamic JSON Parsing using empty Interface and without Struct in Go Language
- Kubernetes CRD 系列:Client-Go 的使用
- 使用client-go 进行k8s相关操作-dynamicclient(二)
- Kubernetes client-go实现yaml创建Deployment