问题背景

大概是因为 k8s 定义了一些特殊的数据类型,所以 k8s 对象 yaml 序列化时与其它 yaml 包结果不同。

源代码

package main

import (
	"log"
	"os"

	"github.com/ghodss/yaml"
	yamlv2 "gopkg.in/yaml.v2"
	yamlv3 "k8s.io/apimachinery/pkg/util/yaml"
	
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

func main() {
	// 输出重定向到文件
	f, err := os.OpenFile("test.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	log.SetOutput(f)

	var service *corev1.Service
	service = &corev1.Service{
		ObjectMeta: v1.ObjectMeta{
			Name:      "test-service",
			Namespace: "default",
			Labels: map[string]string{
				"app": "test-app",
			},
		},
		Spec: corev1.ServiceSpec{
			Selector: map[string]string{
				"app": "test-app",
			},
			Ports: []corev1.ServicePort{
				{
					Name: "http",
					Port: 80,
					TargetPort: intstr.IntOrString{
						Type:   intstr.Int,
						IntVal: 8080,
					},
				},
			},
		},
	}
	{
		bytes, err := yaml.Marshal(service)
		if err != nil {
			panic(err)
		}
		log.Printf("yaml:\n%s\n\n", string(bytes))
		var service2 *corev1.Service
		err = yaml.Unmarshal(bytes, &service2)
		if err != nil {
			panic(err)
		}
		log.Printf("service2: %+v\n\n", service2)
		log.Println()
	}

	{
		bytes, err := yamlv2.Marshal(service)
		if err != nil {
			panic(err)
		}
		log.Printf("yamlv2:\n%s\n\n", string(bytes))
		var service2 *corev1.Service
		err = yamlv2.Unmarshal(bytes, &service2)
		if err != nil {
			panic(err)
		}
		log.Printf("service2: %+v\n\n", service2)
		log.Println()
	}

	{
		bytes3, err := yaml.Marshal(service)
		if err != nil {
			panic(err)
		}
		log.Printf("yaml:\n%s", string(bytes3))
		var service2 *corev1.Service
		err = yamlv3.Unmarshal(bytes3, &service2)
		if err != nil {
			panic(err)
		}
		log.Printf("service2: %+v\n\n", service2)
		log.Println()
	}
}

module tmpGo

go 1.20

require (
	github.com/ghodss/yaml v1.0.0
	gopkg.in/yaml.v2 v2.4.0
	k8s.io/api v0.29.1
	k8s.io/apimachinery v0.29.1
)

require (
	github.com/go-logr/logr v1.3.0 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	golang.org/x/net v0.19.0 // indirect
	golang.org/x/text v0.14.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	k8s.io/klog/v2 v2.110.1 // indirect
	k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
	sigs.k8s.io/yaml v1.3.0 // indirect
)

输出结果:

2024/01/31 17:10:20 yaml:
metadata:
  creationTimestamp: null
  labels:
    app: test-app
  name: test-service
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: test-app
status:
  loadBalancer: {}


2024/01/31 17:10:20 service2: &Service{ObjectMeta:{test-service  default    0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[app:test-app] map[] [] [] []},Spec:ServiceSpec{Ports:[]ServicePort{ServicePort{Name:http,Protocol:,Port:80,TargetPort:{0 8080 },NodePort:0,AppProtocol:nil,},},Selector:map[string]string{app: test-app,},ClusterIP:,Type:,ExternalIPs:[],SessionAffinity:,LoadBalancerIP:,LoadBalancerSourceRanges:[],ExternalName:,ExternalTrafficPolicy:,HealthCheckNodePort:0,PublishNotReadyAddresses:false,SessionAffinityConfig:nil,IPFamilyPolicy:nil,ClusterIPs:[],IPFamilies:[],AllocateLoadBalancerNodePorts:nil,LoadBalancerClass:nil,InternalTrafficPolicy:nil,},Status:ServiceStatus{LoadBalancer:LoadBalancerStatus{Ingress:[]LoadBalancerIngress{},},Conditions:[]Condition{},},}

2024/01/31 17:10:20 
2024/01/31 17:10:20 yamlv2:
typemeta:
  kind: ""
  apiversion: ""
objectmeta:
  name: test-service
  generatename: ""
  namespace: default
  selflink: ""
  uid: ""
  resourceversion: ""
  generation: 0
  creationtimestamp: "0001-01-01T00:00:00Z"
  deletiontimestamp: null
  deletiongraceperiodseconds: null
  labels:
    app: test-app
  annotations: {}
  ownerreferences: []
  finalizers: []
  managedfields: []
spec:
  ports:
  - name: http
    protocol: ""
    appprotocol: null
    port: 80
    targetport:
      type: 0
      intval: 8080
      strval: ""
    nodeport: 0
  selector:
    app: test-app
  clusterip: ""
  clusterips: []
  type: ""
  externalips: []
  sessionaffinity: ""
  loadbalancerip: ""
  loadbalancersourceranges: []
  externalname: ""
  externaltrafficpolicy: ""
  healthchecknodeport: 0
  publishnotreadyaddresses: false
  sessionaffinityconfig: null
  ipfamilies: []
  ipfamilypolicy: null
  allocateloadbalancernodeports: null
  loadbalancerclass: null
  internaltrafficpolicy: null
status:
  loadbalancer:
    ingress: []
  conditions: []


2024/01/31 17:10:20 service2: &Service{ObjectMeta:{test-service  default    0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[app:test-app] map[] [] [] []},Spec:ServiceSpec{Ports:[]ServicePort{ServicePort{Name:http,Protocol:,Port:80,TargetPort:{0 8080 },NodePort:0,AppProtocol:nil,},},Selector:map[string]string{app: test-app,},ClusterIP:,Type:,ExternalIPs:[],SessionAffinity:,LoadBalancerIP:,LoadBalancerSourceRanges:[],ExternalName:,ExternalTrafficPolicy:,HealthCheckNodePort:0,PublishNotReadyAddresses:false,SessionAffinityConfig:nil,IPFamilyPolicy:nil,ClusterIPs:[],IPFamilies:[],AllocateLoadBalancerNodePorts:nil,LoadBalancerClass:nil,InternalTrafficPolicy:nil,},Status:ServiceStatus{LoadBalancer:LoadBalancerStatus{Ingress:[]LoadBalancerIngress{},},Conditions:[]Condition{},},}

2024/01/31 17:10:20 
2024/01/31 17:10:20 yaml:
metadata:
  creationTimestamp: null
  labels:
    app: test-app
  name: test-service
  namespace: default
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: test-app
status:
  loadBalancer: {}
2024/01/31 17:10:20 service2: &Service{ObjectMeta:{test-service  default    0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[app:test-app] map[] [] [] []},Spec:ServiceSpec{Ports:[]ServicePort{ServicePort{Name:http,Protocol:,Port:80,TargetPort:{0 8080 },NodePort:0,AppProtocol:nil,},},Selector:map[string]string{app: test-app,},ClusterIP:,Type:,ExternalIPs:[],SessionAffinity:,LoadBalancerIP:,LoadBalancerSourceRanges:[],ExternalName:,ExternalTrafficPolicy:,HealthCheckNodePort:0,PublishNotReadyAddresses:false,SessionAffinityConfig:nil,IPFamilyPolicy:nil,ClusterIPs:[],IPFamilies:[],AllocateLoadBalancerNodePorts:nil,LoadBalancerClass:nil,InternalTrafficPolicy:nil,},Status:ServiceStatus{LoadBalancer:LoadBalancerStatus{Ingress:[]LoadBalancerIngress{},},Conditions:[]Condition{},},}

2024/01/31 17:10:20 

02-01 08:43