gin製のアプリケーションをランダムに止める
私はGo言語でapiを実装するときしばしばgin-gonic/gin を利用します. マイクロサービスとかでapiが死んだときの検証は結構たいへんです. なのでapiが死んだ状態を再現すべく, gin製のアプリケーションがランダムに 404 とか 503 をランダムに返せるようにするMiddlewareを作成した. alcoholics カオスモンキーとかみたいな利用用途を考えている.
私はGo言語でapiを実装するときしばしばgin-gonic/gin を利用します. マイクロサービスとかでapiが死んだときの検証は結構たいへんです. なのでapiが死んだ状態を再現すべく, gin製のアプリケーションがランダムに 404 とか 503 をランダムに返せるようにするMiddlewareを作成した. alcoholics カオスモンキーとかみたいな利用用途を考えている.
~/.gitconfig に次の内容を記述する $ cat ~/.gitconfig [url "git@github.com:"] insteadOf = https://github.com/ GIT_TERMINAL_PROMPT=1 をつけて go get する $ go get プライベートリポジトリのパッケージ
GoのプロジェクトをDockerで動かすことがあると思う. 知らんけど. まぁきっとあるんだろう. ちなみに私は何度かある. 私がよく使うDockerfileの設定を書いておく. Dockerのimageをbuildするときに気を使うことが一つだけある. それはimageのサイズを小さくすることである. 何も考えずにGoのプロジェクトをbuildすると多分こんな具合だろう. FROM golang:latest WORKDIR /go/src/hogehoge.com/who/bar COPY . . RUN set -e \ && go build -o /bin/bar main.go WORKDIR / RUN set -e \ && rm -rf /go/src CMD ["bar"] 流石にここまで雑に書くことはなかなか無いだろうが, 一応ビルドして掃除までしている. だが小さくするには不十分である. まずGoのDocker imageがそれなりに大きい. 2018-12-12のlatestの段階で774MBある. $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE golang latest df6ac9d1bf64 3 weeks ago 774MB Goはシングルバイナリのビルド物を生成する. なので実行時にコンパイラは必要ない. GoのbaseになってるDocker imageはDebianである. なのでGoのdocker imageでバイナリを生成してDebianのimageにうつしてやればいいじゃんてなるだろう. Debianのlatestのimageは101MBである. 小さいように見えるがまだダイエットが可能である. alpine Linuxというもっと小さいDocker imageが用意されている....
12/7 (金) に長野県がやっているときどきナガノの制度を利用して長野県へ行ってきた. 長野県にはよく行く(今年20回くらい?)のだがときどきナガノを利用するのを忘れて普通に行ってしまうミスをしてしまう. 今回は忘れずに利用することができた. 行った場所は八ヶ岳とかの拠点で有名な茅野(今年5回目くらい)で茅野駅直結のワークラボ八ヶ岳を利用した. ここのコワーキングスペースのいいところは駅から近いのと御飯食べれるところが近い, 広いしwi-fi安定してるし人が少なくて静かだということである. とりあえず良い. 多分また利用する機会があるので良さはその時に書くとして, そこで何を作っていたかと言うとmessage queueを実装していた. queue worker式のシステムを構築するとき全体の処理速度を制限したい時がある. そのときworker側で制限するのは大変である. worker側でやるとするとworker同士が通信して誰かが全体の速度計算してみんなにこれ以上通信しないでねって教える必要がある. それは大変そうだし作りたくない. なのでqueueのほうで制限することにした. 作ったものはvalveMQというもので, 裏側がmysqlになっていてアプリケーション側はスケールすることができる. まだ試験的な実装なので設定項目とかは作り込んではいない. そういえばこれだけは言っておきたい. 八ヶ岳Octetのソフトクリームは絶対食べるべき.
これは結構前に作ったネタなんだけども, 何らかのアプリケーションをつくったとき設定をjsonだったりyamlで管理することがある. そのとき秘密情報は隠したいみたいな案件があったとする. 設定ファイルをまるごと暗号化してもいいが, jsonとかだとkeyは暗号化せずvalueだけ暗号化しておいたほうが管理上都合がいい時がある. というわけでcryptexというものを作った. cryptexはmap型のvalue部分のみを暗号化するパッケージである. cryptex自体がサポートする暗号化の手法はrsa暗号とAWSのKMSを利用した暗号である. type Encryptor interface を満たす実装をするとほかの暗号方式にも対応が可能であるように実装してある. 暗号化したり暗号化された情報を復号する機能の他にコマンドラインツールを実装したときに使えるように less で内容を確認する機能や任意のエディタを立ち上げ(デフォルトはnano)て編集する機能を提供している. 編集したりlessで確認するときはユーザーに暗号化と復号を意識しなくてもできるようにしている. 質問やバグ報告はissueから受け付けます. 英語でも日本語でも良い.
電話番号の判定をやりたくてつくり始めた. 一応ルールがあるらしい. 細かい解説はqiitaに存在していたので割愛する. とりあえず作り始めたやつはこれ(tel-num-parser). 使い方 参考実装です. 電話番号であるかどうかと種別が取得できます. package main import ( "fmt" tnp "github.com/ieee0824/go-tel-num-parser-jp" ) var telList = map[string]string{ "東京都庁": "03-5321-1111", "東京都庁2": "03(5321)1111", "東京都庁3": "0353211111", "国土交通省": "03-5253-4150", } func main() { for k, v := range telList { fmt.Print(k + ": ") fmt.Println(tnp.IsTelNumber(v)) } }
Go言語でとあるinterface型の何かが特定の関数を持ってるかを調べる というタイトル的に お前は何を行っているんだ? と言われそうです. まぁ私もどう日本語で表現すればいいのだろうかってなったので例を示しましょう. 例えば次のような実装があったとします. func f() io.Reader { return 何らかのreader interfaceを満たしたやつ } golangで io.Reader といったinterfaceはとても有名で様々な場所で利用されています. 例えば os.File や bytes.Buffer などの構造体などが io.Reader をみたしたものに当たります. io.Reader の定義は次のようになっています. type Reader interface { Read(p []byte) (n int, err error) } つまり構造体が Read(p []byte) (n int, err error) みたいな関数を持っていればいいわけですね. ここで一つ問題です. os.File は利用したあと Close() 関数をよんで開放してあげる必要があります. type io.Reader だと Close() を呼び出すことができません. さてどのように解決しましょうか? 予め入ってる型のパターンが決まっている場合は簡単です. 何も考えずに型アサーションか型switchすればいけます. type hasClose stuct {} func (h *hasClose) Read(p []byte)(int, error){ return 0, nil } func (h *hasClose) Close() error { return nil } type doesNotHaveClose struct{} func (d *doesNotHaveClose) Read(p []byte)(int, error){ return 0, nil } func newReader() io....
golangでSQSのqueueの一覧を取得しようとしたときこんなふうに書くことができる. package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sqs" ) func main() { c := &aws.Config{ Region: aws.String("ap-northeast-1"), } svc := sqs.New(session.Must(session.NewSession(c))) list, err := svc.ListQueues(&sqs.ListQueuesInput{}) if err != nil { panic(err) } fmt.Println(list.String()) } このように書いた場合 github.com/aws/aws-sdk-go/aws/credentials とか github.com/aws/aws-sdk-go/aws/stscredes とかを利用していない. つまり利用されるawsのアクセス権限の元になる情報は ~/.aws 以下に設定された情報だったり ECSの場合はtaskRoleARNに設定された情報となる. 私が上記の実装をした上でdocker containerをビルドして実行したときハマったことを書こうと思う. ECSは標準出力の内容をcloud watch logsに出力することができる. 上記の実装を実行したと気次のようなエラーが出力された. For verbose messaging see aws.Config.CredentialsChainVerboseErrors panic: NoCredentialProviders: no valid providers in chain. Deprecated. はじめはtask roleにsqsのアクセス権限を忘れたことによって発生していた問題かと思ったが違った. ECS上でコンテナを動かしたとき特に何もしない場合はtask roleで設定した情報を元に権限が設定される. 実際のところ裏側ではAWS SDK側で 169.254.170.2 にアクセスしてメタ情報からクレデンシャル情報を引っ張ってくる....
フォーマットが決まっていない文字列から日付を推測するgolangのpackageを作った. fdateという. fdate の f は ふわっと(fuwatto) の f である. フォーマットが決まっていない文字列というか半角数字の羅列から日付として成り立つように組み立てるというのが正しいが. 日付として成り立つ区間の年度を1945~今の年にしてはあるが fdate.MIN_YEAR() と fdate.MAX_YEAR() を上書きすると定義を書き換える事ができる. 使用例は次のような感じである. package main import ( "encoding/json" "fmt" "log" "github.com/ieee0824/fdate" ) func main() { strs := []string{ "2018/10/21", "2018-10-20", "197211", "19720101", "19800824", "1980824", "200011", "200021232", "2010年1月1日", } for _, v := range strs { d, err := fdate.PickPossibleDate(v) if err != nil { log.Println(err) } bin, _ := json.MarshalIndent(d, "", " ") fmt.Print(v, " = ") fmt....