紫色飞猪的研发之旅--08使用client-go的dynamic客户端对crd的操作实践

ClientSet的使用在此篇博文已有详细案例:紫色飞猪的研发之旅--02golang:client-go浅学demo 对于dynamicClient的使用将在本篇案例.

本篇有改动的目录结构为:

├── cmd
│   └── root.go
├── pkg
│   ├── dynamic-crd
│   │   ├── crd.yaml
│   │   └── crontab.yaml
└── service
      └── demo.go

3 directories, 4 files

cmd

root.go

// 初始化配置
func initConifg() {
	config.Loader(cfgFile) // cfgFile string
	service.Crd()
}

dynamic-crd

crd.yaml

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # name must match the spec fields below, and be in the form: <plural>.<group>
  name: crontabs.stable.example.com
spec:
  # group name to use for REST API: /apis/<group>/<version>
  # 对应 Group 字段的值
  group: stable.example.com
  # list of versions supported by this CustomResourceDefinition
  versions:
    # 对应 Version 字段的可选值
    - name: v1
      served: true
      # One and only one version must be marked as the storage version.
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  # either Namespaced or Cluster
  scope: Namespaced
  names:
    # plural name to be used in the URL: /apis/<group>/<version>/<plural>
    # 对应 Resource 字段的值
    plural: crontabs
    # singular name to be used as an alias on the CLI and for display
    singular: crontab
    # kind is normally the CamelCased singular type. Your resource manifests use this.
    kind: CronTab
    # shortNames allow shorter string to match your resource on the CLI
    shortNames:
    - ct

crontab.yaml

---
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: cron-1
  namespace: default
spec:
  cronSpec: "* * * * */5"
  image: my-awesome-cron-image-1

---
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: cron-2
  namespace: default
spec:
  cronSpec: "* * * * */8"
  image: my-awesome-cron-image-2

---
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: cron-3
  namespace: default
spec:
  cronSpec: "* * * * */10"
  image: my-awesome-cron-image-3

service

demo.go

package service

/*
	注:在实际借助client-go 开发时最常用的时clientSet和dynamicClient客户端
        clientSet的使用在此篇博文已有详细案例:https://www.cnblogs.com/zisefeizhu/p/15207204.html
        对于dynamicClient的使用将在本篇案例
*/

import (
	"context"
	"fmt"
	"github.com/sirupsen/logrus"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/client-go/dynamic"
	"operator/config"
	"strings"
)

// Crd client-go 对crd的有关操作
func Crd()  {
	dynamicClient, err := dynamic.NewForConfig(config.KubeConfig()); if err != nil {
		logrus.Println(err)
	}
	searchWorld := "list"
	// 删除空格
	search := strings.TrimSpace(searchWorld)
	switch search {
	case "list":
		list, err := listCrontabs(dynamicClient,"default"); if err != nil {
			logrus.Println(err)
		}
		for _, t := range list.Items {
			fmt.Printf("%s %s %s %s\n", t.Namespace, t.Name, t.Spec.CronSpec, t.Spec.Image)
		}
	case "get":
		ct, err := getCrontab(dynamicClient, "default","cron-1" ); if err != nil {
			logrus.Println(err)
		}
		fmt.Printf("%s %s %s %s\n", ct.Namespace, ct.Name, ct.Spec.CronSpec, ct.Spec.Image)
	case "create":
		createData := `
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: cron-5
  namespace: default
spec:
  cronSpec: "* * * * */20"
  image: my-awesome-cron-image-5`
		ct,err := createCrontabWithYaml(dynamicClient,"default",createData)
		if err != nil {
			logrus.Println(err.Error())
		}
		fmt.Printf("%s %s %s %s\n", ct.Namespace, ct.Name, ct.Spec.CronSpec, ct.Spec.Image)
	case "update":
		upData := `
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
  name: cron-2
  namespace: default
spec:
  cronSpec: "* * * * */15"
  image: my-awesome-cron-image-2`
		ud, err := updateCrontabWithYaml(dynamicClient,"default",upData); if err != nil {
			logrus.Println(err.Error())
		}
		fmt.Printf("%s %s %s %s\n", ud.Namespace, ud.Name, ud.Spec.CronSpec, ud.Spec.Image)
	case "patch":
		patchData := []byte(`{"spec": {"image": "my-awesome-cron-image-2-patch"}}`)
		pt,err := pathCrontab(dynamicClient, "default", "cron-1", types.MergePatchType, patchData); if err != nil {
			logrus.Println(err.Error())
		}
		fmt.Printf("%s %s %s %s\n", pt.Namespace, pt.Name, pt.Spec.CronSpec, pt.Spec.Image)
	case "delete":
		if err := deleteCrontab(dynamicClient,"default","cron-1"); err != nil {
			logrus.Println(err.Error())
		}
	}
}

