きょこみのーと

元六本木でGo書いてました。今はVRでGo書いてます。

goa勉強会 in 六本木一丁目でLTしてきましたので内容の補足など

istyle.connpass.com

LTしてきました。色々学びあって楽しかったです。

会場提供のistyleさんありがとうございました。

少しだけ補足説明とexampleコードを書いたので紹介しようと思います。

LTした資料

speakerdeck.com

注意点

  • goとgoaについてすでにある程度理解がある人向けです
  • goaのversionは、v1.2.0の前提で話しています

go generate使ってコード生成して開発する流れについて

–forceをつけることで毎回mainのgenerateは作り直してます。

//go:generate goagen -o gen main --force -d github.com/kyokomi/example_goa_api/_design
//go:generate goagen -o gen app -d github.com/kyokomi/example_goa_api/_design
//go:generate goagen -o gen swagger -d github.com/kyokomi/example_goa_api/_design

_design下を変更したときの作業手順は以下のような感じになります。

  1. _design下を編集する
  2. ルートディレクトリでgo generateする
  3. 生成された gen/main.go から追加したリソースのMountのコードをルートディレクトリの main.go にコピペする
  4. gen/ 下に生成されたリソースのControllerを application/controllers下にコピーしてimport等を直す(リソース追加ではなく編集の場合は、必要なMethodだけコピペする)

swaggerの配布について

werckerのworkflowの設定はこんな感じです。

f:id:kyokomi:20170617235511p:plain

buildのworkflowはexampleのリポジトリをご覧ください(https://github.com/kyokomi/example_goa_api/blob/master/wercker.yml)。 build のworkflowが終わったあとに design/* のブランチ名だったら put-swagger のworkflowが実行される感じになってます。

put-swagger:
  box: golang:1.8
  push-to-ecr:
  steps:
    - script:
        name: set timezone and deploy tag
        code: |
          export VERSION=`cat $WERCKER_ROOT/VERSION`
          echo $VERSION
    - edgecaseadmin/install-aws-cli:
        key:    $AWS_ACCESS_KEY_ID
        secret: $AWS_SECRET_ACCESS_KEY
        region: $AWS_REGION
    // s3にswagger.jsonをコピーする
    - script:
        name: s3 put swagger
        code: |
          aws s3 cp $WERCKER_ROOT/gen/swagger/ s3://swager.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/ --recursive
    // s3に配置したswaggerのjsonをurlに指定したswagger-uiのリンクをpullRequestコメントする
    - script:
        name: swagger github comment
        code: |
          go get github.com/kyokomi/gh-rice
          gh-rice \
            -t "$GITHUB_TOKEN" \
            -o "$WERCKER_GIT_OWNER" \
            -r "$WERCKER_GIT_REPOSITORY" \
            -b "$WERCKER_GIT_BRANCH" \
            -c "http://swagger.example.dev/?url=http://swagger.example.dev/$WERCKER_GIT_REPOSITORY/$VERSION/swagger.json"

※s3のパスとかは適当です

exampleコード

ヘルスチェックのAPIくらいしか書いてないのであれですが、参考になればと。

github.com

gormのCallbackをつかってexplain結果をログ出力する

はじめに

explainは基本的に実装時に自分で叩いてると思いますが、dev環境とかそこそこデータとか利用頻度が高い環境で雑に垂れ流したいなーと思ってgormのDebug実装を眺めていたらCallbackという仕組みがあったので、それを使ってみました。

作ったもの

github.com

使い方

gormのexampleをにちょこっと追加してみました。

package main

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql" // mysqlを使う
    "github.com/jinzhu/gorm"
    explain "github.com/kyokomi/gorm-explain"
)

type options struct {
    user     string
    password string
    host     string
    port     int
    dbName   string
    location string
}

func buildDataSourceName(opts options) string {
    const format = "%s:%s@tcp(%s:%d)/%s?parseTime=True&loc=%s"
    return fmt.Sprintf(format, opts.user, opts.password, opts.host, opts.port, opts.dbName, opts.location)
}

// Product product table struct
type Product struct {
    gorm.Model
    Code  string
    Price uint
}

