AWS SDK for Goを使って、EC2の主要な情報を集めるツールを作成したときの開発メモ

# 1.TL;DR
EC2を運用する上で、主要なEC2の情報をTSVファイルで出力するツールを作成してみました.

- gatherec2info  
https://github.com/CkReal/gatherec2info

# 2.どんな機能?
運用者以外の人と話すときなど、ExcelやGoogleスプレッドシートに何度かEC2情報をまとめるときがあったので、以下の2種類の方法でインポート用TSVファイルを生成する機能を実装しています。

A.対話モード
B.バッチモード

対話モードは、AWSの初心者もしくはEC2の各種情報を覚えていない人をサポートする想定したモードです.
バッチモードは、cronなどで定期的にEC2の情報を取得して、TSVファイルを生成することを想定したモードです（JenkinsやRundeckなどで定期実行しておけば、毎時や日次でのEC2の差分が取得できそう）

aws-cliやbotoなどでEC2の情報を生で取得すると、その後のjqのフィルターなどが複雑になってしまったりしたので、バッチモードではフィルターなどをかけずに、EC2に関連する主要な情報を全て一つのTSVに出力し、その後の整形はユーザにお任せした方針です.

# 3.インストール方法
goをインストールしてある環境であれば、以下のコマンドでインストールできます.

```
$ go get github.com/CkReal/gatherec2info
```

# 4.事前準備
以下2種類の方法で動作確認を行っています.設定方法1が優先です.  

## 4-1.設定方法1
credentialsファイルを準備し、`AWS_SHARED_CREDENTIALS_FILE`の環境変数に設定する.

```
$ export AWS_SHARED_CREDENTIALS_FILE=~/.aws/credentials
```

## 4-2.設定方法2
デフォルトの`~/.aws/credentials`のファイルに、アカウント情報を追記していく。

```
$ cat ~/.aws/credentials
[sample1]
aws_access_key_id = AK***************
aws_secret_access_key = ***************
[sample2]
aws_access_key_id = AK***************
aws_secret_access_key = ***************
```

# 5.使い方
## 5-1.対話モード

```
$ gatherec2info
#####################################
###### Gather EC2 Information #######
#####################################
Input Profile Id(ex. sample)> sample1
-------------------------------------
[0] default
[1] US East (N. Virginia)
[2] US West (Oregon)
[3] US West (N. California)
[4] EU (Ireland)
[5] EU (Frankfurt)
[6] Asia Pacific (Singapore)
[7] Asia Pacific (Tokyo)
[8] Asia Pacific (Sydney)
[9] Asia Pacific (Seoul)
[10] Asia Pacific (Mumbai)
[11] South America (São Paulo)
Input Region Id(default:Tokyo)> 0
-------------------------------------
[0] ALL
[1] Tags.Name
[2] PrivateIpAddress
[3] PrivateDnsName
[4] PublicIpAddress
[5] PublicDnsName
[6] SubnetId
[7] VpcId
[8] AvailabilityZone
[9] InstanceId
[10] InstanceType
[11] SecurityGroups
[12] ImageId
[13] RootDeviceType
[14] VirtualizationType
[15] KeyName
[16] State
[17] LaunchTime
Input Request Information,comma delimited(ex. 1,2) > 1,2
-------------------------------------
Tags.Name	PrivateIpAddress
sample	10.0.0.1

-------------------------------------
Create [sample1_EC2.tsv] File?(Y/N)> Y
```

### 実行結果
```
$ cat sample_EC2.tsv
Tags.Name	PrivateIpAddress
sample	10.0.0.1
```

## 5-2.バッチモード

```
$ gatherec2info batch -p sample1 -r ap-northeast-1 -o sample1.txt
```

### 実行結果

```
$ cat sample1.txt
Tags.Name	PrivateIpAddress	PrivateDnsName	PublicIpAddress	PublicDnsName	SubnetId	VpcId	AvailabilityZone	InstanceId	InstanceType	SecurityGroups	ImageId	RootDeviceType	VirtualizationType	KeyName	State	LaunchTime
app1.sample1	10.0.0.1	ip-10-0-0-1.ap-northeast-1.compute.internal	54.238.10.100	ec2-54-238-10-100.ap-northeast-1.compute.amazonaws.com	subnet-12345678	vpc-12345678	ap-northeast-1c	i-12345678	t2.small	app.sample	ami-12345678	ebshvm	sample	running	2016-01-06 12:12:00 +0000 UTC
app2.sample1	10.0.0.1	ip-10-0-0-2.ap-northeast-1.compute.internal	54.199.10.100	ec2-54-199-10-100.ap-northeast-1.compute.amazonaws.com	subnet-12345678	vpc-12345678	ap-northeast-1c	i-12345678	t2.small	app.sample	ami-12345678	ebshvm	sample	running	2016-07-12 18:51:18 +0000 UTC
```


