开发者

go解析YAML文件(多文档解析)

目录
  • 概述
  • 正文
    • yaml文档介绍
  • 代码示例
    • yaml文件与结构体之间的转换
    • 解析yaml文件并创建pod
    • yaml文件中多文档的处理
  • 总结

    概述

    摘要: 在kubernetes二次开发中,常用yaml文件来创建资源,比如Pod,deployment。在使用client-go创建资源之前,需要将yaml文件转换为资源对象obj。本文介绍yaml文件的编码和解码的使用。

    正文

    yaml文档介绍

    YAML 是一种轻量级的数据序列化格式,用于在不同平台间传输数据,也用于配置文件等场景。在 YAML 中,整个数据文件称为“文档”,而一般情况下,每个 YAML 文件只包含一个文档。而在kubernetes使用中,kubectl为了创建资源方便性,常用的YAML文件确包括多个文档。

    文档可以包含一个或多个“节点”,每个节点包含一个或多个键值对。键值对之间的关系使用冒号(:)表示,键值对的键和值之间使用空格隔开,同时整个键、值对的行末不能有多余的空格。一个节点可以用减号(-)开头表示它是一个序列,即列表。

    # 这是一个 YAML 文档
    name:  Tom
    age: 30
    hobbies: 
      - reading
      - running
      - swimming

    在这个文档中,使用“#”符号表示注释,每个节点的键和值之间用冒号分隔,节点之间使用换行符分隔。在“hobbies”节点中,使用“-”符号表示这是一个序列(即一个列表),列表中的项目用“-”符号开始表示

    YAML 文档特点包括

    • 易读性:YAML 使用缩进和结构化的格式,使得它非常易读。它使用空格而不是特殊符号来表示层级关系,使得文档具有良好的可读性。

    • 简洁性:YAML 使用简洁的语法来表示数据结构,避免了冗余和重复。它支持使用引用和折叠块等特性来简化文档的编写。

    • 可嵌套性:YAML 支持嵌套结构,可以在一个节点中包含其他节点,从而表示更复杂的数据结构。这种嵌套结构使得数据的组织更加灵活和清晰。

    • 跨平台:YAML 是一种与编程语言无关的数据格式,可以轻松地在http://www.devze.com不同的编程语言和平台之间进行交换和共享。

    • 强大的数据表达能力:YAML 支持表示各种数据类型,包括字符串、整数、浮点数、布尔值、日期、时间、正则表达式等。它还支持列表、哈希表等数据结构。

    • 注释:YAML 允许在文档中包含注释,注释以“#”符号开头,可以用于提供有关数据的额外说明或注解。

    • 可扩展性:YAML 允许用户通过自定义标签和类型的方式扩展其功能,使其适应更多的应用场景和需求。

    • 与配置文件的兼容性:由于其易读性和简洁性,YAML 在配置文件方面非常流行。它被广泛用于各种软件和框架的配置文件中,如 Kubernetesdocker ComposeAnsible 等。

      总的来说,YAML 是一种简单、可读性强且功能丰富的数据序列化格式,非常适合在配置文件和数据交换方面使用

    对YAML文档的处理常用三方库 "gopkg.in/yaml.v2"

    常用方法包括

    • Yaml.Marshal() 将结构体对象 转换为YAML字符串

    • yaml.Unmarshal() 将YAML字符串转换为结构体对象

    代码示例

    yaml文件与结构体之间的转换

    创建一个用于测试YAML文件fakePersonStruct.yaml

    ---
    name: xianwei
    age: 18
    city: shenzhen
    

    编写测试代码

    // go test -run="^TestPersonStruct$"  -v
    func TestPersonStruct(t *testing.T) {
      // 定义一个结构体
    	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字符串转换为person对象
    	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对象转换为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))
    }
    

    上面代码实现了:

    • yaml字符串转换为person对象
    • Person对象转换为yaml字符串

    代码输出

    === RUN   TestPersonStruct

    person= &{Name:xianwei Age:0 City:shenzhen}

    yamlstr= name: xianwei

    int: 0

    city: shenzhen

    --- PASS: TestPersonStruct (0.00s)

    PASS

    解析yaml文件并创建pod

    使用yaml创建pod时,需要注意的是,需要先将yaml串转换为json串,再由json串Unmarshal成一个pod对象。原因是v1.pod类型定义中(源码如下所示),只指定定义字符串的转换。

    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"`
    }
    

    创建一个用于测试YAML文件pod-nginx.yaml

    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: mynginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.14.2

    编写测试代码

    // go test -run="^TestYamlfilePod$"  -v
    func TestYamlfilePod(t *testing.T) {
    	// 将yaml文件转换为pod对象
    	byteStream, err := ioutil.ReadFile("yamldir/pod-nginx.yaml")
    	if err != nil {
    		t.Errorf("cann not open file.err=%v\n", err)
    		return
    	}
    
    	// yaml串转换为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串转换为pod结构体
    	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)
    	}
    
    	// 创建pod
    	_, err = K8sClientSet.CoreV1().Pods("default").Create(pod)
    	if err != nil {
    		t.Errorf("cann not create pod.err=%v\n", err)
    		return
    	}
    	
    }
    

    上面代码实现了:

    • yaml字符串转换为json字符串
    • 再由json字符串转换pod对象
    • 最后使用clientset创建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,Swww.devze.comtdin:false,StdinOnce:false,TTY:false,EnvFrom:[]EnvFromSource{},TerminationMessagePolicy:,VolumeDevices:[]VolumeDevice{},StartupjsProbe: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:[www.devze.com]ContainerStatus{},QOSClass:,InitContainerStatuses:[]ContainerStatus{},Nominatedwww.devze.comNodeName:,PodIPs:[]PodIP{},EphemeralContainerStatuses:[]ContainerStatus{},},}

    --- PASS: TestYamlfilePod (0.05s)

    PASS

    yaml文件中多文档的处理

    在使用kubectl或client-go创建Pod时,经常会将多个YAML文档写到一个YAML文件。此种情况下,可以通过按字符串"\n—"进行分割,分别处理。

    创建一个用于测试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
    

    编写测试代码

    // go test -run="^TestYamlfileMultiDocument" -mod=vendor -v
    func TestYamlfileMultiDocument(t *testing.T) {
    	// 将yaml文件转换为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 {
    		// 处理第一个yaml文档,转换为 pod 对象
    		if index == 0 {
    			// 转换为对象
    			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)
    		}
    		// 处理第二个yaml文档,转换为secret对象
    		if index == 1 {
    			fmt.Println("obj=", yamlObj)
    			// 转换为对象
    			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)
    		}
    
    	}
    
    }
    

    上面代码实现了:

    • 对yaml字符串按"\n—"进行分割
    • 对分割后的第一个yaml文档,转换为 pod 对象
    • 对分割后的第二个yaml文档,转换为secret对象

    代码输出

    === 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

    总结

    本文介绍了YAML文档的特点,并用代码演示了GO语言对YAML文档的处理,特别是描述了当YAML文件中存在多个YAML文档时如何处理的方法。

    到此这篇关于go解析YAML文件(多文档解析)的文章就介绍到这了,更多相关go解析YAML文件内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