go解析YAML文件(多文檔解析)
概述
摘要: 在kubernetes二次開發(fā)中,常用yaml文件來創(chuàng)建資源,比如Pod,deployment。在使用client-go創(chuàng)建資源之前,需要將yaml文件轉(zhuǎn)換為資源對(duì)象obj。本文介紹yaml文件的編碼和解碼的使用。
正文
yaml文檔介紹
YAML 是一種輕量級(jí)的數(shù)據(jù)序列化格式,用于在不同平臺(tái)間傳輸數(shù)據(jù),也用于配置文件等場(chǎng)景。在 YAML 中,整個(gè)數(shù)據(jù)文件稱為“文檔”,而一般情況下,每個(gè) YAML 文件只包含一個(gè)文檔。而在kubernetes使用中,kubectl為了創(chuàng)建資源方便性,常用的YAML文件確包括多個(gè)文檔。
文檔可以包含一個(gè)或多個(gè)“節(jié)點(diǎn)”,每個(gè)節(jié)點(diǎn)包含一個(gè)或多個(gè)鍵值對(duì)。鍵值對(duì)之間的關(guān)系使用冒號(hào)(:)
表示,鍵值對(duì)的鍵和值之間使用空格隔開,同時(shí)整個(gè)鍵、值對(duì)的行末不能有多余的空格
。一個(gè)節(jié)點(diǎn)可以用減號(hào)(-)
開頭表示它是一個(gè)序列,即列表。
# 這是一個(gè) YAML 文檔 name: Tom age: 30 hobbies: - reading - running - swimming
在這個(gè)文檔中,使用“#”符號(hào)
表示注釋,每個(gè)節(jié)點(diǎn)的鍵和值之間用冒號(hào)分隔,節(jié)點(diǎn)之間使用換行符分隔。在“hobbies”節(jié)點(diǎn)中,使用“-”符號(hào)表示這是一個(gè)序列(即一個(gè)列表),列表中的項(xiàng)目用“-”符號(hào)
開始表示
YAML 文檔特點(diǎn)包括:
易讀性
:YAML 使用縮進(jìn)和結(jié)構(gòu)化的格式,使得它非常易讀。它使用空格而不是特殊符號(hào)來表示層級(jí)關(guān)系,使得文檔具有良好的可讀性。簡(jiǎn)潔性
:YAML 使用簡(jiǎn)潔的語法來表示數(shù)據(jù)結(jié)構(gòu),避免了冗余和重復(fù)。它支持使用引用和折疊塊等特性來簡(jiǎn)化文檔的編寫。可嵌套性:YAML 支持嵌套結(jié)構(gòu),可以在一個(gè)節(jié)點(diǎn)中包含其他節(jié)點(diǎn),從而表示更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。這種嵌套結(jié)構(gòu)使得數(shù)據(jù)的組織更加靈活和清晰。
跨平臺(tái)
:YAML 是一種與編程語言無關(guān)的數(shù)據(jù)格式,可以輕松地在不同的編程語言和平臺(tái)之間進(jìn)行交換和共享。強(qiáng)大的數(shù)據(jù)表達(dá)能力
:YAML 支持表示各種數(shù)據(jù)類型,包括字符串、整數(shù)、浮點(diǎn)數(shù)、布爾值、日期、時(shí)間、正則表達(dá)式等。它還支持列表、哈希表等數(shù)據(jù)結(jié)構(gòu)。注釋:YAML 允許在文檔中包含注釋,注釋以“#”符號(hào)開頭,可以用于提供有關(guān)數(shù)據(jù)的額外說明或注解。
可擴(kuò)展性:YAML 允許用戶通過自定義標(biāo)簽和類型的方式擴(kuò)展其功能,使其適應(yīng)更多的應(yīng)用場(chǎng)景和需求。
與配置文件的兼容性:由于其易讀性和簡(jiǎn)潔性,YAML 在配置文件方面非常流行。它被廣泛用于各種軟件和框架的配置文件中,如
Kubernetes
、Docker
Compose
、Ansible
等。總的來說,YAML 是一種簡(jiǎn)單、可讀性強(qiáng)且功能豐富的數(shù)據(jù)序列化格式,非常適合在配置文件和數(shù)據(jù)交換方面使用
對(duì)YAML文檔的處理常用三方庫 "gopkg.in/yaml.v2"
常用方法包括
Yaml.Marshal() 將結(jié)構(gòu)體對(duì)象 轉(zhuǎn)換為YAML字符串
yaml.Unmarshal() 將YAML字符串轉(zhuǎn)換為結(jié)構(gòu)體對(duì)象
代碼示例
yaml文件與結(jié)構(gòu)體之間的轉(zhuǎn)換
創(chuàng)建一個(gè)用于測(cè)試YAML文件fakePersonStruct.yaml
--- name: xianwei age: 18 city: shenzhen
編寫測(cè)試代碼
// go test -run="^TestPersonStruct$" -v func TestPersonStruct(t *testing.T) { // 定義一個(gè)結(jié)構(gòu)體 type Person struct { Name string `yaml:"name"` Age int `yaml:"int"` City string `yaml:"city"` } byteStream, err := ioutil.ReadFile("yamldir/fakePersonStruct.yaml") if err != nil { t.Errorf("cann not open file.err=%v\n", err) return } // yaml字符串轉(zhuǎn)換為person對(duì)象 p := new(Person) if err := yaml.Unmarshal(byteStream, p); err != nil { t.Errorf("yaml.Unmarshal.err=%v\n", err) return } fmt.Printf("person= %+v\n", p) // Person對(duì)象轉(zhuǎn)換為yaml字符串 yamlstr, err := yaml.Marshal(p) if err != nil { t.Errorf("yaml.Marshal.err=%v\n", err) return } fmt.Printf("yamlstr= %+v\n", string(yamlstr)) }
上面代碼實(shí)現(xiàn)了:
- yaml字符串轉(zhuǎn)換為person對(duì)象
- Person對(duì)象轉(zhuǎn)換為yaml字符串
代碼輸出
=== RUN TestPersonStruct
person= &{Name:xianwei Age:0 City:shenzhen}
yamlstr= name: xianwei
int: 0
city: shenzhen--- PASS: TestPersonStruct (0.00s)
PASS
解析yaml文件并創(chuàng)建pod
使用yaml創(chuàng)建pod時(shí),需要注意的是,需要先將yaml串轉(zhuǎn)換為json串,再由json串Unmarshal成一個(gè)pod對(duì)象。原因是v1.pod類型定義中(源碼如下所示),只指定定義字符串的轉(zhuǎn)換。
type Pod struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` Spec PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` Status PodStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` }
創(chuàng)建一個(gè)用于測(cè)試YAML文件pod-nginx.yaml
--- apiVersion: v1 kind: Pod metadata: name: mynginx spec: containers: - name: nginx image: nginx:1.14.2
編寫測(cè)試代碼
// go test -run="^TestYamlfilePod$" -v func TestYamlfilePod(t *testing.T) { // 將yaml文件轉(zhuǎn)換為pod對(duì)象 byteStream, err := ioutil.ReadFile("yamldir/pod-nginx.yaml") if err != nil { t.Errorf("cann not open file.err=%v\n", err) return } // yaml串轉(zhuǎn)換為json串 pod := new(v1.Pod) // yaml2 表示 三方庫"k8s.io/apimachinery/pkg/util/yaml" jsonstr, err := yaml2.ToJSON(byteStream) if err != nil { t.Errorf("yaml2.ToJSON.err=%v\n", err) return } fmt.Printf("jsonstr= %v\n", string(jsonstr)) // json串轉(zhuǎn)換為pod結(jié)構(gòu)體 err = json.Unmarshal(jsonstr, &pod) if err != nil { t.Errorf("json.Unmarshal.err=%v\n", err) return } fmt.Printf("pod= %v\n", pod) // clientset獲取 restConfig, err := clientcmd.BuildConfigFromFlags("", "/Users/80280051/.kube/dg11test/config") if err != nil { panic(err) } K8sClientSet, err := kubernetes.NewForConfig(restConfig) if err != nil { panic(err) } // 創(chuàng)建pod _, err = K8sClientSet.CoreV1().Pods("default").Create(pod) if err != nil { t.Errorf("cann not create pod.err=%v\n", err) return } }
上面代碼實(shí)現(xiàn)了:
- yaml字符串轉(zhuǎn)換為json字符串
- 再由json字符串轉(zhuǎn)換pod對(duì)象
- 最后使用clientset創(chuàng)建pod
代碼輸出
=== RUN TestYamlfilePod
jsonstr= {"apiVersion":"v1","kind":"Pod","metadata":{"name":"mynginx"},"spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx"}]}}
pod= &Pod{ObjectMeta:{mynginx 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] [] []},Spec:PodSpec{Volumes:[]Volume{},Containers:[]Container{Container{Name:nginx,Image:nginx:1.14.2,Command:[],Args:[],WorkingDir:,Ports:[]ContainerPort{},Env:[]EnvVar{},Resources:ResourceRequirements{Limits:ResourceList{},Requests:ResourceList{},},VolumeMounts:[]VolumeMount{},LivenessProbe:nil,ReadinessProbe:nil,Lifecycle:nil,TerminationMessagePath:,ImagePullPolicy:,SecurityContext:nil,Stdin:false,StdinOnce:false,TTY:false,EnvFrom:[]EnvFromSource{},TerminationMessagePolicy:,VolumeDevices:[]VolumeDevice{},StartupProbe:nil,},},RestartPolicy:,TerminationGracePeriodSeconds:nil,ActiveDeadlineSeconds:nil,DNSPolicy:,NodeSelector:map[string]string{},ServiceAccountName:,DeprecatedServiceAccount:,NodeName:,HostNetwork:false,HostPID:false,HostIPC:false,SecurityContext:nil,ImagePullSecrets:[]LocalObjectReference{},Hostname:,Subdomain:,Affinity:nil,SchedulerName:,InitContainers:[]Container{},AutomountServiceAccountToken:nil,Tolerations:[]Toleration{},HostAliases:[]HostAlias{},PriorityClassName:,Priority:nil,DNSConfig:nil,ShareProcessNamespace:nil,ReadinessGates:[]PodReadinessGate{},RuntimeClassName:nil,EnableServiceLinks:nil,PreemptionPolicy:nil,Overhead:ResourceList{},TopologySpreadConstraints:[]TopologySpreadConstraint{},EphemeralContainers:[]EphemeralContainer{},},Status:PodStatus{Phase:,Conditions:[]PodCondition{},Message:,Reason:,HostIP:,PodIP:,StartTime:<nil>,ContainerStatuses:[]ContainerStatus{},QOSClass:,InitContainerStatuses:[]ContainerStatus{},NominatedNodeName:,PodIPs:[]PodIP{},EphemeralContainerStatuses:[]ContainerStatus{},},}
--- PASS: TestYamlfilePod (0.05s)
PASS
yaml文件中多文檔的處理
在使用kubectl或client-go創(chuàng)建Pod時(shí),經(jīng)常會(huì)將多個(gè)YAML文檔寫到一個(gè)YAML文件。此種情況下,可以通過按字符串"\n—"進(jìn)行分割,分別處理。
創(chuàng)建一個(gè)用于測(cè)試YAML文件pod-nginx-secret.yaml
--- apiVersion: v1 kind: Pod metadata: name: mynginx spec: containers: - name: nginx image: nginx:1.14.2 --- apiVersion: v1 type: Opaque stringData: userdata: "eGlhbndlaTExMQo=" kind: Secret metadata: name: mySecret
編寫測(cè)試代碼
// go test -run="^TestYamlfileMultiDocument" -mod=vendor -v func TestYamlfileMultiDocument(t *testing.T) { // 將yaml文件轉(zhuǎn)換為yaml串 fp, err := ioutil.ReadFile("yamldir/pod-nginx-secret.yaml") if err != nil { t.Errorf("cann not open file.err=%v\n", err) return } // 定義分隔符 const yamlSeparator = "\n---" pod := new(v1.Pod) secret := new(v1.Secret) yamlsplit := strings.Split(string(fp), yamlSeparator) fmt.Println("len of yamlsplit = ", len(yamlsplit)) for index, yamlObj := range yamlsplit { // 處理第一個(gè)yaml文檔,轉(zhuǎn)換為 pod 對(duì)象 if index == 0 { // 轉(zhuǎn)換為對(duì)象 fmt.Println("obj=", yamlObj) if err = yaml.Unmarshal([]byte(yamlObj), pod); err != nil { t.Errorf("yaml unmarshal error of pod.err = %v\n", err) return } fmt.Printf("pod obj =%+v\n", pod) } // 處理第二個(gè)yaml文檔,轉(zhuǎn)換為secret對(duì)象 if index == 1 { fmt.Println("obj=", yamlObj) // 轉(zhuǎn)換為對(duì)象 if err = yaml.Unmarshal([]byte(yamlObj), secret); err != nil { t.Errorf("yaml unmarshal error of secret.err = %v\n", err) return } fmt.Printf("secret obj =%+v\n", secret) } } }
上面代碼實(shí)現(xiàn)了:
- 對(duì)yaml字符串按"\n—"進(jìn)行分割
- 對(duì)分割后的第一個(gè)yaml文檔,轉(zhuǎn)換為 pod 對(duì)象
- 對(duì)分割后的第二個(gè)yaml文檔,轉(zhuǎn)換為secret對(duì)象
代碼輸出
=== RUN TestYamlfileMultiDocument
len of yamlsplit = 2
obj= ---
apiVersion: v1
kind: Pod
metadata:
name: mynginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
pod obj =&Pod{ObjectMeta:{ 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] [] []},Spec:PodSpec{Volumes:[]Volume{},Containers:[]Container{Container{Name:nginx,Image:nginx:1.14.2,Command:[],Args:[],WorkingDir:,Ports:[]ContainerPort{},Env:[]EnvVar{},Resources:ResourceRequirements{Limits:ResourceList{},Requests:ResourceList{},},VolumeMounts:[]VolumeMount{},LivenessProbe:nil,ReadinessProbe:nil,Lifecycle:nil,TerminationMessagePath:,ImagePullPolicy:,SecurityContext:nil,Stdin:false,StdinOnce:false,TTY:false,EnvFrom:[]EnvFromSource{},TerminationMessagePolicy:,VolumeDevices:[]VolumeDevice{},StartupProbe:nil,},},RestartPolicy:,TerminationGracePeriodSeconds:nil,ActiveDeadlineSeconds:nil,DNSPolicy:,NodeSelector:map[string]string{},ServiceAccountName:,DeprecatedServiceAccount:,NodeName:,HostNetwork:false,HostPID:false,HostIPC:false,SecurityContext:nil,ImagePullSecrets:[]LocalObjectReference{},Hostname:,Subdomain:,Affinity:nil,SchedulerName:,InitContainers:[]Container{},AutomountServiceAccountToken:nil,Tolerations:[]Toleration{},HostAliases:[]HostAlias{},PriorityClassName:,Priority:nil,DNSConfig:nil,ShareProcessNamespace:nil,ReadinessGates:[]PodReadinessGate{},RuntimeClassName:nil,EnableServiceLinks:nil,PreemptionPolicy:nil,Overhead:ResourceList{},TopologySpreadConstraints:[]TopologySpreadConstraint{},EphemeralContainers:[]EphemeralContainer{},},Status:PodStatus{Phase:,Conditions:[]PodCondition{},Message:,Reason:,HostIP:,PodIP:,StartTime:<nil>,ContainerStatuses:[]ContainerStatus{},QOSClass:,InitContainerStatuses:[]ContainerStatus{},NominatedNodeName:,PodIPs:[]PodIP{},EphemeralContainerStatuses:[]ContainerStatus{},},}
obj=
apiVersion: v1
type: Opaque
stringData:
userdata: "eGlhbndlaTExMQo="
kind: Secret
metadata:
name: mySecret
secret obj =&Secret{ObjectMeta:{ 0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[] map[] [] [] []},Data:map[string][]byte{},Type:Opaque,StringData:map[string]string{},}
--- PASS: TestYamlfileMultiDocument (0.00s)
PASS
總結(jié)
本文介紹了YAML文檔的特點(diǎn),并用代碼演示了GO語言對(duì)YAML文檔的處理,特別是描述了當(dāng)YAML文件中存在多個(gè)YAML文檔時(shí)如何處理的方法。
到此這篇關(guān)于go解析YAML文件(多文檔解析)的文章就介紹到這了,更多相關(guān)go解析YAML文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang函數(shù)的返回值實(shí)現(xiàn)
本文主要介紹了golang函數(shù)的返回值實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03golang使用信號(hào)量熱更新的實(shí)現(xiàn)示例
這篇文章主要介紹了golang使用信號(hào)量熱更新的實(shí)現(xiàn)示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04關(guān)于Golang獲取當(dāng)前項(xiàng)目絕對(duì)路徑的問題
這篇文章主要介紹了Golang獲取當(dāng)前項(xiàng)目絕對(duì)路徑的問題,通常的做法是go run用于本地開發(fā),用一個(gè)命令中快速測(cè)試代碼確實(shí)非常方便;在部署生產(chǎn)環(huán)境時(shí),我們會(huì)通過go build構(gòu)建出二進(jìn)制文件然后上傳到服務(wù)器再去執(zhí)行,那么會(huì)產(chǎn)生什么問題呢?感興趣的朋友一起看看吧2022-04-04