Terraform graph を少し見やすく画像化してみました

とりあえず結論を先に書いてしまうと、四角い箱で表現されてしまうノードを画像で置き換える、Graphvizdot コマンドを置き換える形で使用可能なコマンドを作りました。

github.com

実行結果の例

terraform graph | tfdot -ograph.png

f:id:semnil:20200704062738p:plain

作るまでの経緯

  1. terraform graph の結果そのままを図にしたものが難解
    かなり無機質な感じで箱と矢印が配置される事になるので、あまり理解しやすい雰囲気になりません。正直見辛いです。draw.io とかで書き直して欲しくなります。
    f:id:semnil:20200704062256p:plain

  2. Python のコードから良い感じの画像を作れる Diagrams と言うものがあるらしい
    複数の方から教えていただいて、存在はちょっと前から知ってはいましたが使う機会はありませんでした。
    dev.classmethod.jp

  3. 1 から 2 へうまく変換できたらいけるかも・・・

発生した課題

何をどう変換すれば良いかわからない

terraform graphDiagrams の仕掛けも何もわからないところからだったので、出力をファイルに吐き出しながら探っていく事にしました。最終的には下記のことが分かったので、Python で Diagrams を利用するコマンド (パッケージ) を作る事にしました。

最終的に画像を作るのは Graphviz

過去にどこかでお世話になった方も多いのではないでしょうか。
terraform graph コマンドも Diagrams も、画像を生成するため最終的に使っているのは Graphviz でした。

graphviz.org

どちらも中間ファイルとして DOT 言語 で書かれたファイル (以下 dot ファイル) を吐き出しているので、その中身を良い感じに変換してやれば良い事になります。

dot ファイルを取り込んで編集する手段がよく分からない

良い感じに変換できそうなのですが、Graphviz に含まれているインターフェースでは dot ファイルを読み込んだ後に編集する機能がありませんでした。
とりあえずこのプランの実現性を測るため、dot ファイルを sed とか awk を使って荒っぽくこねるシェルスクリプトを書いてお茶を濁しつつ、下記のライブラリにたどり着きました。

github.com ja.stackoverflow.com

Python のパッケージを作ったことがない

これは歴史的な事情が積み重なって、現時点でのベストプラクティスを探すのに大変苦労しました。
おそらく Portey を使っておくと全体的に楽なのではないかと言う印象です。下記のページが大変参考になりました。ありがとうございます。

kk6.hateblo.jp

CLI 対応のために argparse を使ったのは失敗でした・・・。python-fire を使っておけば良かったと大変後悔しています。

qiita.com

CI への組み込み

前々から GitHub Actions を使いたかったので今回使ってみようと思ったのがさらに時間を消費する原因になりましたが、terraform と今回作ったコマンドをセットにしてコンテナにまとめました。

hub.docker.com

フォントがインストールされていなくて、出力された画像内の文字が豆腐になってがっかりしたりもしました。

qiita.com

CircleCI とかもそうですけどなんと言うか地味で上手く行った後も喜びがそこまで大きくないので辛いですね。

今後について

とりあえず AWS の一部リソースにしか対応できていないですが、他のリソースもマッピングを追加すれば使えるようになります。・・・が、そう言う方向性で良いのか悩みます (大変面倒で正直やりたくありません)。
おそらく Diagrams にコミットしていくのが一番近道なのだと思います。
実際これに関する issue はかなり前から存在するようです。
Diagrams に追加のリソースアイコンをコミットしつつ、マージの可能性を探っていくのが良いとは思うのですが。

実際のコード差分と GitHub 上の compare 結果が異なる状況の再現

開発現場でこのような状況が発生して pull request をどうレビューして良いのか分からない、マージして良いのか分からないという状況が発生したため書きました。
Git, GitHub の熟練者は改めて確認するような内容ではないと思います。

再現結果

いきなり結論ですが、再現した状況をここに用意しました。

f:id:semnil:20200318174952p:plain

再現方法

