Go packages from Hashicorp
hashi_wantedly
5 August 2015
deeeet
deeeet
Hashicorp / mitchellh氏が開発しているGo pacakgeを紹介する
3自分が採用しているもの/触ったことのあるものを紹介する
サブコマンド付きのCLIツールを作るためのPackage.Hashicorpツール(e.g., packer,Consul,Terraform)の基本の骨格はこのPackageによって作られている.記述はcodegangsta/cliより多いがテストしやすいなどGoに慣れてるならこちらの方が使いやすい(と思う).
以下のinterfaceを満たすようにサブコマンドを実装していく.
type Command interface { // -h オプション(e.g., packer -h)で各サブコマンドの概要を一行で表示する. // そこで出力する簡単なヘルプメッセージを記述する. Synopsis() string // より詳細なヘルプメッセージを記述する. // e.g., packer build -h Help() string // 実際のコマンドを記述する.引数([]string)を受け取り終了ステータスを返す. Run(args []string) int }
Hashicorpツールならcommand/ディレクトリ以下に各サブコマンドのinterface実装がある
$ cd $GOPATH/src/github.com/mitchellh/packer $ ls command/ build.go command_test.go fix_test.go meta.go push_test.go validate.go build_test.go fix.go inspect.go push.go test-fixtures validate_test.go
(コードを読むときはここから始めれば良い)
7Homeディレクトリを取得するためのPackage.グローバルな設定ファイルをHomeディレクトリに置きたい場合などに使える.
標準パッケージでももちろん取得できるがcgoに依存しておりクロスコンパイルすると実行時に死ぬ.
userInfo, _ := user.Current() userInfo.HomeDir
go-homedirは環境変数などを利用してOSの差分を吸収する.
homedir.Dir()
コンソールへのアウトプットに色付けをするためのPackage.シンプルなinline syntaxが使える.
text := ` [red]colorstring[reset] is a [blue][bold]Go[reset] library for outputting colored strings to a console. using a simple inline syntax in your string to specify the color to print as. ` colorstring.Fprintf(os.Stdout, text)
標準のlog packageにログレベルを設定できるようにする.サードパーティのlogパッケージはたくさんある.が,どれも重すぎたりする... logutilsはその中でも軽くて良い
filter := &logutils.LevelFilter{ Levels: []logutils.LogLevel{"DEBUG", "WARN", "ERROR"}, // ログレベルの定義 MinLevel: logutils.LogLevel("WARN"), // ログレベルの設定 Writer: os.Stderr, } // フィルターとして標準のlogパッケージに登録するだけ log.SetOutput(filter) log.Print("[DEBUG] Debugging") // ログレベルはWARNなので表示されない log.Print("[WARN] Warning") // 表示される 🙆 log.Print("[ERROR] Erring") // 表示される 🙆
Panicが発生した際にコマンドを再実行するしあらかじめ登録しておいたHanlder関数を実行する(リカバーできるわけではない).
どう使うのが良い?
もっとも単純な使い方.
func main() { // (1) Handler(panicが発生したときに実行する)を登録する. exitStatus, _ := panicwrap.BasicWrap(handler) // (2) Panicが発生した場合はexitStatusは0以上の値になりここで終了する. if exitStatus >= 0 { os.Exit(exitStatus) } // (3) 通常のコマンドの実装 panic("Panic happend here...") } func handler(output string) { f, _ := os.Create("crash.log") fmt.Fprintf(f, "The child panicked!\n\n%s",output) os.Exit(1) }
(参考: Go言語のCLIツールのpanicをラップしてクラッシュレポートをつくる | SOTA)
12Genericなmap(map[string]interface{})を特定のstructにDecodeするためのPackage.
なぜ? ... データ(e.g., JSON)の構造があらかじめ分かっていれば標準パッケージでそれをDecodeするのは容易いが,構造が不明確な場合は厳しくなる.この場合Decodeが2回必要になる.1回目のDecodeでそのデータの構造(種類)を判別し2回目に特定のStructへDecodeする.mapstructureはこれを簡単にする.
Hashicorpツールでは設定ファイルやAPIのDecodeで多用されている.
13シンプルな例(より複雑なデータ構造でもっと威力を発揮しそう).
type Person struct { Name string Age int Emails []string Extra map[string]string } // 構造が不明のデータ(e.g., JSON).map[string]interface{}としてDecodeしておく. input := map[string]interface{}{ "name": "Mitchell", "age": 91, "emails": []string{"one", "two", "three"}, "extra": map[string]string{ "twitter": "mitchellh", }, } var result Person _ := Decode(input, &result) fmt.Printf("%#v", result)
mapやslice,pointerといった参照型の値のDeep Copyを行うPackage.
input := map[string]interface{}{ "bob": map[string]interface{}{ "emails": []string{"a", "b"}, }, } dup, _ := Copy(input) fmt.Printf("%#v", dup)
TerraformのConfigオブジェクトのコピーに使われている.
15基数木(Radix tree)のGo実装.トライ木とは異なりは1つの文字だけでなく文字の並びがラベルとして付与される.集合が小さい場合(特に長い文字列の集合)や長い接頭部を共有する文字列の集合などで効果を発揮する(ファイルシステムとか).
r := iradix.New() r, _, _ = r.Insert(([]byte)("foo/"), "read") r, _, _ = r.Insert(([]byte)("bar/"), "write") r, _, _ = r.Insert(([]byte)("foo/bar"), "write") _, policy, ok := r.Root().LongestPrefix([]byte("foo/zip")) if ok { fmt.Println(policy) // read }
ConsulでACLのkeyのLoolupに使われている(armon/go-radix)
16