【ChatOps】golang + slack + ansibleで、プロビジョニングからデプロイまで。

# はじめに

デプロイツールとして、`anisble-playbook`を利用して
slackからデプロイできるようにした話です。

# 背景

1. stg/prd環境のEC2 provisioning -> middleware install -> deployを全て、`ansible-playbook`で実施している。
2. 開発チームの方々にも`ansible-playbook`のコマンドを実行してもらっていた。
3. 各種環境のデプロイはEC2上のサーバーから実施している。
4. スプレッドシートにて、`ansible-playbook`の各種サーバー毎/task毎にコマンドを記載して、実行してもらっていた。
5. 今までインフラとして、AWSやログ分析・監視基盤の構築をしていたので、自分自身が全くと言っていいレベルでプログラムのスキルがなかったので、勉強がてらgolangやりたかった。

# 課題・懸念

解決したい課題・懸念は、以下3点。

1. 実行コマンドをコピペミスで、誤実行してしまうリスクがある。
2. 「誰が」実行しているかわからない。
3. サーバーにそもそも入らなければ、デプロイできない。

# 作ったもの

| 対象 | URL |
|:-----------|:------------|
| go-slack-ansible | https://github.com/jkkitakita/go-slack-ansible |

![go-slack-ansible-trim.gif](https://qiita-image-store.s3.amazonaws.com/0/95804/3c195fb7-7754-8f29-ed3f-cdd93281a2f4.gif)


# 前提

Mercariの @deeeet さんが書いた
[GolangでSlack Interactive Messageを使ったBotを書く](https://tech.mercari.com/entry/2017/05/23/095500)
の記事を参考にさせていただきながら、作成しました。

# ディレクトリ構成

```
ansible
　├ inventory
　│　　├ stg
　│　　│　├ group_vars
　│　　│　└ host
　│　　└ prd
　│　　　├ group_vars
　│　　　└ host
go-slack-ansible
　├ ansible
　│　　├ config
　│　　│　└ config.go            // ansible-playbookのconfig関連
　│　　├ ansible_playbook.go     // ansible-playbookで、provisioningとdeploy実行
　│　　├ branch.go               // デプロイするブランチを指定
　│　　├ config.yaml             // hostとrepoの対応一覧 & ansibleのtagの一覧
　│　　├ const.go                // 定数
　│　　├ inventory_scanner.go    // ansibleのinventoryを読み取って、対象ホストを取得
　│　　├ tag.go                  // 指定するtagを取得する
　│　　└ terminate.go            // ansible-playbookでサーバーのterminateを実行
　├ cmd
　│　　└ bot
　│　　　├ handler.go            //
　│　　　├ main.go               // Slack Interactive Messages
　│　　　└ slack.go              // 
　├ config
　│　　　└ config.go             // logger等の設定
　├ logger
　│　　└ logger.go               // zapにて、logging
　├ scripts
　│　　└ restart.sh              // 再起動スクリプト
　├ .env　　　　　　　　　　　　　 // slack BotID Channel,GitHub token etc...
　├ go.mod
　├ go.sum
　└ Makefile
```

# 工夫したところ
## 前のメッセージの状況を残したい。

上記のgifを見てもらえればわかる通り
選択したhostやtagが選択後にメッセージに表示させています。
このように既存のメッセージの状態を上書きするためには
Slack Interactive Messageで、`replace_original`を`true`にする必要があリます。
>Replacing the original message
https://api.slack.com/interactive-messages#building_workflows

これをしないと

![go-slack-ansible-replace.gif](https://qiita-image-store.s3.amazonaws.com/0/95804/1550a776-3863-f729-e8fb-1c0a2f2767f8.gif)

このようになり、無駄に投稿されてしまう。
また、前の投稿を再度編集、選択できてしまいます。
`nlopes/slack`を使った今回の場合は

```
originalMessage.ReplaceOriginal = true
```
https://github.com/jkkitakita/go-slack-ansible/blob/5a7f858e73/cmd/go-slack-ansible/handler.go#L109
として、`replace_original`を`true`に毎回上書きをすることで、できるようになりました。

## zapのloggerにlogvel,caller,traceを追加する

botと全然関係ないけど（笑）
試しにloggerの勉強をしようと思って、zapを入れて見たものの
zapのドキュメントが少なく、loggerを仕込むにも一苦労したので、めも。
zap.New時に、↓のように渡してあげると、

```golang
return zap.New(
	zapcore.NewCore(enc, w, zapConfig.Level),
	zap.AddCaller(),
	zap.AddStacktrace(zapcore.WarnLevel),
)
```
https://github.com/jkkitakita/go-slack-ansible/blob/5a7f858e736347ae5879fad4e68551306786dd2e/logger/logger.go#L43-L47

```json
ex. INFO
{"severity":"INFO","timestamp":"2018-12-14T22:24:08.972+0900","caller":"go-slack-ansible/main.go:58","message":"[INFO] Start slack event listening"}

ex. WARN
{"severity":"WARN","timestamp":"2018-12-14T22:26:27.968+0900","caller":"go-slack-ansible/slack.go:71","message":"[WARN] Invalid bot: Jun Kitamura [10:22 PM]","trace":"main.(*SlackListener).handleMessageEvent\n\t/Users/jun.kitamura/go/src/go-slack-ansible/cmd/go-slack-ansible/slack.go:71\nmain.(*SlackListener).ListenAndResponse\n\t/Users/jun.kitamura/go/src/go-slack-ansible/cmd/go-slack-ansible/slack.go:37"}
```

# 今後の課題

1. そもそもtest書いてない。。すいません。。
2. methodを使って書きたかったが、全て普通の関数funcになってしまった。。
3. 構造体、interfaceまだ勉強中です。。

# さいごに

golang初挑戦でした。
色々な参考書や記事をツギハギでやりました。

コードも、記事の構成もツッコミどころ満載で、どこから突っ込んだらいいかわからないとは思いますが
「ここは違うだろ！」「ここはこうした方がいいよ！」
とかあれば、いつでも、コメントください。非常に喜びます。