func main() {
    opts := options{
        user:     "test-user",
        password: "test-user",
        host:     "127.0.0.1",
        port:     3306,
        dbName:   "test_db",
        location: "UTC",
    }
    db, err := gorm.Open("mysql", buildDataSourceName(opts))
    if err != nil {
        panic("failed to connect database")
    }
    defer db.Close()
    db = db.Debug() // query trace
    db.Callback().Query().Register("explain", explain.Callback) // add explain callback

    // Migrate the schema
    db.AutoMigrate(&Product{})

    // Create
    db.Create(&Product{Code: "L1212", Price: 1000})

    // Read
    var product Product
    db.First(&product, 1)                   // find product with id 1
    db.First(&product, "code = ?", "L1212") // find product with code l1212

    // Update - update product's price to 2000
    db.Model(&product).Update("Price", 2000)

    // Delete - delete product
    db.Delete(&product)
}

実行した結果

(あと、gormのDebug機能をつかうとqueryがログに吐かれるようになるのでこれを合わせて使うと便利です)

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:53)
[2017-05-05 18:16:38]  [1.08ms]  INSERT INTO `products` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2017-05-05 18:16:38','2017-05-05 18:16:38',NULL,'L1212','1000')

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:57)
[2017-05-05 18:16:38]  [0.95ms]  SELECT * FROM `products`  WHERE `products`.`deleted_at` IS NULL AND ((`products`.`id` = '1')) ORDER BY `products`.`id` ASC LIMIT 1
+-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+
|    id |    select_type |    table |    partitions |    type |    possible_keys |    key |    key_len |    ref |    rows |    filtered |                                                  Extra |
+=======+================+==========+===============+=========+==================+========+============+========+=========+=============+========================================================+
|     1 |         SIMPLE |          |               |         |                  |        |            |        |         |             |    Impossible WHERE noticed after reading const tables |
+-------+----------------+----------+---------------+---------+------------------+--------+------------+--------+---------+-------------+--------------------------------------------------------+



  1 Fix example to add query trace

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:58)
[2017-05-05 18:16:38]  [1.11ms]  SELECT * FROM `products`  WHERE `products`.`deleted_at` IS NULL AND ((code = 'L1212')) ORDER BY `products`.`id` ASC LIMIT 1
+-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+
|    id |    select_type |       table |    partitions |    type |              possible_keys |                        key |    key_len |      ref |    rows |    filtered |                                 Extra |
+=======+================+=============+===============+=========+============================+============================+============+==========+=========+=============+=======================================+
|     1 |         SIMPLE |    products |               |     ref |    idx_products_deleted_at |    idx_products_deleted_at |          5 |    const |       2 |         100 |    Using index condition; Using where |
+-------+----------------+-------------+---------------+---------+----------------------------+----------------------------+------------+----------+---------+-------------+---------------------------------------+


(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:61)
[2017-05-05 18:16:38]  [0.86ms]  UPDATE `products` SET `price` = '2000', `updated_at` = '2017-05-05 18:16:38'  WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'

(/Users/kyokomi/workspace/go/src/github.com/kyokomi/gorm-explain/example/main.go:64)
[2017-05-05 18:16:38]  [0.88ms]  UPDATE `products` SET `deleted_at`='2017-05-05 18:16:38'  WHERE `products`.`deleted_at` IS NULL AND `products`.`id` = '7'

注意点

さっと書いたので、fmtで出力してます。ちゃんと自前のロガーとかで出力したい場合は、このコード参考に自分で書いてください 🙏🏻 (もしくはPullRequestまってます 😇 )

おわり

explainの結果に filesortが含まれていたら〜みたいなHandlerを設定できるような感じに改修とかすると便利そうだな〜とか思ってます。

esaのAlfred WorkflowをGoで作りました

作ったもの

https://cloud.githubusercontent.com/assets/1456047/22448591/cdbc9f68-e79d-11e6-8853-9a032cc1b6c1.png

github.com

とりあえずtokenとチーム名を登録して記事を検索できるだけです。

注意点

ちなみに、esaAPIは利用制限があるので検索しすぎにご注意ください

https://docs.esa.io/posts/102#2-3-0

現時点では、ユーザ毎に15分間に75リクエストまで受け付けます。 とのこと

参考にしたもの

使ったライブラリ

github.com

filterで一覧表示するのがこんなに楽に実装できて感謝

github.com

esaのClientすでに作っている方がいてesaAPI仕様はあまり見ないでもサクッと作れました! 感謝

github.com

最初、直接 Open in Finder したフォルダ内にバイナリを放り込んで開発してたんですが、 普通に xxx.alfredworkflow をダブルクリックしてInstallして動作確認したかったので、 alfred-workflow-packager を使うようにしました。(まあ、色々replaceしてzipに固めるだけっぽいですが)

ただ、これはMacでAlfred3がインストールされている前提のコードがあるので、ちょっと書き直してCIで動くようにしようかなと思ってます。