# 6.開発メモ
自分の備忘録を兼ねて、開発メモなりを書いておきます.
「GolangやAWS SDK for Goで何かツールを作成しようかなあ」と考えている方の参考になればと^^;  

## 6-1.ファイル構成
最終的なファイル構成は、以下の通り.  

```
gatherec2info
├── README.md
├── cmd
│   ├── batch.go
│   ├── root.go
│   └── version.go
├── common
│   ├── get_info_slice.go
│   ├── get_profile_id.go
│   ├── get_region_id.go
│   └── get_session.go
├── ec2info
│   ├── exec_ec2.go
│   ├── gather_ec2_info.go
│   ├── initialize.go
│   └── var_ec2infoslice.go
├── main.go
└── message
    ├── print_footer.go
    └── print_header.go

```

### main.go
書き始めた初期は、このファイルがすごく膨れ上がり、commonやmessageのパッケージを分離しながら、整理していきました.  

### cmdパッケージ
ヘルプコマンドなどを自作するのは大変そうだったので、その他のgolang製ツールのヘルプコマンドを読むときにも役に立ちそうかと思い、dockerやhugoでも使われている[cobra](https://github.com/spf13/cobra)と[viper](https://github.com/spf13/viper)を利用しました.  

|ファイル名|主機能|
|:----|:----|
|root.go|対話モードのメイン処理|
|batch.go|バッチモードのメイン処理|
|version.go|ツールのバージョン表示|

### commonパッケージ
AWS SDK for Goを使うにあたって、共通で利用するような処理.
最初にこの部分を準備した後、文言などの微調整を行ったりしました.
mapの要素を取り出す順番がランダムになる仕様を知らなかったので、下記のqiita記事が参考になりました.
[Goで、mapをrangeでイテレーションすると、取り出す順番は実行ごとに異なる罠](http://qiita.com/yuki2006/items/5a43644e278c0777ca52)

|ファイル名|主機能|
|:----|:----|
|get_info_slice.go|入力リストを,区切りでユーザーに入力させる|
|get_profile_id.go|プロファイルIDを設定する|
|get_region_id.go|リージョンIDを設定する|
|get_session.go|AWS接続情報取得する|

### ec2infoパッケージ
EC2情報を収集してくる処理.本ツールのメイン部分.
公式ドキュメントと取得してきたJSONとにらめっこしながら、粛々とfor文と格闘しました.
なお、`var_ec2infoslice.go`は、複数の関数から呼び出されるため、途中から変数のみのファイルとして切り出しました.

|ファイル名|主機能|
|:----|:----|
|exec_ec2.go|main関数から呼び出され、ユーザーに必要な情報を入力させる|
|gather_ec2_info.go|EC2の主要な情報を取得する|
|initialize.go|EC2接続情報初期化する|
|var_ec2infoslice.go|EC2取得変数|

独自のTag名などを使って運用しているときは、`gather_ec2_info.go`のこのあたりを修正すればよさそうです.
https://github.com/CkReal/gatherec2info/blob/master/ec2info/gather_ec2_info.go#L58-L60

### messageパッケージ
対話モードのときの文言表示用.  

|ファイル名|主機能|
|:----|:----|
|print_footer.go|起動ロゴ表示|
|print_header.go|アウトプットファイルの生成確認処理|


# 7.参考サイト
以下は、ツール作成しながら、頭を悩ました場所の参考リンク  

### 7-1.AWS SDK for Go関連
- ドキュメント
[session](http://docs.aws.amazon.com/sdk-for-go/api/aws/session/)
[ec2](http://docs.aws.amazon.com/sdk-for-go/api/service/ec2/)

- ブログ
[AWS SDK for Go でEC2インスタンス情報取得](http://qiita.com/draco/items/59c94ab8c66314d3a9f0)
[aws-sdk-goで指定したProfileのCredentialを利用しアクセスを行う](http://qiita.com/simeji/items/17f738a4800e9a18888d)
[Goで、mapをrangeでイテレーションすると、取り出す順番は実行ごとに異なる罠](http://qiita.com/yuki2006/items/5a43644e278c0777ca52)


### 7-2.ヘルプコマンド
- ドキュメント
[cobra](https://github.com/spf13/cobra)
[viper](https://github.com/spf13/viper)

- ブログ
[GolangでCLIの場合にcobraを使うことにした件](http://lab.flama.co.jp/archives/1536/)
[CLI作成支援パッケージ Cobra を使い、Go 言語でコマンドラインツールを作ってみる](http://qiita.com/kent_ocean/items/eb518c0816addd69f353)
[golangで開発アプリのコマンドインタフェースを拡張するには ...](http://ttsubo.hatenablog.com/entry/2015/08/17/193758)

### 7-3.エラー処理
ここはいまだに迷っています^^;  
[Go言語のエラーハンドリングについて](http://qiita.com/nayuneko/items/3c0b3c0de9e8b27c9548)


# 8.あとがき
Golangの勉強がてらに作ったツールだったけど、想定以上にメモ書きが膨れ上がりましたw