Cloud Functions(nodejs)から、Kubernetes APIを呼び出す

# はじめに

GCSの変更通知を[Cloud Functions](https://cloud.google.com/functions/)で受け取り、そこからKubernetes側にとある処理を実行したいな…と思ったときに結構ハマったのでメモです。

私は、社内用のbotを作るときしか、nodejsを触らないバックエンド側のエンジニアです。
暖かい目でコードを見て、厳しいご指摘くれると嬉しいです。

# 結果

下記のような結果となりました。
https://github.com/h-r-k-matsumoto/cloud-function-gke

# ハマったポイント

## Cloud Functionsからではgcloudコマンド使えないから認証できない。

適当に、 `~/.kube/config` を使おうとすると下記のようにエラーになりました。
[サンプル](https://github.com/kubernetes-client/javascript/blob/master/examples/example.js) ですら動きません。

```
{
    "kind": "Status",
    "apiVersion": "v1",
    "metadata": {},
    "status": "Failure",
    "message": "pods is forbidden: User \"system:anonymous\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",
    "reason": "Forbidden",
    "details": {
        "kind": "pods"
    },
    "code": 403
}
```

どうにも `system:anonymous` となっている事から認証されていないっぽいですね。

### 対応

通常は、下記のような定義だと思います。

```json:kubeconfig
    "users": [
      {
        "authProvider": {
          config: {
            "cmd-args": "config config- helper--format = json",
            "cmd-path": "/usr/lib64/google-cloud-sdk/bin/gcloud",
            "expiry": "2019-04-23T07:05:48Z",
            "expiry-key": '{.credential.token_expiry}',
            "token-key": '{.credential.access_token}'
          },
          "name": "gcp"
        }
      }
    ]
```

下記のように、`authProvider` をばっさり消しちゃいます。この部分があると、 `@kubernetes/client-node` ライブラリの、 [cloud_auth.ts](https://github.com/kubernetes-client/javascript/blob/master/src/cloud_auth.ts#L21-L26)が動作してしまうためです。

```json:kubeconfig
    "users": [
      {
      }
    ]
```

さみしいですね。で、変わりに何で認証するか？という部分ですが、
[config.ts#L294](https://github.com/kubernetes-client/javascript/blob/master/src/config.ts#L294)
に、下記の記述があります。

```typescript:config.ts
        if (user.token) {
            token = 'Bearer ' + user.token;
        }
```

そう。自分でtokenを設定してやれば良いわけですね。なので・・・下記のようにtokenを取得します。

```javascript
async function getToken() {
  let authResult = await auth.getClient({ scopes: 'https://www.googleapis.com/auth/cloud-platform' });
  let accessToken = await authResult.getAccessToken().catch((err) => {
    throw err;
  })
  return accessToken.token
}
```

そして、下記のように取得した値をセットしています。

```javascript
    users: [
      {
        user: {
          token: token
        }
      }
    ]
```


# その他

## golangはあきらめたお話
最初は、golangで実装しようとしました。でも、golang 1.11だと、下記問題が発生しました。

https://github.com/kubernetes/kubernetes/issues/76304

```
..\..\..\..\..\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)
```

なんていうgolang初心者にはつらいエラーが・・・。で、master使おうとすると、

```
>go build main.goform\lake\notification>go build main.go
go: finding google.golang.org/api/support/bundler latest
go: finding google.golang.org/api/support latest..\..\..\..\..\pkg\mod\contrib.go.opencensus.io\exporter\ocagent@v0.4.12\ocagent.go:24:2: unknown import path "google.golang.org/api/support/bundler": cannot find moduleproviding package google.golang.org/api/support/bundler
```

とライブラリ関係でエラーが出るので、ちょっと初心者には更に厳しいと思い nodejsで実装する事に変えました。

## このやり方は正しいのかは分からない。

公式サイトや、stackoverflowなどでも探したのですが、Cloud Functionからの呼び出し方法は不明なままでした。
あくまで、これでやったら現時点では上手くいったよ。という内容となります。