紫色飛豬的研發之旅--04client-go客戶端

此部分是對紫色飛豬的研發之旅--02golang:client-go淺學demo[https://www.cnblogs.com/zisefeizhu/p/15207204.html]的補充

對02的改動點如下:

cmd/root.go

// 初始化配置
func initConifg() {
	config.Loader(cfgFile) // cfgFile string
    //dservice.Demo()
	//service.RESTClient()
	//service.ClientSet()
	//service.DynamicClient()
	service.DiscoveryClient()
}

config/config.go

// DeployAndKuExternal 部署與k8s外部
func DeployAndKuExternal() *rest.Config {
	// 3. 在k8s的環境中kubectl配置文件一般放在用戶目錄的.kube文件中
	if home := homeDir(); home != ""{
		kubeconfig = flag.String("kubeconfig",filepath.Join(home,".kube","config"),"(可選)kubeconfig 文件的絕對路徑")
		fmt.Println("kubeConfig", *kubeconfig)
	}else {
		kubeconfig = flag.String("kubeconfig","","kubeconfig 文件的絕對路徑")
		fmt.Println(kubeconfig)
		fmt.Println("##################")
	}
	flag.Parse()
	// 4.創建集羣配置,首先使用 inCluster 模式(需要區配置對應的RBAC 權限,默認的sa是default-->是沒有獲取deployment的List權限)
	if config, err = rest.InClusterConfig(); err != nil {
		// 使用Kubeconfig文件配置集羣Config對象
		if config,err = clientcmd.BuildConfigFromFlags("",*kubeconfig); err != nil {
			panic(err.Error())
		}
	}

	return config
}

// DeployAndKuInternal 部署與k8s內部
func DeployAndKuInternal() *rest.Config {
	// 使用當前上下文環境
	kubeconfig := filepath.Join(
		os.Getenv("KUBECONFIG"),
		)
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		logrus.Fatal(err)
	}
	return config
}

// KubeConfig k8s的config加載
func KubeConfig() *rest.Config  {
	switch choose := viper.GetInt("DeploymentMethod"); choose {
	case 1 :
		config = DeployAndKuExternal()
	case 0 :
		config = DeployAndKuInternal()
	}
	return config
}

service/demo.go

package service

/*
	參考文檔:
	Kubernetes的Group、Version、Resource學習小記: https://xinchen.blog.csdn.net/article/details/113715847
	Client-go 實戰:https://xinchen.blog.csdn.net/article/details/113753087

	注:上面的鏈接博主寫的已經十分詳細,本着好記性不如爛筆頭的目的 跟着敲了一邊
*/

import (
	"context"
	"flag"
	"fmt"
	appsV1 "k8s.io/api/apps/v1"
	coreV1 "k8s.io/api/core/v1"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/kubernetes/scheme"

     rschema "k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"operator/config"
	"operator/pkg"
)

func RESTClient()  {

	//RestClient demo : 查詢kube-system這個namespace下的所有pod,然後在控制檯打印每個pod的幾個關鍵字段;

	config := config.KubeConfig()
	// 參考path : /api/v1/namespaces/{namespace}/pods
	config.APIPath = "api"
	// pod的group是空字符串
	config.GroupVersion = &coreV1.SchemeGroupVersion
	// 指定序列化工具
	config.NegotiatedSerializer = scheme.Codecs

	// 根據配置信息構建restClient實例
	restClient, err := rest.RESTClientFor(config)

	if err!=nil {
		panic(err.Error())
	}

	// 保存pod結果的數據結構實例
	result := &coreV1.PodList{}

	//  指定namespace
	namespace := "kube-system"
	// 設置請求參數,然後發起請求
	// GET請求
	err = restClient.Get().
		//  指定namespace,參考path : /api/v1/namespaces/{namespace}/pods
		Namespace(namespace).
		// 查找多個pod,參考path : /api/v1/namespaces/{namespace}/pods
		Resource("pods").
		// 指定大小限制和序列化工具
		VersionedParams(&metaV1.ListOptions{Limit:100}, scheme.ParameterCodec).
		// 請求
		Do(context.TODO()).
		// 結果存入result
		Into(result)

	if err != nil {
		panic(err.Error())
	}

	// 表頭
	fmt.Printf("namespace\t status\t\t name\n")

	// 每個pod都打印namespace、status.Phase、name三個字段
	for _, d := range result.Items {
		fmt.Printf("%v\t %v\t %v\n",
			d.Namespace,
			d.Status.Phase,
			d.Name)
	}
}

