KubernetesのPod内からObjectを生成する

kubernetes便利ですよね。

golangでkubernetesを操作するときは[client-go](https://github.com/kubernetes/client-go)という便利なライブラリがあるので、基本的にはこれを見れば完結します。
しかし、最近CRD(Custom Resource Definition)という自作Objectを作成したときに色々ハマったので、記事として残しておきます。

### Objectとは

要はDeploymentとかServiceとかのことです。
今回は簡単のために、Jobを使います。
適宜自分が使いたいものに変えてください。

## CRDを使わない場合

１つ目の方法は[client-go](https://github.com/kubernetes/client-go)を使うものです。
client-goは名前そのままでkubernetesと通信をするためのクライアントのライブラリです。
以下のコードは簡単なJobを起動するためのコードです。

```go
package main

import (
	"log"

	batchv1 "k8s.io/api/batch/v1"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

func main() {
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	jobClient := clientset.BatchV1().Jobs("default")

	job := &batchv1.Job{
		// 省略...
	}

	job, err = jobClient.Create(job)
	if err != nil {
		log.Printf("Failed to create job: %v", err)
	}
}
```

この方法の欠点としては `k8s.io/client-go/kubernetes`に存在しているものしか作ることは出来ません。(DeploymentとかJobとか)
なので、もしCRD(Custom Resource Definition)を使う場合は上の方法では作成することは出来ないようです。(* 後ろの方にCRDでも実装する方法を書いています。)
その場合は以下の方法で作成してみてください。

## CRDを使う場合

こっちもclient-goは使ってはいるのですが、メインは`controller-runtime/pkg/client`です。

```go
package main

import (
	"context"
	"fmt"
	"log"

	batchv1 "k8s.io/api/batch/v1"
	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/rest"
	clientPkg "sigs.k8s.io/controller-runtime/pkg/client"
)

func main() {
	// Kubernetesの設定
	config, err := rest.InClusterConfig()
	if err != nil {
		panic(err.Error())
	}

	scheme := runtime.NewScheme()
	batchv1.AddToScheme(scheme)
	option := clientPkg.Options{
		Scheme: scheme,
	}

	client, err := clientPkg.New(config, option)
	if err != nil {
		fmt.Println(err)
		return
	}

	job := &batchv1.Job{
		// 省略...
	}

	ctx := context.Background()

	err = client.Create(ctx, job)
	if err != nil {
		log.Printf("Failed to create job %v\n", err)
	}
}
```

気をつけるポイントとしては、

```go
batchv1.AddToScheme(scheme)
```

の部分です。ここを適宜CRDのディレクトリのSchemeとして指定すると良いです。

#### ちなみにCRDを使って最初の方法で実装出来るらしいです。

[参考記事](https://www.martin-helmich.de/en/blog/kubernetes-crd-client.html)

上の記事はちゃんと見たわけでは無いですが、自分でClientを自作して呼び出している感じですね。

## 地味にハマったポイント

今回は `client-go`のv11.0.0を使っていたんですが、以下のようなエラーが結構出てきました。
ググればすぐに解決策が出てくるのですが、念の為ここにも書いておきます。

```
# k8s.io/client-go/rest
../pkg/mod/k8s.io/client-go@v11.0.0+incompatible/rest/request.go:598:31: not enough arguments in call to watch.NewStreamWatcher
	have (*versioned.Decoder)
	want (watch.Decoder, watch.Reporter)
```

Go modulesを使っている場合は以下のようになると思います。
引用元: [client-go/INSTALL.md](https://github.com/kubernetes/client-go/blob/master/INSTALL.md#go-modules)

```bash
$ go get k8s.io/client-go@v11.0.0              # replace v11.0.0 with the required version (or use kubernetes-1.x.y tags if desired)
$ go get k8s.io/api@kubernetes-1.14.0          # replace kubernetes-1.14.0 with the required version
$ go get k8s.io/apimachinery@kubernetes-1.14.0 # replace kubernetes-1.14.0 with the required version
```

## 実装したリポジトリ

https://github.com/kitagry/kube-test