Go开发大全

https://www.qikqiak.com/post/code-k8s-yaml-templating/

HelmKustomize

Golang 的模板化

text/template
package templatesimport (    "bytes"    "path/filepath"    "text/template"    ...)func Read(filePath string) ([]byte, error) {    tmpl, err := template.New(filepath.Base(filePath)).    Funcs(availableFunctions).    ParseFiles(filePath)    if err != nil {        return nil, err    }    var buf bytes.Buffer    if err := tmpl.Execute(&buf, availableData); err != nil {        return nil, err    }    return buf.Bytes(), nil}
availableFunctionsavailableData
apiVersion: v1kind: ConfigMapmetadata:  name: my-configmap  namespace: {{ .Namespace }}  labels:    app: myappdata:  USER: admin  PASSWORD: {{ GeneratePassword }}
availableDataavailableFunctions
var availableData = map[string]string{    "Namespace": "my-namespace",}var availableFunctions = template.FuncMap{    "GeneratePassword": GeneratePasswordFunc,}func GeneratePasswordFunc() (string, error) {...}

这样上面定义的 Read 函数调用后的输出结果如下所示。

apiVersion: v1kind: ConfigMapmetadata:  name: my-configmap  namespace: my-namespace  labels:    app: myappdata:  USER: admin  PASSWORD: s0m3p455w0rd # 依赖你的 GeneratePassword 函数

在程序中使用 YAML

当我们使用 kubectl 这样的 CLI 工具的时候,在 Kubernetes 中使用 YAML 非常简单:

kubectl create -f myfile.yaml
client-go
  • 使用 YAML 中的Kind和Version 反序列化为静态类型,然后使用它的类型化 REST 客户端进行通信。

  • 使用 Discovery 功能,Discovery 允许我们动态地查找给定类型的 REST 客户端,而不是通过静态类型去访问,下面我们就使用这种方式来进行演示。

kubeconfig$HOME/.kube/config
import (    "k8s.io/client-go/tools/clientcmd"    "k8s.io/client-go/kubernetes")...// 使用本地 ~/.kube/config 创建配置kubeConfigPath := os.ExpandEnv("$HOME/.kube/config")config, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)if err != nil {    log.Fatal(err)}// 使用上面的配置获取连接c, err := kubernetes.NewForConfig(config)if err != nil {    log.Fatal(err)}

ClientSet 相当于和 K8S 集群通信的网关,使用它我们可以获取对象来给我们提供发现接口。对于我们想要实现的功能,需要能够查询给定资源的类型,并与该类型的 REST 客户端进行通信,所以我们分别需要一个 Discovery REST mapper 和一个动态的 REST 接口,代码如下所示:

import (    "k8s.io/client-go/restmapper"    "k8s.io/client-go/dynamic")...// 获取支持的资源类型列表resources, err := restmapper.GetAPIGroupResources(c.Discovery())if err != nil {    log.Fatal(err)}// 创建 'Discovery REST Mapper',获取查询的资源的类型mapper:= restmapper.NewDiscoveryRESTMapper(resourcesAvailable)// 获取 'Dynamic REST Interface',获取一个指定资源类型的 REST 接口dynamicREST, err := dynamic.NewForConfig(config)if err != nil {    log.Fatal(err)}

接下来我们去查找 YAML 文件中所代表的对象类型,并得到一个支持它的 REST 客户端是不是就可以去操作这个资源对象了?

首先调用前面的 Read 函数读取并执行一个模板:

finalYAML, err := templates.Read(myFilePath)if err != nil {    log.Fatal(err)}
runtime.Objects
---
objectsInYAML := bytes.Split(yamlBytes, []byte("---"))if len(objectsInYAML) == 0 {    return nil, nil}

然后在每个片段上使用 k8s.io 的反序列化功能输出得到 runtime.Object 对象,以及一个持有 Group、Version 和 Kind 信息的结构体。

import(    "k8s.io/apimachinery/pkg/runtime/serializer/yaml")...for _, objectInYAML := range objectsInYAML {    runtimeObject, groupVersionAndKind, err :=     yaml.        NewDecodingSerializer(unstructured.UnstructuredJSONScheme).        Decode(objectInYAML.Raw, nil, nil)    if err != nil {        log.Fatal(err)    }...

现在我们可以回头去使用我们的 RESTMapper,通过上面得到的 GVK 来获取一个映射:

// 查找 Group/Version/Kind 的 REST 映射mapping, err := d.mapper.RESTMapping(groupVersionAndKind.GroupKind(), groupVersionAndKind.Version)if err != nil {    log.Fatal(err)}

有了资源类型,我们就可以使用前面的动态 REST 接口获取特定资源对象的客户端了:

unstructuredObj := runtimeObject.(*unstructured.Unstructured)var resourceREST dynamic.ResourceInterface// 需要为 namespace 范围内的资源提供不同的接口if mapping.Scope.Name() == meta.RESTScopeNameNamespace {    if unstructuredObj.GetNamespace() == "" {        unstructuredObj.SetNamespace("default")    }    resourceREST =     d.      dynamicREST.      Resource(mapping.Resource).      Namespace(unstructuredObj.GetNamespace())} else {    resourceREST = d.dynamicREST.Resource(mapping.Resource)}

到这里我们就可以在 Kubernetes 中使用得到的 client 对象来执行创建删除等操作了!

import (    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1")// 创建对象_, err = resourceREST.Create(unstructuredObj, metav1.CreateOptions{})if err != nil {    log.Fatal(err)}// 删除对象prop := metav1.DeletePropagationForegrounderr = resourceREST.Delete(unstructuredObj.GetName(),    &metav1.DeleteOptions{       PropagationPolicy: &prop,    })if err != nil {   log.Fatal(err)}

到这里我们就使用 Golang 完成了一个轻量级的 YAML 模板处理工具了。

 - EOF -

推荐阅读(点击标题可打开)

1、Go 语言层出不穷的安全问题

2、如何在 Go 中做依赖注入?

3、英雄联盟的大厂开发商是如何玩转 Go 的?

如果觉得本文不错,欢迎转发推荐给更多人。

分享、点赞和在看

支持我们分享更多好文章,谢谢!