GolangでHelmのAPIを叩く

# 概要

デフォルトインストール設定のHelmで作成されたリリースを削除したかったので調べました。
いい感じのサンプルがなかったので纏めて公開します。
# 参考
基本的には[テストコード](https://github.com/helm/helm/blob/master/pkg/helm/helm_test.go)を参考に実装していきます。
記事ではkubernetesのAPIも利用するためこちらのexampleを参考にしてください。
[kubernetesのクラスタ内からAPIを叩く場合](https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration)
[kubernetesのクラスタ外からAPIを叩く場合](https://github.com/kubernetes/client-go/tree/master/examples/out-of-cluster-client-configuration)
# 詳細
## 実装

テストコードを参考にHelmのクライアントを実装していきます。
本記事の実装ではkubernetes内からk8sのAPIを叩いておりますので、詳細については[こちら](https://github.com/kubernetes/client-go/tree/master/examples/in-cluster-client-configuration)をご参考ください。
kubernetes外からのリクエストについては[GitHub](https://github.com/masahiro331/helm_sample/tree/master/out-of-cluster-config)にて公開しています。
下記のコードはk8sクライアントとHelmクライアントを作成するコードになります。

```main.go
	kubeConfig, err := rest.InClusterConfig()
	if err != nil {
		log.Print("Failed to get kubernetes in cluster config")
		return
	}
	kubeClientSet, err := kubernetes.NewForConfig(kubeConfig)
	if err != nil {
		log.Printf("failed to construct kubernetes client set from config: %+v", err)
		return
	}

	tillerTunnel, err := portforwarder.New("kube-system", kubeClientSet, kubeConfig)
	if err != nil {
		log.Print(err, "failed to portforward")
		return
	}
	tillerHost = fmt.Sprintf("127.0.0.1:%d", tillerTunnel.Local)

	helmOpts := []helm.Option{
		helm.Host(tillerHost),
		helm.ConnectTimeout(10), // デフォルトのタイムアウト値が0秒なため必須
	}
	helmClient := helm.NewClient(helmOpts...)
```

いくつか注意する必要がありますので解説していきます。  
tillerのインストール時にtiller-tlsの設定をしていない場合(デフォルトのインストール設定)は`~/.helm/`配下に証明書が存在しないためkubeconfigの証明書を利用する必要があります。
kubeconfigの証明書を利用するためtillerとの通信はkubeconfigで構築した通信を利用します。  
そのため下記のコードを利用してkubernetesのPortforwardAPIを叩き、ローカルのランダムなポートとkubernetesの間で接続を確立します。


```main.go
	tillerTunnel, err := portforwarder.New("kube-system", kubeClientSet, kubeConfig)
	if err != nil {
		log.Print(err, "failed to portforward")
		return
	}
	tillerHost = fmt.Sprintf("127.0.0.1:%d", tillerTunnel.Local)
```


このようにHelmのClientが実装できましたら実際にAPIを叩いていきます。
下記では任意のReleaseNameが存在することを確認し、削除しています。

```main.go
	listOpts := []helm.ReleaseListOption{
		helm.ReleaseListFilter("hoge-release"),
	}

	listRelease, err := helmClient.ListReleases(listOpts...)
	if err != nil {
		log.Print(err, "failed to get ReleaseName")
		return
	}

	if len(listRelease) == 0 {
		log.Print("hoge-release not found")
		return
		}

	deleteOpts := []helm.DeleteOption{
		helm.DeleteDisableHooks(false),
		helm.DeletePurge(true),
		helm.DeleteTimeout(10), // デフォルトのタイムアウト値が0秒なため必須
	}

	_, err = helmClient.DeleteRelease("hoge-release", deleteOpts...)
	if err != nil {
		log.Print("failed to delete")
		return
	}


```

ここまでが実装です。完全なコードについては[GitHub](https://github.com/masahiro331/helm_sample)にて公開しています。

次にビルドをします。私はGo初心者のためかなりハマりました。

## ビルド
ビルドを行うのにかなりの時間を費やしたので、説明したいのですが非常にわかりやすい記事が公開されているので此方を参考にしてください。　GithubのREADMEにも簡単なビルド方法を記載します。

[参考記事](https://qiita.com/knqyf263/items/ef1a8714eb32b4753950#%E3%83%93%E3%83%AB%E3%83%89)

## RBACの対策

RBACについての説明も省略します。

今回のサンプルコードに関しては `pods/portforward` の権限を付与すれば動作するかと思います。
私の環境では面倒なので、cluster-adminのroleを付与してます。



## 利用用途

私が参加しているプロジェクトでは、k8sのアプリケーション管理にHelmを利用しておりCI/CDのツールとしてSlack Botなども利用しています。
Bot自体はGoで書かれており、任意のWebhookやSlashコマンドを受け取り、Helmリリースを操作したかったため、今回の検証を行いました。


# 感想

今回の検証をするにあたり、[Helmのバグ](https://github.com/helm/helm/pull/4777
)を発見し修正のプルリクを出してContributeすることができたので良かったかなと思います。

サンプルコードなども公開されていないためついでにコミットしようかなと思います。