golang 使用模板新增檔案_自己動手寫一個 Kubernetes YAML 模板化工具
技術標籤:golang 使用模板新增檔案
(給Go開發大全
加星標)
來源:陽明
https://www.qikqiak.com/post/code-k8s-yaml-templating/
我們在使用 Kubernetes 編寫資源清單檔案的時候,往往會使用類似於Helm
或者Kustomize
這樣的工具來進行模板化處理,一來是提高了資源清單的靈活性,另一方面也確實降低了我們安裝複雜的 Kubernetes 應用的門檻。本文我們嘗試自己使用 Golang 來實現一個 YAML 資源清單檔案模板化的方案。
Golang 的模板化
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}
上面的程式碼讀取一個位於 filePath 的檔案,並將其作為模板,使用availableFunctions
中的函式和availableData
中的資料來填充所有的模板值。比如我們讀取的是一個 ConfigMap 的 YAML 檔案。
apiVersion: v1kind: ConfigMapmetadata: name: my-configmap namespace: {{ .Namespace }} labels: app: myappdata: USER: admin PASSWORD: {{ GeneratePassword }}
然後我們把availableData
availableFunctions
定義成如下所示的程式碼。
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 非常簡單:
kubectlcreate-fmyfile.yaml
但是如果要我們自己去編寫程式碼來應用 YAML 檔案的話,一般情況下會去使用client-go
這個客戶端工具包,但是 client-go 是針對靜態型別的,而 YAML 檔案中是沒有對應的資訊的,但是我們還可以通過下面兩種方案來解決這個問題。
使用 YAML 中的Kind和Version 反序列化為靜態型別,然後使用它的型別化 REST 客戶端進行通訊。
使用 Discovery 功能,Discovery 允許我們動態地查詢給定型別的 REST 客戶端,而不是通過靜態型別去訪問,下面我們就使用這種方式來進行演示。
首先我們需要像往常一樣與 APIServer 通訊建立一個 ClientSet 物件,如果我們從一個可以使用 kubectl 的系統執行程式碼,就意味著有一個可用的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)}
為了使用我們的DiscoveryREST mapper和動態 REST 介面,我們需要將 YAML 檔案的內容 decode 成一個runtime.Objects
物件。
首先將 YAML 檔案內容根據---
進行分割(一個 YAML 檔案中可能有多個資源物件):
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 的?
如果覺得本文不錯,歡迎轉發推薦給更多人。
分享、點贊和在看
支援我們分享更多好文章,謝謝!