client-go 全系列之四 ——《go 反序列化》

1 前言

  本节介绍如何反序列化 yaml 文件,并将其转换为 K8S Deployment 对象。

备注:有关 GO SDK 安装、GO 项目配置请查看本系列前面的文章,本文不再赘述。


2 序列化与反序列化

  1907年3月12日,一个叫斯坦因的英国人来到敦煌,随后的几天里,他通过小恩小惠取得了守护人王道士的信任,并在3月16日进入了千佛洞。

  那一天,一个震惊世界的文化奇迹从历史的尘封中露出真貌,这——便是敦煌。

  那里曾经风雨西窗,那里曾经衰草斜阳。

  斯坦因以极低的价格买走了许多珍贵的文物,其中不乏精美的壁画。因为有些壁画实在过于庞大,为了方便运输,斯坦因竟然“肢解”了它们,尽管这些壁画在到达英国后经过了重新拼装,但却带来了不可估量的艺术损失。

  斯坦因将壁画切割成碎片,再从碎片恢复成原样的过程,就是对象的序列化与反序列化过程。

  序列化和反序列化的本质是实现对象数据类型转换(比如对象转换成切片),而对象类型转换的目的是方便数据的存储和传输。


3 运行环境

  本系列文章使用的环境配置如下(不要求读者完全匹配,可根据自己实际情况酌情处理):

  • K8s 版本 : 1.19.0
  • Docker 版本:20.10.8
  • CentOS 版本:7.7.1908
  • Go SDK 版本:go1.16.6 linux/amd64

4 项目代码

  进入 GO 项目根目录:

# cd $GOPATH/src

  新建 k8s-unmarshal 目录,并在目录下创建 busybox.yaml 文件:

# mkdir k8s-unmarshal
# cd k8s-unmarshal
# touch busybox.yaml

  编辑 busybox.yaml,在文件中创建一个 deployment 声明:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: busybox
  labels:
    app: busybox
spec:
  replicas: 1
  selector:
    matchLabels:
      app: busybox
  template:
    metadata:
      name: busybox
      labels:
        app: busybox
    spec:
      containers:
      - name: busybox
        image: busybox
        imagePullPolicy: IfNotPresent
        command: [ "/bin/sh", "-c", "sleep 3600" ]

  其次创建 main.go 文件:

# touch main.go

  编辑 main.go 文件,添加如下代码:

package main

import (
    json2 "encoding/json"
    "fmt"
    "io/ioutil"

    v1 "k8s.io/api/apps/v1"
    yaml2 "k8s.io/apimachinery/pkg/util/yaml"
)

func main() {
    yaml, err := ioutil.ReadFile("./busybox.yaml") 
    if err != nil {
        panic(err)
    }   

    json, err := yaml2.ToJSON(yaml) 
    if err != nil {
        panic(err)
    }   

    deployment := v1.Deployment{}
    err = json2.Unmarshal(json, &deployment)
    if err != nil {
        panic(err)
    }   

    fmt.Println(deployment)
}

5 运行项目

  如果想运行成功上面的代码样例,需执行如下两个步骤:

  • 创建 go.mod
  • 运行 main.go

5.1 创建 go.mod

  go mod 在 GO v1.11 版本引入,在 v1.12 版本基本稳定,到 v1.13 版本的时候默认是打开的,可以使用如下命令查看:

[root@k8s-master-1 k8s-unmarsha]# go env | grep -i GO111MODULE
GO111MODULE="on"

  执行下列命令创建 go.mod 文件:

[root@k8s-master-1 k8s-unmarsha]# go mod init
go: creating new go.mod: module k8s-unmarsha
go: to add module requirements and sums:
	go mod tidy

  如果执行结果如上所示,还需要继续执行下面指令(备注,如果指令执行失败,大概率是依赖包下不到,可参考本系列第一篇文章配置好下载包代理):

[root@k8s-master-1 k8s-unmarshal]# go mod tidy
go: finding module for package k8s.io/apimachinery/pkg/util/yaml
go: finding module for package k8s.io/api/apps/v1
go: found k8s.io/api/apps/v1 in k8s.io/api v0.22.2
go: found k8s.io/apimachinery/pkg/util/yaml in k8s.io/apimachinery v0.22.2

  执行完毕后, $GOPATH/src/k8s-unmarsha 目录结构如下:

[root@k8s-master-1 k8s-unmarshal]# tree
.
├── busybox.yaml
├── go.mod
├── go.sum
└── main.go

0 directories, 4 files

5.2 运行 main.go

  运行 main.go:

[root@k8s-master-1 k8s-unmarshal]# go run main.go
{{Deployment apps/v1} {busybox      0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[app:busybox] map[] [] []  []} {0xc000293128 &LabelSelector{MatchLabels:map[string]string{app: busybox,},MatchExpressions:[]LabelSelectorRequirement{},} {{busybox      0 0001-01-01 00:00:00 +0000 UTC <nil> <nil> map[app:busybox] map[] [] []  []} {[] [] [{busybox busybox [/bin/sh -c sleep 3600] []  [] [] [] {map[] map[]} [] [] nil nil nil nil   IfNotPresent nil false false false}] []  <nil> <nil>  map[]   <nil>  false false false <nil> nil []   nil  [] []  <nil> nil [] <nil> <nil> <nil> map[] [] <nil>}} { nil} 0 <nil> false <nil>} {0 0 0 0 0 0 [] <nil>}}

  如果命令输出上述结果则表示程序执行成功。

  自此,成功将 yaml 文件反序列化 为 K8S deployment 对象。

5.3 代码解释

  下面简单介绍代码逻辑。

5.3.1 读取 yaml 格式文件

  下面代码作用是读取当前目录下 busybox.yaml 资源文件,并以字节切片的数据类型保存在变量 yaml 中,如果读取失败直接报错退出。

yaml, err := ioutil.ReadFile("./busybox.yaml") 
if err != nil {
    panic(err)
}  

5.3.2 转换成 json 切片格式

  下面代码作用是读取上面生成的 yaml 字节切片,并将其转换为 json 格式的字节切片数据类型。读者如果觉得费解,可以简单理解为单纯的数据类型转换即可。

json, err := yaml2.ToJSON(yaml) 
if err != nil {
    panic(err)
}   

5.3.3 反序列化字节切片

  下面代码根据上面创建好的 json 字节切片,再利用 encoding/json 库的 Unmarshal 方法将字节切片类型反序列成 K8S deployment 对象。

deployment := v1.Deployment{}
err = json2.Unmarshal(json, &deployment)
if err != nil {
  panic(err)
}     

6 总结

  本小节介绍了如何将 yaml 文件反序列化为 deployment 对象,下节笔者将会带领读者深入到 K8S 源码,看看 Deployment 对象的内部结构组成。更多精彩,敬请期待。