GKEへレシピごとアプリケーションをデプロイするSlackボットをClojureで作ったはなし

![デプロイの様子](https://qiita-image-store.s3.amazonaws.com/0/31405/47372294-9865-4a7b-88d2-5bed558875da.png)

# はじめに

この記事は[Clojure Advent Calendar 2017](https://qiita.com/advent-calendar/2017/clojure) 18日目の記事です。
前回は 223kazukiさんの[ClojureでGraphQLサーバを立てる](https://qiita.com/223kazuki/items/ba4ba84e2da1daea3b52)でした。

今回は、Clojureから様々なJava向けライブラリを使って実装したデプロイ用Slackbot「puppeteer」について~~使ってもらうために宣伝~~実装時に注意した事などを書こうと思います。

また、Clojure Advent Calendar 2017へはこれが初投稿なのですが、
[eureka Engineering Advent Calendar 2017](https://qiita.com/advent-calendar/2017/eureka-engineering)にて２つClojure関係の記事を投稿しておりますので、興味があれば是非読んでみてください！

- [社内システムでのClojureScript+re-frame活用事例](https://developers.eure.jp/tech/how-to-use-clojurescript-on-our-office-system/)
- [GKE+CircleCI 2.0で継続的デプロイ可能なアプリケーションをシュッと作る](https://developers.eure.jp/tech/make_countinous_deployment_with_gke_and_circleci2/)

それでは本題へ入ります。

# 作成したSlackbot「puppeteer」について

Slackbot「puppeteer」は、Slackで受け取ったレポジトリのユーザー名・レポジトリ名・ブランチ名のメンションをもとにその動作環境をGKEクラスタ上へ構築し、KubernetesのIngressやGoogle Cloud DNSを使用して構築した環境へアクセス出来るURLを自動で発行してくれるBotです。簡略化していますが、大体以下の図のような動作をします。

![puppeteeer.png](https://qiita-image-store.s3.amazonaws.com/0/31405/06fa7116-e120-0c3a-2140-b2e0dfc18bbd.png)

また、簡略化のために省略していますが、ビルド＆デプロイのJob管理のためにAWSのDynamDB、ビルドの完了通知を受け取る為にCloud Pub/Subも使用しています。
詳しい使用方法については[README](https://github.com/boxp/puppeteer)をご参照ください。

# puppeteerの技術スタック

puppeteerでは以下のライブラリが使われています。

- environ: 環境変数をClojureで便利に使用するためのライブラリ
- com.stuartsierra/component: アプリケーション全体の状態管理と、DIコンテナ相当の役割をするClojure向けライブラリ
- org.julienxx/clj-slack: Slack APIをClojureで便利に使用するためのライブラリ
- slack-rtm: SlackからのリアルタイムメッセージをClojureで便利に使用するためのライブラリ
- cheshire: ClojureからJSONへのエンコード/デコードを行うライブラリ
- circleci/clj-yaml: Clojureからyamlへのエンコード/デコードを行うライブラリ
- tentacles: GitHub APIをClojureで便利に使用するためのライブラリ
- com.taoensso/faraday: DynamoDBをClojureから便利に使用するためのライブラリ
- io.fabric8/kubernetes-client: Kubernetes APIの公式Javaライブラリ
- com.google.cloud/google-cloud-pubsub: Google Cloud Pub/Subの公式Javaライブラリ
- com.google.apis/google-api-services-cloudbuild: Google Cloud Container Builderの公式Javaライブラリ
- com.google.apis/google-api-services-dns: Google Cloud DNSの公式Javaライブラリ

簡単のために出来るだけClojure向けのライブラリを使用したかったのですが、
AWSと比べるとGCPのClojure向けライブラリは殆ど存在せず、半分しぶしぶと言った形でJava向けライブラリを使用しました。

# puppeteerのアーキテクチャ

[puppeteer](https://github.com/boxp/puppeteer)の開発者は現状私ひとりですが、
もしかしたらClojureに馴染みのない社内の方にも触って貰えるかも知れないと言った期待から、社内でよく用いられているアーキテクチャに近しいアーキテクチャを決めて開発を始めました。

以下のフォルダ構成のように、大まかに `app` ・ `domain` ・ `infra` の三層構造になっていて、 `infra` 層がKubernetesやGoogle Cloud Container Builder等の外部サービスとの連携を、 `domain` 層がビジネスロジックを、 `app` 層がユーザーとの連携を執り行っています。

```
├── app
│   └── slackbot
├── domain
│   ├── entity
│   └── usecase
└── infra
    ├── client
    └── repository
```

# com.stuartsierra/componentの活用

puppeteerでは、stuartsierra氏のcomponentをアプリケーション全体で使って開発しています。
`app` ・ `domain` ・`infra` 層にあるモジュールはそれぞれ一つずつ必ずComponentを持っていて、アプリケーションの立ち上げ時に他のComponentを注入する事によって利用されます。

例えば、GitHubレポジトリ上にある設定ファイルに関する操作を実装する `ConfigurationRepositoryComponent` はGitHubのAPIのクライアントである `GithubComponent` が注入される事で実装されています。

```clj:puppeteer/src/puppeteer/infra/repository/conf.clj
...

(defn load-conf
  [{:keys [github-client]}
   {:keys [user-name repo-name branch-name]}]
  (some->>
    (github/get-file github-client ;; 注入されたgithub-clientで設定を取得
                     {:user user-name
                      :repo repo-name
                      :path conf-path
                      :ref branch-name})
    Content->Configuration))

(defrecord ConfigurationRepositoryComponent [github-client] ;; github-clientが注入されてくる
  component/Lifecycle
  (start [this]
    (println ";; Starting ConfigurationRepositoryComponent")
    this)
  (stop [this]
    (println ";; Stopping ConfigurationRepositoryComponent")
    this))

...
```

このパターンを全ての層で徹底させているため、アプリケーションの起動・終了が簡単になっています。

# おわりに

かなり早足ですが、puppeteerの実装について振り返りました。
まだまだ開発中のプロジェクトなので、もし興味があればPRやIssueを立てて頂けると泣いて喜びますので何卒よろしくお願い致します :pray: 

それではまたお会いいたしましょう！
次回はyuwki0131さんの記事です、お楽しみに！

2018/07/06 追記: puppeteerはsorcererへ改名されました