詳しくはここに書いてある通りです。 二つのブランチに対して、複数のコミットを逆順でマージした後、それぞれのブランチのソースコードは同一の内容になり、差分はありません。 ですが、一方のブランチから新たに作成したブランチで変更をコミットした後に GitHub で pull request をもう一方のブランチに向けて作成すると、コミットした変更以外の差分が表示されます。 GitHub の差分確認の機能を使用しても、本来発生していない差分が表示される状態になります。

f:id:semnil:20200318175235p:plain

なんで発生するのか

GitHub の差分が Three-dot 基準で表示されるためです。

help.github.com

git-scm.com

Three-dot のオプションを使用すると、二つのコミットの共通の祖先になるコミットから、後に指定したコミットの差分を出力する結果になります。 f:id:semnil:20200318181625p:plain

どうしたら良いのか

  1. 二つ以上のブランチに複数のコミットをマージする時には順番に気を付けましょう
  2. 発生してしまったら、二つのブランチ (今回の例では master に staging) をマージして pull request を作り直しましょう

補足

もっとわかりやすく書いていらっしゃる方がいらっしゃいました。

qiita.com

補足 2

どうしたら良いのかの 2 を実施してみたのがこれです。
GitHub 上で pull request を作ろうとするとボタンが押せない状態になるので、
f:id:semnil:20200319082724p:plain

手元でマージしてプッシュします。

% git checkout staging
Switched to branch 'staging'
% git merge master --no-ff
Merge made by the 'recursive' strategy.
# ↑ あまりお目にかかれない 'recursive' strategy の出力
% git push origin staging

f:id:semnil:20200319083240p:plain

この状態で改めて pull request を作り直すと、求めていた状態になるはずです。

Serverless Framework (go) で API をデプロイするまで

ゴール

Serverless Framework (go) のプロジェクトを作って API Gateway 経由でレスポンスが得られるところまでです。
Lambda をデプロイするまでは情報があったのですが、API Gateway まで繋げるところの解説がすぐに見つからなかったのでここに書いておきます。

go のインストール

これは必要に応じて実施する感じですね。 私は Mac を使用しているので brew で anyenv インストールからの goenv で 1.13.5 をインストールしました。

Serverless Framework のインストール、プロジェクトテンプレートの作成

Serverless Framework(Go) でHello worldしてみる - Qiita

上記のページを参考にさせていただきました。 プロジェクト内に Serverless Framework を入れておきたかったので以下を実行しておきました。

nom init
npm install --save-dev serverless

nom build でビルドと npm run sls が実行できるように package.json を編集しておきます。

"scripts": {
    "build": "GOOS=linux go build -o bin/main",
    "sls": "./node_modules/.bin/sls"
}

main.go の編集

API Gateway の Lambda proxy integration に対応するために適当に書き換えます。

package main

import (
    "encoding/json"
    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
)

func Handler() (events.APIGatewayProxyResponse, error) {
    body := map[string]interface{}{
        "Message": "Go Serverless v1.0! Your function executed successfully!",
    }
    s, _ := json.Marshal(body)
    return events.APIGatewayProxyResponse{Body: string(s), StatusCode: 200}, nil
}

func main() {
    lambda.Start(Handler)
}

必要なパッケージのインストール

go get \
    github.com/aws/aws-lambda-go/events \
    github.com/aws/aws-lambda-go/lambda

ビルド

以下のコマンドで bin/main を作ります。

npm run build

serverless.yml の編集

Lambda と API Gateway をデプロイするために適当に書き換えます。

service: hello-go-lambda

provider:
  name: aws
  runtime: go1.x
  stage: dev
  region: ap-northeast-1

package:
  exclude:
    - ./**
  include:
    - ./bin/**

functions:
  hello:
    handler: bin/main
    events:
      - http:
          path: hello
          method: get

デプロイ

以下のコマンドで Lambda と API Gateway をデプロイします。

npm run sls deploy

動作確認

API Gateway の Management Console のテスト機能から動作確認してみます。

f:id:semnil:20191227232127p:plain

{
  "Message": "Go Serverless v1.0! Your function executed successfully!"
}

レスポンスを得られることが確認できました。

作成したプロジェクト

ここに置いておきます。

github.com