LoginSignup
2
0

YAML形式のKubernetesマニフェストをGo言語のオブジェクトにデシリアライズする

Last updated at Posted at 2023-12-02

これは ZOZO Advent Calendar 2023 カレンダー Vol.5 の 3日目の記事です。

本記事ではYAML形式のKubernetesマニフェストを読み込み、対応する型を持ったGo言語のオブジェクトにデシリアライズする方法を説明します。

k8s.io/apimachineryモジュールが提供するyamlパッケージの機能を利用することでこれを実現できます。

実装例と解説

以下は、YAML形式で書かれたPodのマニフェストファイルを読み取り、core/v1Pod型を持つオブジェクトにデシリアライズし、client-goを使ってGo言語のオブジェクトからKubernetesのPodオブジェクトを作成する例になります。この例で作成するPodは次の2種類になります。

  1. 元々のマニフェストのフィールド値を持つPod
  2. 1のPod名を変更した新しいPod
deserialize.go
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マニフェストは以下になります。

sample_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: original-sample
  namespace: default
spec:
  containers:
  - name: sleep-container
    image: alpine
    command: ["sleep", "3600"]

以下ではdeserialize.goの実装について説明します。

[]bytescorev1.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関数を呼び出しています。
こちらの関数の実装は以下のようになっています。

k8s.io/apimachinery/pkg/runtime/selializer/json/json.go
// 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パッケージの機能を利用し、次の手順によりこれを実現できました。

  1. デシリアライズ先の型情報を持ったschemeを用意
  2. 用意したschemeを使って、jsonSerializerを作成
  3. 作成したjsonSerializerからyamlSerializerを作成
  4. yamlSerializerDecodeメソッドに、YAMLマニフェストのバイト列とデシリアライズ先のオブジェクトのポインタを指定して実行

入力がバイト列なので、マニフェストファイルに限らず、requestのbodyをデシリアライズする際など登場する場面は多そうかと思います。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0