ようへいの日々精進XP

よかろうもん

amiCtrl をちょっとバージョンアップした

tl;dr

どうも, かっぱです.

世界で 100 万人の方々からご愛顧頂きたい EC2 AMI を作ったりする「俺のツールズ」シリーズの甘えん坊 amiCtrl をちょっとバージョンアップしました.

github.com

amiCtrl を利用することで, AMI を作ったり, 削除したり, 自分のアカウントに登録されている AMI 一覧を取得することが可能です. 詳しい使い方は README をご一読下さい.

更新内容

アカウントに登録されている AMI 一覧を取得

今までなんで無かったんだろうと過去の自分を問い詰めたい気分ですが, アカウントに登録されている AMI 一覧を取得出来ます. 以下のような感じです.

$ amiCtrl
+--------------------------------------------+--------------+-----------+------------------------+
|                  AMI NAME                  |    AMI ID    |   STATE   |      SNAPSHOT ID       |
+--------------------------------------------+--------------+-----------+------------------------+
| tokito-ami2                                | ami-1234567a | available | snap-0000000a8de111a2a |
|                                            |              |           | snap-0000000a8de111a2b |
+--------------------------------------------+--------------+-----------+------------------------+
| tokito-ami                                 | ami-1234567b | available | snap-0000000a8de111a2c |
+--------------------------------------------+--------------+           +------------------------+
| suzuki-ami2                                | ami-1234567c |           | snap-0000000a8de111a2d |
+--------------------------------------------+--------------+-----------+------------------------+

登録の件数が多いとそれなりの出力行数となります. 現在のところ AMI のオーナー指定は self 固定となっていますが, 将来的には任意のオーナーを指定可能にし, フィルタで絞り込むような既存の AWS CLI 同等の機能を実装する予定です.

JSON 出力

AMI の一覧について, テーブル出力以外に JSON 出力が出来るようになりました. 以下のような感じです.

$ amiCtrl -json
{
  "amis": [
    {
      "ami_name": "tokito-ami2",
      "ami_id": "ami-1234567a",
      "instance_type": "available",
      "snapshot_ids": [
        "snap-0000000a8de111a2a",
        "snap-0000000a8de111a2b"
      ]
    },
    {
      "ami_name": "tokito-ami",
      "ami_id": "ami-1234567b",
      "instance_type": "available",
      "snapshot_ids": [
        "snap-0000000a8de111a2c"
      ]
    },
    {
      "ami_name": "suzuki-ami2",
      "ami_id": "ami-1234567c",
      "instance_type": "available",
      "snapshot_ids": [
        "snap-0000000a8de111a2d"
      ]
    }
  ]
}

JSON 出力は, 他のツールと組み合わせた利用を想定しています. 例えば, 不要な AMI をまるっと削除したい場合, 以下のように使えるんじゃないかなーと思っています.

for id in $(amiCtrl -json | jq -r .amis[].ami_id)
do
  amiCtrl -delete -ami=${id} -batch
done

-delete オプションに -batch オプションを追加することで, 削除確認の出力をスキップすることが可能となります.

# -batch オプションを指定しない場合
$ amiCtrl -delete -ami=ami-e4985d82
+------------+--------------+-----------+------------------------+
|  AMI NAME  |    AMI ID    |   STATE   |      SNAPSHOT ID       |
+------------+--------------+-----------+------------------------+
| suzuki-ami | ami-1234567e | available | snap-0000000a8de111a2d |
+------------+--------------+-----------+------------------------+
上記の AMI を削除しますか?(y/n): n
処理を停止します.

-batch オプションを付与する場合, 問答無用に AMI は削除されてしまいますので注意して利用しましょう.

テストの追加

ユニットテストではありませんが, コマンドの実行結果等をテストする小さなテストを追加しました. 以下のように bytes パッケージを利用してコマンドラインの出力結果をパースしています.

import (
    "bytes"
    "os/exec"
    _ "fmt"
    "strings"
    _ "time"
    "testing"
)

func TestVersionFlag(t *testing.T) {
    cmd := exec.Command("gom", "run", "amiCtrl.go", "-version")
    stdout := new(bytes.Buffer)
    cmd.Stdout = stdout

    _ = cmd.Run()

    if ! strings.Contains(stdout.String(), AppVersion) {
        t.Fatal("Failed Test")
    }
}

exec.Command にダラダラとコマンドを並べるのは見栄えが良くないと思ったので, コマンド自体はシンプルなシェルスクリプトに書いています. 以下のような感じです.

$ cat tests/test_stdout_list.sh
INSTANCE_ID=$(aws --profile=dummy_profile --region=us-east-1 --endpoint=http://192.168.0.100:5000 \
    ec2 run-instances \
      --image-id=ami-1a2b3c4d \
      --count=1 \
      --instance-type=c3.large \
      --key-name=MyKeyPair \
      --security-groups=MySecurityGroup \
      --query=Instances[].InstanceId --output=text)

aws --profile=dummy_profile --region=us-east-1 --endpoint=http://192.168.0.100:5000 \
  ec2 create-image \
    --instance-id=$(echo ${INSTANCE_ID} | tr -d \\r) \
    --name=test-image --output=text

gom run amiCtrl.go -profile=dummy_profile -region=us-east-1 -endpoint=http://192.168.0.100:5000

尚, テストは docker-compose で環境を構築, 実行し, Travis CI でテストを実行するようにしています.

$ docker-compose exec amictrl make test
=== RUN   TestVersionFlag
--- PASS: TestVersionFlag (1.11s)
=== RUN   TestStdoutList
--- PASS: TestStdoutList (2.32s)
=== RUN   TestStdoutCreate
--- PASS: TestStdoutCreate (1.69s)
=== RUN   TestStdoutCreateError
--- PASS: TestStdoutCreateError (1.67s)
=== RUN   TestStdoutDelete
--- PASS: TestStdoutDelete (2.25s)
=== RUN   TestStdoutDeleteError
--- PASS: TestStdoutDeleteError (2.25s)
=== RUN   TestStdoutDeleteNo
--- PASS: TestStdoutDeleteNo (2.33s)
=== RUN   TestStdoutState
--- PASS: TestStdoutState (2.80s)
=== RUN   TestStdoutJson
--- PASS: TestStdoutJson (2.80s)
PASS
ok      amiCtrl 19.219s

.travis.yml は以下の通りです.

sudo: required
matrix:
  include:
  - name: "Go 1.11"
    env: TEST_TARGET=amictrl
services:
  - docker
before_install:
  - docker-compose build
  - docker-compose up -d
install:
before_script:
  - docker-compose exec amictrl make depend
script:
  - docker-compose exec amictrl make test
after_script:
notifications:

こんな感じでテストが走っています.

ということで

この Infrastructure as Code な時代にこんな CLI ツールの存在価値について悩んでいますが, ツールをいじくることで AWSAPI や Go の書き方について学びになっているので, 引き続きこれらのツールを書いていこうと思いますーε≡≡ヘ( ´Д`)ノ

もし, よろしければ amiCtrl をご利用頂きましてフィードバック等を頂けると嬉しいでございます.