var gvr = schema.GroupVersionResource{
	Group: "stable.example.com",
	Version: "v1",
	Resource: "crontabs",
}

type CrontabSpec struct {
	CronSpec string `json:"cronSpec"`
	Image    string `json:"image"`
}

type Crontab struct {
	metaV1.TypeMeta   `json:",inline"`
	metaV1.ObjectMeta `json:"metadata,omitempty"`
	Spec CrontabSpec `json:"spec,omitempty"`
}

type CrontabList struct {
	metaV1.TypeMeta `json:",inline"`
	metaV1.ListMeta `json:"metadata,omitempty"`
	Items []Crontab `json:"items"`
}

// listCrontabs 资源列表
func listCrontabs(client dynamic.Interface, namespace string) (*CrontabList, error)  {
	list, err := client.Resource(gvr).Namespace(namespace).List(context.TODO(),metaV1.ListOptions{}); if err != nil {
		return nil,err
	}
	data, err := list.MarshalJSON(); if err != nil {
		return nil,err
	}
	var ctList CrontabList
	err = json.Unmarshal(data, &ctList); if err != nil {
		return nil, err
	}
	return &ctList, nil

}

// getCrontab 获取资源
func getCrontab(client dynamic.Interface, namespace string, name string) (*Crontab, error)  {
	result, err := client.Resource(gvr).Namespace(namespace).Get(context.TODO(),name,metaV1.GetOptions{}); if err != nil {
		return nil, err
	}
	data , err := result.MarshalJSON(); if err != nil {
		return nil, err
	}
	var ct Crontab
	err = json.Unmarshal(data, &ct);  if err != nil {
		return nil, err
	}
	return &ct, nil
}

// createCrontabWithYaml 创建资源
func createCrontabWithYaml(client dynamic.Interface, namespace string, yamlData string) (*Crontab, error) {
	decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
	obj := &unstructured.Unstructured{}
	 _, gvk, err := decoder.Decode([]byte(yamlData), nil, obj); if err != nil {
	 	logrus.Println(err.Error())
	}
	// Get the common metadata, and show GVK
	fmt.Println(obj.GetName(), gvk.String())

	utd, err := client.Resource(gvr).Namespace(namespace).Create(context.TODO(),obj, metaV1.CreateOptions{})
	if err != nil {
		return nil, err
	}
	data, err := utd.MarshalJSON()
	if err != nil {
		return nil, err
	}
	var ct Crontab
	if err := json.Unmarshal(data, &ct); err != nil {
		return nil, err
	}
	return &ct, nil
}

// updateCrontabWithYaml 更新资源
func updateCrontabWithYaml(client dynamic.Interface, namespace string, yamlData string) (*Crontab, error)  {
	decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
	obj := &unstructured.Unstructured{}
	if _, _, err := decoder.Decode([]byte(yamlData),nil,obj); err != nil {
		return nil,err
	}

	utd, err := client.Resource(gvr).Namespace(namespace).Get(context.TODO(),obj.GetName(), metaV1.GetOptions{}); if err != nil {
		return nil, err
	}
	obj.SetResourceVersion(utd.GetResourceVersion())
	utd, err = client.Resource(gvr).Namespace(namespace).Update(context.TODO(),obj,metaV1.UpdateOptions{}); if err != nil {
		return nil, err
	}

	data, err := utd.MarshalJSON(); if err != nil {
		return nil, err
	}
	var ct Crontab
	if err := json.Unmarshal(data, &ct); err != nil {
		return nil, err
	}
	return &ct, nil
}

// pathCrontab 布丁资源
func pathCrontab(client dynamic.Interface, namespace, name string, pt types.PatchType, data []byte) (*Crontab ,error) {
	resource, err := client.Resource(gvr).Namespace(namespace).Patch(context.TODO(),name,pt,data, metaV1.PatchOptions{}); if err != nil {
		return nil, err
	}
	data, err = resource.MarshalJSON();if err != nil {
		return nil, err
	}
	var ct Crontab
	if err := json.Unmarshal(data, &ct); err != nil {
		return nil, err
	}
	return &ct, nil
}

// deleteCrontab删除资源
func deleteCrontab(client dynamic.Interface, namespace , name string) error  {
	fmt.Printf("将要删除%s名称空间的%s资源",namespace,name)
	err := client.Resource(gvr).Namespace(namespace).Delete(context.TODO(),name,metaV1.DeleteOptions{})
	return err
}

执行

1、部署crd
2、切换不同的``searchWorld``
posted @ 2021-09-08 13:37  紫色飞猪  阅读(1437)  评论(0编辑  收藏  举报