GoでKubernetesまわりのツールを作るときにYAMLを読んでKubernetesオブジェクトにしたいということがあったので調べました。

コードはGistにアップしました。
https://gist.github.com/nownabe/4345d9b68f323ba30905c9dfe3460006

KubernetesのオブジェクトというのはKubernetesのソースで定義されているstructのことです。こんなやつ。

// A namespace provides a scope for Names.
// Use of multiple namespaces is optional
type Namespace struct {
    metav1.TypeMeta
    // +optional
    metav1.ObjectMeta

    // Spec defines the behavior of the Namespace.
    // +optional
    Spec NamespaceSpec

    // Status describes the current status of a Namespace
    // +optional
    Status NamespaceStatus
}

https://godoc.org/k8s.io/api/core/v1#Namespace

YAMLはこんなやつですね。

apiVersion: v1
kind: Namespace
metadata:
  name: my-namespace

このYAMLを上のNamespace structによろしくDeserializeする方法です。

まずはDeserializerを手に入れます。

import (
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/serializer"
)

func main() {
    // SerializeとDeserializeの方法を定義するやつ
    // https://godoc.org/k8s.io/apimachinery/pkg/runtime#Scheme
    scheme := runtime.NewScheme()

    // Deserializerとかをくれるやつ
    // https://godoc.org/k8s.io/apimachinery/pkg/runtime/serializer#CodecFactory
    codecFactory := serializer.NewCodecFactory(scheme)

    // Deserializer
    // https://godoc.org/k8s.io/apimachinery/pkg/runtime#Decoder
    deserializer := codecFactory.UniversalDeserializer()
}

UniversalDeserializer()はJSONとかYAMLとかを自動で認識してDeserializeしてくれるやつをくれます。これを使っておけば問題ないと思いますが、それぞれのシリアライズ形式専用のDeserializerも簡単に手に入ります。1

いずれにしてもruntime.Decoderというinterfaceのインスタンスが得られます。

type Decoder interface {
    Decode(data []byte, defaults *schema.GroupVersionKind, into Object) (Object, *schema.GroupVersionKind, error)
}

https://godoc.org/k8s.io/apimachinery/pkg/runtime#Decoder

DecodeメソッドでDeserializeするわけですが、それぞれの引数はこんな感じになってます。

  • data []byte: Deserializeしたいデータ。YAMLとか
  • defaults *schema.GroupVersionKind: nilでよい。よくわからないときに使われるデフォルト値。Group、Version、Kindそれぞれの文字列が入る構造体
  • into Object: DeserializeしたいオブジェクトNamespaceとかのstructのポインタがこのinterfaceを実装している。

例えばNamespaceのYAMLをNamespace structにDeserializeするのはこんな感じになります。

package main

import (
        "fmt"
        "io/ioutil"

        corev1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/runtime/serializer"
)

func main() {
        scheme := runtime.NewScheme()
        codecFactory := serializer.NewCodecFactory(scheme)
        deserializer := codecFactory.UniversalDeserializer()

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

        object, _, err := deserializer.Decode(yaml, nil, &corev1.Namespace{})
        if err != nil {
                panic(err)
        }
        namespace := object.(*corev1.Namespace)
        fmt.Printf("Name: %s\n", namespace.ObjectMeta.GetName())
}

他のオブジェクトも同様にできます。GistのコードにはDeploymentのサンプルもあります。

オブジェクトについてはk8s.io/apiのgodocにあります。


  1. この辺とかこの辺とか