Go packages from Hashicorp

hashi_wantedly

5 August 2015

deeeet

About me

2

TL;DR

Hashicorp / mitchellh氏が開発しているGo pacakgeを紹介する

3

Goal

4

Packages

自分が採用しているもの/触ったことのあるものを紹介する

5

mitchellh/cli

サブコマンド付きのCLIツールを作るためのPackage.Hashicorpツール(e.g., packerConsulTerraform)の基本の骨格はこの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      
}
6

mitchellh/cli

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

(コードを読むときはここから始めれば良い)

7

mitchellh/go-homedir

Homeディレクトリを取得するためのPackage.グローバルな設定ファイルをHomeディレクトリに置きたい場合などに使える.

標準パッケージでももちろん取得できるがcgoに依存しておりクロスコンパイルすると実行時に死ぬ.

userInfo, _ := user.Current()
userInfo.HomeDir

go-homedirは環境変数などを利用してOSの差分を吸収する.

homedir.Dir()
8

mitchellh/colorstring

コンソールへのアウトプットに色付けをするための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)
9

hashicorp/logutils

標準の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")    // 表示される 🙆
10

mitchellh/panicwrap

Panicが発生した際にコマンドを再実行するしあらかじめ登録しておいたHanlder関数を実行する(リカバーできるわけではない).

どう使うのが良い?

11

mitchellh/panicwrap

もっとも単純な使い方.

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)

12

mitchellh/mapstructure (reflect技1)

Genericなmap(map[string]interface{})を特定のstructにDecodeするためのPackage.

なぜ? ... データ(e.g., JSON)の構造があらかじめ分かっていれば標準パッケージでそれをDecodeするのは容易いが,構造が不明確な場合は厳しくなる.この場合Decodeが2回必要になる.1回目のDecodeでそのデータの構造(種類)を判別し2回目に特定のStructへDecodeする.mapstructureはこれを簡単にする.

Hashicorpツールでは設定ファイルやAPIのDecodeで多用されている.

13

mitchellh/mapstructure (reflect技1)

シンプルな例(より複雑なデータ構造でもっと威力を発揮しそう).

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)
14

mitchellh/copystructure (reflect技2)

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

hashicorp/go-immutable-radix

基数木(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

他にも

17

Thank you

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)