これは ZOZO Advent Calendar 2023 カレンダー Vol.5 の 3日目の記事です。
本記事ではYAML形式のKubernetesマニフェストを読み込み、対応する型を持ったGo言語のオブジェクトにデシリアライズする方法を説明します。
k8s.io/apimachinery
モジュールが提供するyaml
パッケージの機能を利用することでこれを実現できます。
実装例と解説
以下は、YAML形式で書かれたPodのマニフェストファイルを読み取り、core/v1
のPod
型を持つオブジェクトにデシリアライズし、client-go
を使ってGo言語のオブジェクトからKubernetesのPodオブジェクトを作成する例になります。この例で作成するPodは次の2種類になります。
- 元々のマニフェストのフィールド値を持つPod
- 1のPod名を変更した新しいPod
package main
import (
"context"
"flag"
"fmt"
"os"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
)
var kubeconfig string
func main() {
// YAMLマニフェストの読み込み
path := "sample_pod.yaml"
data, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// シリアライザの作成
jsonSerializer := json.NewSerializerWithOptions(
json.SimpleMetaFactory{},
nil,
scheme.Scheme,
json.SerializerOptions{},
)
yamlSerializer := yaml.NewDecodingSerializer(jsonSerializer)
// 読み込んだYAMLマニフェストのバイト列をcorev1.Pod型のオブジェクトにデシリアライズ
var original corev1.Pod
if _, _, err := yamlSerializer.Decode(data, nil, &original); err != nil {
fmt.Println(err)
os.Exit(1)
}
// Podを作成するクライアントの作成
flag.StringVar(&kubeconfig, "kubeconfig", "", "kubeconfig path")
flag.Parse()
config, _ := clientcmd.BuildConfigFromFlags("", kubeconfig)
clientset, _ := kubernetes.NewForConfig(config)
// オブジェクトからsample-original Podを作成
_, err = clientset.CoreV1().Pods("default").Create(context.TODO(), &original, metav1.CreateOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// sample-originalオブジェクトのPod名を変更した新規オブジェクトを作成
updated := original.DeepCopy()
updated.ObjectMeta.Name = "sample-updated"
// Pod名を変更した新規オブジェクトからsample-original Podを作成
_, err = clientset.CoreV1().Pods("default").Create(context.TODO(), updated, metav1.CreateOptions{})
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
読み込み対象のYAMLマニフェストは以下になります。
apiVersion: v1
kind: Pod
metadata:
name: original-sample
namespace: default
spec:
containers:
- name: sleep-container
image: alpine
command: ["sleep", "3600"]
以下ではdeserialize.go
の実装について説明します。
[]bytes
→ corev1.Pod
へのデシリアライズ
byte列 → corev1.Pod型のオブジェクトへのデシリアライズはyamlSerializer
オブジェクトのDecode
メソッドの中で行われています。
yamlSerializer
型のDecode
メソッドでは、引数で受け取ったYAMLをJSONに変換し、apimachinery/pkg/runtime/serializer/json
パッケージのSerializer
型のDecode
メソッドを呼び出しています。
Decode部分の実装が気になる方は、k8s.io/apimachinery/pkg/runtime/serializer/json
パッケージのDecode
メソッドを参照してみてください。
yaml.NewDecodingSerializer
関数を呼んでyamlSerializer
を初期化する際に、引数にjsonSerializer
を渡していることがわかります。
またjsonSerializer
を初期化する際には、k8s.io/apimachinery/pkg/runtime/serializer/json
パッケージのNewSerializerWithOptions
関数を呼び出しています。
こちらの関数の実装は以下のようになっています。
// NewSerializerWithOptions creates a JSON/YAML serializer that handles encoding versioned objects into the proper JSON/YAML
// form. If typer is not nil, the object has the group, version, and kind fields set. Options are copied into the Serializer
// and are immutable.
func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer {
return &Serializer{
meta: meta,
creater: creater,
typer: typer,
options: options,
identifier: identifier(options),
}
}
関数のドキュメントにも記載がある通り、3つ目のtyper
引数にnil
でない値を渡すことで、作成されるオブジェクトがGroup-Version-Kind
のフィールドを持ちます。
こちらにはデシリアライズ先のリソースのscheme
を指定しています。
deserialize.go
では、corev1.Pod
型のscheme
を持ったk8s.io/client-go/kubernetes/scheme
パッケージのScheme
変数を渡しています。
Scheme
変数はk8s.io/apimachinery/pkg/runtime/scheme
パッケージのScheme
型を持ちますが、こちらの型はNewSerializerWithOptions
関数のtyper
引数が持つべきruntime.ObjectTyper
インターフェースを満たしています。
以上が、YAML形式のKubernetesマニフェスト → corev1.Pod型のオブジェクトへの変換部分の説明になります。
GoのオブジェクトからKubernetesオブジェクトを作成
deserialize.go
の// Podを作成するクライアントの作成
以降の処理では、client-go
を使ってマニフェストから作成したオブジェクトからKubernetesのPodオブジェクトを作成しています。
またマニフェストから作成したオブジェクトを元に別オブジェクトを作成し、一部フィールドの値を変更することで元々のマニフェストとは別のKubernetesのPodオブジェクトも作成しています。
client-go
については、別記事のclient-go・api・apimachineryの機能と関係性で触れているので参考にしてみてください。
実行結果
deserialize.go
の実行結果は以下になっており、想定したKubernetes Podのオブジェクトが作成されていることがわかります。
go run deserialize.go --kubeconfig $HOME/.kube/config
k get po
NAME READY STATUS RESTARTS AGE
sample-original 1/1 Running 0 47m
sample-updated 1/1 Running 0 47m
まとめ
本記事ではYAML形式のKubernetesマニフェストを読み込み、対応する型を持ったGo言語のオブジェクトにデシリアライズする方法を紹介しました。
k8s.io/apimachinery
モジュールが提供するyaml
パッケージの機能を利用し、次の手順によりこれを実現できました。
- デシリアライズ先の型情報を持った
scheme
を用意 - 用意した
scheme
を使って、jsonSerializer
を作成 - 作成した
jsonSerializer
からyamlSerializer
を作成 -
yamlSerializer
のDecode
メソッドに、YAMLマニフェストのバイト列とデシリアライズ先のオブジェクトのポインタを指定して実行
入力がバイト列なので、マニフェストファイルに限らず、requestのbodyをデシリアライズする際など登場する場面は多そうかと思います。