みんGO を読んでec2インスンスリストをタグ検索するコマンドラインツールを作ってみた
プロジェクトでGO言語に触れながら学習のためにコマンドラインツールを作り拡張させながら言語理解を深めようと目標を立てた。「みんなのGO言語」を参考にしながら自作のコマンドラインツールを作ったのでまとめます。
- 作者: 松木雅幸,mattn,藤原俊一郎,中島大一,牧大輔,鈴木健太
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/09
- メディア: Kindle版
- この商品を含むブログを見る
どんなコマンドラインツールを作ったか
ec2インスタンスをタグ検索してインスタンス情報を取得できるコマンドラインツールを作りました。
生成したインスタンスリストをpecoでインクリメンタルサーチできるようにして選択したインスタンスにsshできるようなzsh関数も合わせて作りました。
作ったサブコマンドとpecoを組み合わせればインスタンスへのssh接続が快適になります。
ec2インスタンスリストを生成してくれるコマンドラインツール
作りたいイメージは次のようなものです。
describe_es2 tag -tag-key Name '*myweb*'
- describe_es2はインスタンスを検索できる
- describe_es2はサブコマンドを提供する
- サブコマンドのtagはインスタンスをtag検索できる
- pecoでインクリメンタルサーチしたいので、検索したインスタンスごとにローカルにファイルを生成して中身にはパブリックDNSを保存されるようにする
コマンドを実行したイメージは次のようになります。
$ describe_es2 tag -tag-key Name '*myweb*' Completed saving file ./myweb001_i-xxxxxxxxxxxxxx, that content is ec2-xx-xx-xx-xx.region.compute.amazonaws.com Completed saving file ./myweb002_i-xxxxxxxxxxxxxx, that content is ec2-xx-xx-xx-xx.region.compute.amazonaws.com Completed saving file ./myweb003_i-xxxxxxxxxxxxxx, that content is ec2-xx-xx-xx-xx.region.compute.amazonaws.com $ find . -type f ./myweb001_i-xxxxxxxxxxxxxx ./myweb002_i-xxxxxxxxxxxxxx ./myweb003_i-xxxxxxxxxxxxxx $ cat ./myweb001_i-xxxxxxxxxxxxxx ec2-xx-xx-xx-xx.region.compute.amazonaws.com // インスタンスのパブリックDNSがファイルの中身に保存されている
tagのサブコマンドには次のオプションを指定できるようにします。
- aws認証ファイルパス(デフォルトはLinux/OSXであれば"$HOME/.aws/credentials)
- aws認証プロフィール(デフォルトは'default')
- region(デフォルトは'ap-northeast-1')
- 検索対象のタグキー(デフォルトは'Name')
aws認証プロフィールをmyprojectに指定して検索する場合は次のようなコマンドになります。
describe_es2 tag -credential-profile myproject '*myweb*'
サブコマンドにはgoogle/subcommandsを使った
今回はtag検索のみのツールですが今後の拡張でEC2 Container Serviceのクラスタ名を指定すればインスタンスリストが生成されるような拡張を考えているためサブコマンド化したかった。「みんなのGO言語」の「4.4 サブコマンドをもったCLIツール」ではmitchellh/cliの使い方を紹介いただいてますが情報が少なそうな「google/subcommands」を使ってみた。
サブコマンドをインターフェースとして定義できるので定義したサブコマンドのオプションのコード化など見通しの良いコードが書ける。
次からはgoogle/subcommandsを利用したコードの説明をしていきます。
google/subcommandsの実装例
サブコマンドのオプションを定義する
サブコマンドのオプションをstructで定義します
// オプションのaws認証情報とタグキー、regionを定義 type tagCmd struct { credential credential tagKey string region string } // aws認証情報は個別にstructで定義 type credential struct { profile string filename string }
subcommands.Commandのインターフェースを実装する
package subcommands // A Command represents a single command. type Command interface { // Name returns the name of the command. Name() string // Synopsis returns a short string (less than one line) describing the command. Synopsis() string // Usage returns a long string explaining the command and giving usage // information. Usage() string // SetFlags adds the flags for this command to the specified set. SetFlags(*flag.FlagSet) // Execute executes the command and returns an ExitStatus. Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) ExitStatus }
tagのサブコマンドがsubcommands.Commandを実装している例
func (*tagCmd) Name() string { return "tag" } func (*tagCmd) Synopsis() string { return "Fetch the ec2 instance public dns name by tag search, then that stored to text file in the current directory." } func (*tagCmd) Usage() string { return `tag [-credential-profile default] [-credential-filename '~/.aws/credentials'] [-region ap-northeast-1] [-tag-key Name] '*dev*' : Created or updated text file ` } func (p *tagCmd) SetFlags(f *flag.FlagSet) { f.StringVar(&p.credential.filename, "credential-filename", "", "optional: aws credential file name, when filename is empty, that will use '$HOME/.aws/credentials'") f.StringVar(&p.credential.profile, "credential-profile", "default", "optional: aws credential profile, default value is 'default'") f.StringVar(&p.region, "region", "ap-northeast-1", "optional: aws region, default value is 'ap-northeast-1'") f.StringVar(&p.tagKey, "tag-key", "Name", "target tag key, default value is 'Name'") } func (p *tagCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { // 省略 return subcommands.ExitSuccess }
"Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) ExitStatus"のメソッドはサブコマンドのメインの処理です。詳細は"github.com/nsoushi/describe-ec2"を参照してください。
定義したtagCmdをsubcommands.Registerで追加する
package main func main() { subcommands.Register(subcommands.HelpCommand(), "") subcommands.Register(subcommands.FlagsCommand(), "") subcommands.Register(subcommands.CommandsCommand(), "") subcommands.Register(&tagCmd{}, "") flag.Parse() ctx := context.Background() os.Exit(int(subcommands.Execute(ctx))) }
インターフェースを実装することでコマンドの使い方やヘルプがまとまる
上記のコードにある通りName()、Synopsis()、Usage()、SetFlags(*flag.FlagSet)を実装することでtagサブコマンドのヘルプが綺麗に出力されます。
subcommands.Registerで登録したサブコマンドが列挙されている
describe-ec2 Usage: describe-ec2 <flags> <subcommand> <subcommand args> Subcommands: commands list all command names flags describe all known top-level flags help describe subcommands and their syntax tag Fetch the ec2 instance public dns name by tag search, then that stored to text file in the current directory. Use "describe-ec2 flags" for a list of top-level flags
tagサブコマンドのヘルプ出力
./describe-ec2 help tag help tag tag [-credential-profile default] [-credential-filename '~/.aws/credentials'] [-region ap-northeast-1] [-tag-key Name] '*dev*' : Created or updated text file -credential-filename string optional: aws credential file name, when filename is empty, that will use '$HOME/.aws/credentials' -credential-profile string optional: aws credential profile, default value is 'default' (default "default") -region string optional: aws region, default value is 'ap-northeast-1' (default "ap-northeast-1") -tag-key string target tag key, default value is 'Name' (default "Name")
subcommands.Commandを使ってみて
- 導入は難しくなく簡単にサブコマンドを増やせるので拡張しやすくヘルプも綺麗にまとまり保守性も良さそうです。
作ったサブコマンドで生成したインスンスリストをpecoでインクリメンタルサーチする
次のようなzsh関数を使って生成したインスタンスリストをインクリメンタルサーチして選択したインスタンスにsshできます。
peco-describe-ec2() { local MAXDEPTH=${1:-1} local BASE_DIR="${2:-`pwd`}" local FILENAME=$(find ${BASE_DIR} -maxdepth ${MAXDEPTH} -type f -exec basename {} ';' | peco | head -n 1) if [ -n "$FILENAME" ] ; then local HOST=`cat $BASE_DIR/$FILENAME` echo "ssh $HOST" BUFFER="ssh ${HOST}" zle accept-line fi zle clear-screen }
ソースを公開しています
使い方
$ go get github.com/nsoushi/describe-ec2/cmd/describe-ec2 $ source $GOPATH/src/github.com/nsoushi/describe-ec2/.zsh.describe_ec2 $ describe-ec2 tag '*AWS*' $ peco-describe-ec2 // '*AWS*'がtag.Nameに含まれるインスタンスリストをpecoでインクリメンタルサーチして選択したらsshします