const (
	NAMESPACE = "test-clientset"
	DEPLOYMENT_NAME = "client-test-deployment"
	SERVICE_NAME = "client-test-service"
)

func ClientSet()  {
	/*
		本次編碼實戰的需求如下:
		寫一段代碼,檢查用戶輸入的operate參數,該參數默認是create,也可以接受clean;
		如果operate參數等於create,就執行以下操作:
		新建名爲test-clientset的namespace
		新建一個deployment,namespace爲test-clientset,鏡像用tomcat,副本數爲2
		新建一個service,namespace爲test-clientset,類型是NodePort
		如果operate參數等於clean,就刪除create操作中創建的service、deployment、namespace等資源:
		以上需求使用Clientset客戶端實現,完成後咱們用瀏覽器訪問來驗證tomcat是否正常;
	*/

	// 獲取用戶輸入的操作類型,默認是create,還可以輸入clean,用於清理所有資源
	operate := flag.String("operate", "clean", "operate type : create or clean")
	// 把用戶傳遞的命令行參數解析爲對應變量的值
	flag.Parse()

	fmt.Printf("operation is %v\n", *operate)

	// 實例化clientset對象
	clientset, err := kubernetes.NewForConfig(config.KubeConfig()); if err != nil {
		panic(err.Error())
	}

	// 如果要執行清理操作
	if "clean" == *operate {
		clean(clientset)
	} else {
		// 創建namespace
		createNamespace(clientset)

		// 創建deployment
		createDeployment(clientset)

		// 創建service
		createService(clientset)
	}
}

// 清理本次實戰創建的所有資源
func clean(clientset *kubernetes.Clientset)  {
	emptyDeleteOptions := metaV1.DeleteOptions{}

	// 刪除service
	if err := clientset.CoreV1().Services(NAMESPACE).Delete(context.TODO(), SERVICE_NAME,emptyDeleteOptions); err != nil {
		panic(err.Error())
	}

	// 刪除deployment
	if err := clientset.AppsV1().Deployments(NAMESPACE).Delete(context.TODO(), DEPLOYMENT_NAME, emptyDeleteOptions); err != nil {
		panic(err.Error())
	}

	// 刪除namespace
	if err := clientset.CoreV1().Namespaces().Delete(context.TODO(),NAMESPACE, emptyDeleteOptions); err != nil {
		panic(err.Error())
	}
}

// 新建namespace
func createNamespace(clientset *kubernetes.Clientset)  {
	namespaceClient := clientset.CoreV1().Namespaces()
	namespace := &coreV1.Namespace{
		ObjectMeta: metaV1.ObjectMeta{
			Name: NAMESPACE,
		},
	}

	result, err := namespaceClient.Create(context.TODO(), namespace, metaV1.CreateOptions{}); if err != nil{
		panic(err.Error())
	}

	fmt.Printf("Create namespace %s \n", result.GetName())
}

// 新建service
func createService(clientset *kubernetes.Clientset)  {
	// 得到service的客戶端
	serviceClient := clientset.CoreV1().Services(NAMESPACE)

	// 實例化一個數據結構
	service := &coreV1.Service{
		ObjectMeta: metaV1.ObjectMeta{
			Name: SERVICE_NAME,
		},
		Spec: coreV1.ServiceSpec{
			Ports: []coreV1.ServicePort{
				{
					Name: "http",
					Port: 8080,
					NodePort: 30080,
				},
			},
			Selector: map[string]string{
				"app" : "tomcat",
			},
			Type: coreV1.ServiceTypeNodePort,
		},
	}
	result, err := serviceClient.Create(context.TODO(), service, metaV1.CreateOptions{}); if err != nil {
		panic(err.Error())
	}
	fmt.Printf("Create service %s \\n", result.GetName())
}