GoでAlfredWorkflowを作る手順(雑メモ)

AlfredWorkflowつくるぞ〜と思ってから最初どうすればいいかわからない具合いが結構すごかったです。

とりあえず自分は、似てるworkflowをinstallして Alfred上で Open in Finder して中身を漁ってつくりを把握してつくってみました。 思ったより単純な構造で、基本的には以下の流れでつくればいいのかなという理解。

  • 普通にCLIツールをGoで作る
    • Script filter での出力は go-alfred 等を使ってxmlをPrintする
    • Run Script して Post Notification に表示する出力は普通に fmt.PrintでOK
  • Alfred上でkeywordsとかをとりあえず作成して ./<バイナリ名> search "{query}" みたいな感じでCLIツールの呼び出しする
  • Open URLとか適当なActionを紐付けて、とりあえず動くところまでいく
  • Open in Finder して icon.pnginfo.plist だけコピーしてGoのProjectにコピーする
  • あとは alfred-workflow-packager を使って xxx.alfredworkflowを作成して、ダブルクリックしてインストールして動作確認していく感じ

まとめ

Alfred便利なのでどんどん作っていくぞ!!!!!!

昔作ったgaego-initをglide対応した

github.com

gaego-initとは以下のように、GAE/Goの開発環境をコマンド一発でgenerateするやつです。

$ gaego-init -app hoge-app
$ tree hoge-app/
hoge-app/
├── Makefile
├── README.md
├── appengine
│   ├── app.yaml
│   └── init.go
└── circle.yml

1 directory, 5 files

以前作ったときは、vendoringの対応をどうするかべきかわからず、結局shellでgoapp getするという荒業をしていたのでそれを修正しました。

# Makefile                                                                                                                                                                                            22:26:56
APP_ROOT=appengine
VENDOR_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath/vendor
APP_GOPATH=$(shell pwd)/$(APP_ROOT)/gopath
RUN_GOPATH=$(APP_GOPATH):$(VENDOR_GOPATH)

install:
    glide init
    glide install
    mkdir -p $(APP_ROOT)/gopath/vendor
    ln -s $(shell pwd)/vendor/ $(VENDOR_GOPATH)/src

run:
    GOPATH=$(RUN_GOPATH) goapp serve $(APP_ROOT)

clean-run:
    GOPATH=$(RUN_GOPATH) goapp serve --clear_datastore $(APP_ROOT)

deploy:
    GOPATH=$(RUN_GOPATH) goapp deploy $(APP_ROOT)

こちらの記事を参考にglideでのvendoringを行えるようにしました。

motemen.hatenablog.com

感謝 ? ? ? ? ?

Arukasを使って無料でGo製のslackbotを運用する

はじめに

SlackbotをHerokuで一日中動かすと課金が発生するので色々ハックが必要となったりするので、なんとかしたくArukasに出会いました。

今回は、自分が作ったgo製のslackbotを元につくったbotgithubにpushしてWercker経由でdockerImageを作ってArukasへのDeployを行う方法を紹介します。

github.com

今回のコードとか設定をexampleとして公開してますので、よろしければ参考にしていただければと。

Arukasとは?

いまのところ無料でDockerをホスティングできるサービスです。

arukas.io

一応CLIツールも公開されていて、結構便利です。

github.com

※注意点としては、CMDで起動したプロセス監視とかは無いので自分でとかgo-server-starterとかを入れてプロセス死んだときに再起動する仕組みを入れる必要があるところです。

流れ

  • 以前作ったgo製のslackbotのdockerImageをWerckerで作成する kyokomi.hatenablog.com kyokomi.hatenablog.com
  • Arukasのコンソールでアプリケーションを作成し、dockerImageを指定する
  • WerckerのworkflowでArukasのrestartを設定する

slackbotのコード自体はDockerとか意識しない形になっているので、wercker.ymlとかを見ていただければと。

example-go-slackbot/wercker.yml at master · kyokomi/example-go-slackbot · GitHub

Werckerの設定

f:id:kyokomi:20170101221031p:plain

README.md に書いてある通り6つの環境変数の登録が必要です。

ポイントはdocker-pushのworkflowで github.com/lestrrat/go-server-starter/cmd/start_serverをinstallしてます。 まあ別にバイナリをdonwloadでもいいんですが面倒だったので...

docker-push:
  box: golang
  steps:
    - glide-install
    - setup-go-workspace
    - script:
        name: install deamontools
        code: |
          go get github.com/lestrrat/go-server-starter/cmd/start_server
    - script:
        name: install application
        code: |
          go install
    - internal/docker-push:
        username:   $DOCKER_HUB_USERNAME
        password:   $DOCKER_HUB_PASSWORD
        tag:        latest
        repository: $DOCKER_HUB_REPOSITORY
        registry:   https://registry.hub.docker.com