// 新建deployment
func createDeployment(clientset *kubernetes.Clientset)  {
	// 得到deployment的客戶端
	deploymentClient := clientset.AppsV1().Deployments(NAMESPACE)

	// 實例化一個數據結構
	deployment := &appsV1.Deployment{
		ObjectMeta: metaV1.ObjectMeta{
			Name: DEPLOYMENT_NAME,
		},
		Spec: appsV1.DeploymentSpec{
			Replicas: pkg.Int32Ptr(2),
			Selector: &metaV1.LabelSelector{
				MatchLabels: map[string]string{
					"app": "tomcat",
				},
			},
			Template: coreV1.PodTemplateSpec{
				ObjectMeta: metaV1.ObjectMeta{
					Labels: map[string]string{
						"app": "tomcat",
					},
				},
				Spec: coreV1.PodSpec{
					Containers: []coreV1.Container{
						{
							Name:            "tomcat",
							Image:           "tomcat:latest",
							ImagePullPolicy: "IfNotPresent",
							Ports: []coreV1.ContainerPort{
								{
									Name:          "http",
									Protocol:      coreV1.ProtocolTCP,
									ContainerPort: 8080,
								},
							},
						},
					},
				},
			},
		},
	}
	result, err := deploymentClient.Create(context.TODO(), deployment, metaV1.CreateOptions{}); if err != nil {
		panic(err.Error())
	}
	fmt.Printf("Create deployment %s \n", result.GetName())
}

func DynamicClient()  {
	// 查詢指定namespace下的所有pod,然後在控制檯打印出來,要求用dynamicClient實現
	dynamicClient, err := dynamic.NewForConfig(config.KubeConfig()); if err != nil {
		panic(err.Error())
	}

	// 從dynamicClient 的唯一關聯方法所需的入參
	gvr := rschema.GroupVersionResource{Version: "v1", Resource: "pods"}
	// 使用dynamicClient的查詢列表方法,查詢指定namespace下的所有pod,
	// 注意此方法返回的數據結構類型是UnstructuredList
	unstructObj, err := dynamicClient.
		Resource(gvr).
		Namespace("kube-system").
		List(context.TODO(), metaV1.ListOptions{Limit: 100}); if err != nil {
			panic(err.Error())
	}

	// 實例化一個PodList數據結構,用於接收從unstructObj轉換後的結果
	podList := &coreV1.PodList{}

	// 轉換
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList); if err != nil {
		panic(err.Error())
	}

	// 表頭
	fmt.Printf("namespace\t status\t\t name\n")

	// 每個pod都打印namespace、status.Phase、name三個字段
	for _, d := range podList.Items {
		fmt.Printf("%v\t %v\t %v\n",
			d.Namespace,
			d.Status.Phase,
			d.Name)
	}
}

func DiscoveryClient()  {
	// 從kubernetes查詢所有的Group、Version、Resource信息,在控制檯打印出來
	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config.KubeConfig()); if err != nil{
		panic(err.Error())
	}

	// 獲取所有分組和資源數據
	APIGroup, APIResourceListSlice, err := discoveryClient.ServerGroupsAndResources(); if err != nil {
		panic(err.Error())
	}

	// 先看Group信息
	fmt.Printf("APIGroup :\n\n %v\n\n\n\n",APIGroup)

	// APIResourceListSlice是個切片,裏面的每個元素代表一個GroupVersion及其資源
	for _, singleAPIResourceList := range APIResourceListSlice {

		// GroupVersion是個字符串,例如"apps/v1"
		groupVerionStr := singleAPIResourceList.GroupVersion

		// ParseGroupVersion方法將字符串轉成數據結構
		gv, err := rschema.ParseGroupVersion(groupVerionStr)

		if err != nil {
			panic(err.Error())
		}

		fmt.Println("*****************************************************************")
		fmt.Printf("GV string [%v]\nGV struct [%#v]\nresources :\n\n", groupVerionStr, gv)

		// APIResources字段是個切片,裏面是當前GroupVersion下的所有資源
		for _, singleAPIResource := range singleAPIResourceList.APIResources {
			fmt.Printf("%v\n", singleAPIResource.Name)
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章