arukas-deployのworkflowでは、 arukasのバイナリをdownloadして適当にPATHを通してstop -> startしてます。 sleep 10 を挟んでいるのは、stopする前にstartするとエラーになるので仕方なくです。

arukas-deploy:
  box: golang
  steps:
    - script:
        name: install tools
        code: |
          sudo apt-get update
          sudo apt-get -f install
          sudo apt-get install -y wget unzip curl tree
    - script:
        name: arukas install
        code: |
          mkdir -p $HOME/lib
          export PATH=$PATH:$HOME/lib
          cd $HOME/lib
          wget https://github.com/arukasio/cli/releases/download/v0.1.2/arukas_v0.1.2_linux_amd64.zip
          unzip arukas_v0.1.2_linux_amd64.zip
          rm arukas_v0.1.2_linux_amd64.zip
    - script:
        name: arukas restart
        code: |
          arukas stop ${ARUKAS_CONTAINER_ID}
          sleep 10
          arukas start ${ARUKAS_CONTAINER_ID}

Arukasの設定

f:id:kyokomi:20170102005223p:plain

おまけ

ちゃんとプロセス死んで再起動するかの動作確認ですが、昔ネタでいれたcommandを実行させるプラグインが役に立ちました。(危険)

f:id:kyokomi:20170102010051p:plain

何度でも蘇る。

2016年振り返り

主にTwitterの全ツイート履歴を見て振り返る。

振り返り

半分くらいは業務コードと思われる.

f:id:kyokomi:20170101122503p:plain

開発合宿とかPC持っていく旅行

結構同じメンバー何回も開発合宿行ってて、会社が別になっても気軽に集まれる関係なのがいいなと思いました。 来年も行きましょう!!!

各月のサマリー

2〜7月: ひたすらAndroidして退職

  • Droidkaigiに行った
  • 業務でAndroidエンジニアが居なくて困っていたので、Androidエンジニアにコンバートした
  • ひたすらAndroid書いてた。とにかく仕様バグとかクラッシュを削りながらな日々
  • twitter.com
  • 去っていた人のコードを当時いた人の気持ちになってリーディングするサイコメトリーをマスターする
  • twitter.com
  • twitter.com
  • Androidそこそこできるようになったので、Androidエンジニアの採用を頑張っていた
  • 制約と誓約を意識して、Goを書かないで全ステータスをAndroidに振っていた
  • 7月一杯でGunosyを退職

8月〜9月

10月〜12月

その他

麻雀

2016年は10回くらい打った気がする。役満は1回。 メンツは結構バラバラだったけど、トータル勝ち越した気がする。

twitter.com

関係者各位また定期的にやりましょう!!!

個人開発

  • GAE/Goでちょっとしたキュレーションシステム
  • Androidアプリ2つ(キュレーションのクライアントと、Twitterの画像をひたすら表示するやつ)
  • 放置ゲーのAPIサーバーをGAE/Go -> ECSに移行
  • 放置ゲーのクライアントをUnityでプロトタイプ作成

相変わらずエターなってるけど、作ったものを自分では使っているのでそこそこ満足している。

まとめ

  • 2016年は発表とかブログとかアウトプット少なめで本を読んだり技術検証したりでインプット多めだった気がする
  • 温泉行って開発合宿すると疲労を回復しながら、開発が捗るので2017年もやっていきたい
  • 2017年はリリースもあるので、Tipsとか開発フローとかについてとか発表したり、ブログでの情報発信とかもしっかりやっていくぞ

werckerでprivate repositoryを含むglide installする方法

手順

  1. glide.ymlでsshのURLを指定する
  2. wercker上でApplication Environmentか Organization settings Environmentで + Generate SSH Keys してssh keyを環境変数に登録する
  3. 「2.」で登録したssh keyをgithub上の Settingsの SSH keys で登録する
  4. wercker.ymlに add-ssh-keyadd-to-known_hosts を記載する

glide.ymlとwercker.yml

gist.github.com

fingerprintはこちらを参考に入力しましょう。

What are GitHub's SSH key fingerprints? - User Documentation

wercker Generate SSH Keys

Application Environment

f:id:kyokomi:20161218150924p:plain

Organization settings Environment

こっちで指定すると全部のアプリケーションで使いまわせます。

f:id:kyokomi:20161218150826p:plain