その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

PHPでテストに使うための空きポートを取ってくる

kazuhoさんのこの記事と同じことがPHPでも出来る.

d.hatena.ne.jp

<?php
$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, '127.0.0.1', 0); // localhostのport 0をbindする
socket_getsockname($sock, $address, $port); // ここの$portに取得した空きポートが入ってくる
socket_close($sock); // closeしないともちろんalready bindが出るので閉じておく

以上です.元記事にもあるように競合する可能性がありますが,テスト用途だったらなんとかなるでしょう.

最近の社内Wikiの書き出し

最近,社内Wiki書く時にその記事の頭に「この記事で分かること」というセクションを持ってくるようにしている.こんな感じ.

f:id:moznion:20161118190135p:plain

wikiのページを開いた時に真っ先に「どういう情報が得られるか」が書いてあると,取捨選択を早い段階から行えるので調べ物のスピードが上がって良いような気がしている.
あと所属している組織ではConfluenceを使っていて,Confluenceの検索機能に引っかかるような (引っかけやすいような) ワードを「この記事で分かること」に含めるように心がけている.
地味に便利になりつつある気がしている.

[追記]
そう言えばConfluenceの検索結果は「タイトル」と「サマリ」が出るんだけど,冒頭にこういう情報を書いておくとそれがサマリ部分に表示されるから,検索結果一覧の段階から情報の取捨選択できて便利というのもあった.

golangで書いたツールをCircleCI上でビルドしてその成果物をGitHub Releasesにリリースする

表題の通り.いくらかポイントがあったのでメモとして記す.

基本的にこの記事の内容を真似した.

medium.com

あらかじめ,CircleCIの側の設定でGITHUB_TOKENという環境変数を登録しておく.なおGitHubのPersonal access tokenにはrepoのpermissionを付与しておく.

とは言えこれでは動かない.理由はghr (ghrについてはこちら: 高速に自作パッケージをGithubにリリースするghrというツールをつくった | SOTA) がcontextに依存しているからで,contextはgo 1.7以降でないと利用できない.しかしながらCircleCIのgolang環境は1.6系が使える内の最新なので *1 このままではghrをgo getすることが出来ない.
というわけでCircleCI上でgo 1.7を使うようにしましょう,ということでそういうconfigをcircle.ymlに書く.基本的にこのgistの真似.
しかしこのgistにはタイポがあってそのままでは動かない.ので以下のようにする.

machine:
  environment:
    GODIST: "go1.7.3.linux-amd64.tar.gz"
  post:
    - mkdir -p downloads
    - test -e downloads/$GODIST || curl -o downloads/$GODIST https://storage.googleapis.com/golang/$GODIST
    - sudo rm -rf /usr/local/go
    - sudo tar -C /usr/local -xzf downloads/$GODIST

こうしておくとgo 1.7.3がビルド時に利用されるようになる.

そして以下のようなdeploymentセクションを書く.オリジナルの記事ではmasterにpushする度にGitHub Releasesにアップロードされてしまうので,git tagがpushされた時にだけリリース処理が走るようにする (deployment.release.tagの部分でそうしている).

deployment:
  release:
    tag: /[0-9]+\.[0-9]+\.[0-9]+/
    commands:
    - go get github.com/tcnksm/ghr
    - make clean
    - make VERSION=`git describe --tags | perl -anlE 'm/\A([^\-]+)-?/; print $1'`
    - rm bin/.gitkeep
    - ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace `git describe --tags | perl -anlE 'm/\A([^\-]+)-?/; print $1'` bin/

その他の細々した差としては,ghrのオプション部分で--replace `git describe --tags`として渡されている部分を--replace `git describe --tags | perl -anlE 'm/\A([^\-]+)-?/; print $1'`というperlワンライナーを噛ませた形に書き換えている.git describe --tagsを実行すると{タグ名}-{ハッシュ}という結果が出てくる場合があるので,タグ名以降の内容を落とすためにそうしている.
あとは.gitkeepがアップロード元のディレクトリに含まれているとghrのアップロード時に失敗する (そんなものアップロードするな,みたいなエラーが出る) のであらかじめ消しておくという感じ.
なおビルド自体はmakeコマンドを叩くだけでglide installからクロスコンパイルまでやるという感じにしている (Makefileはこんな感じ
https://github.com/moznion/linenotcat/blob/4349058d9557d49e1f2fc9a23ebeb79e2279f81d/Makefile).

最終的なcircle.ymlとしては以下のような感じ.

machine:
  environment:
    GODIST: "go1.7.3.linux-amd64.tar.gz"
  post:
    - mkdir -p downloads
    - test -e downloads/$GODIST || curl -o downloads/$GODIST https://storage.googleapis.com/golang/$GODIST
    - sudo rm -rf /usr/local/go
    - sudo tar -C /usr/local -xzf downloads/$GODIST
deployment:
  release:
    tag: /[0-9]+\.[0-9]+\.[0-9]+/
    commands:
    - go get github.com/tcnksm/ghr
    - make clean
    - make VERSION=`git describe --tags | perl -anlE 'm/\A([^\-]+)-?/; print $1'`
    - rm bin/.gitkeep
    - ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace `git describe --tags | perl -anlE 'm/\A([^\-]+)-?/; print $1'` bin/

これでtagをpushしたら自動的にGitHub Releasesにビルド成果物がリリースされるようになった.めでたしめでたし.それはそうとghr便利ですね.

[追記]

確かに!!!! ghrのバイナリダウンロードしてくれば良いですね.

linenotcatをhomebrewでインストール出来るようにした

この前書いたlinenotcatをhomebrew経由でインストール出来るようにしました.そうすると便利だと思ったからです.
linenotcatはこんなの.

要はコマンドラインからLINE Notifyにメッセージを投げつけられるツールです.

もともとはGitHub Releasesにビルド済みバイナリをポンと置いていたのですが,それをbrewで引っ張ってこれるようにしましたという話.
こんな感じでインストールが出来るようになって楽ちん.

$ brew tap moznion/homebrew-linenotcat
$ brew install linenotcat

brewで公開するのははじめてやったのですが,案外手軽にできて良かったです.

こんな感じのリポジトリを作ってFormulaを書くだけでいける.
urlGitHub Releasesに置いてあるビルド済みバイナリのURLを指定するとそのバイナリがダウンロードされてきて,それをmvでいい感じのファイル名にrenameして,bin.installでパスが通ってる所にそのバイナリに対するsymlinkを張る,という感じ.
こうすることで,クライアント側でgo buildとかを走らせて各自コンパイルしてもらう必要なく,ビルド済みバイナリを配布するだけで即利用可能になって便利!!

という感じでした.ご利用下さいませ.

「Open Beer Serverの理論とその実装」というタイトルでbuilderscon tokyo 2016にて発表します

builderscon.io

で表題の通り発表します.

builderscon.io

内容としては物理のビールサーバをイチから組み立てて,更にそのビールサーバにHTTPを喋らせるという何を言ってるんだかよくわからない感じですが当方至って真剣です.
ビールサーバってどうやって動いてるの? ビール樽の構造ってどうなってるの? というところから,ビール樽を継続的に監視する為のシステムの構築までざっくばらんに話せたらな〜と思っています.
皆さんぜひ来て下さい!


ところで以下は現時点でOpen Beer Serverの構築にかかっている費用になりますが,今後更にかかります.

f:id:moznion:20161110144738p:plain

なんてこった,これでは年が越せません.破産してしまいます.Open Beer Serverではカンパを募集しています.

linenotcatというツールを書いた

LINE Notifyが便利でよく使っています.LINE Notifyが何かとか何が便利なのかとかをご存じない方は

コマンドラインから LINE にメッセージを送れる LINE Notify « LINE Engineers' Blog

を読んでいただければと思いますが,あえてものすごくざっくり説明するとim.kayacのLINE版みたいなやつです.

んで,Slackというやつも便利で,こちらはチャットツールなわけですが,そのSlackにはslackcatというこれまた便利なコマンドラインツールがあり,これはコマンドを叩くだけで任意のSlackのchannelに対してメッセージやファイルの中身を送ることが出来ます.その名の通りcatコマンドの結果がSlackに流れるという感じ.

というわけでそのLINE Notify版を作ったという話です.コマンドライン経由でLINE Notifyにメッセージを送ることが出来ます.

github.com

LINE NOTify + catということでlinenotcatという名称です *1

基本的な使い方としてはslackcatとだいたい同じで,

$ echo 'YOUR_ACCESS_TOKEN' > $HOME/.linenotcat

という感じでAccess Tokenを登録するともう使えます.

$ echo 'Hello world!' | linenotcat

という風にすれば標準入力経由で投稿が出来て,

$ linenotcat /your/awesome/file.txt

という風にすればファイルの中身を投稿できます.

$ echo 'Hello world!' | linenotcat --tee
Hello world!

teeもできます.

$ tail -f /your/awesome/error.log | linenotcat --stream

stream modeなんてのもあり,上記の例の場合はtailが何か出力したらその内容を投稿するようになります (現状だと3秒間内容を溜め込んで投稿するようになっています).

$ linenotcat --message 'Hello world!'

なにかと便利だったのでそのままメッセージ送れるモードなんてのもあります.

$ linenotcat --image /path/to/your/awesome/image.png

LINE Notifyは実は画像をアップロードして送れるという機能もあるので画像も送れるようにしました

$ linenotcat --image /path/to/your/awesome/image.png --message "Yo!"

メッセージも添えられます.

$ linenotcat --config_file /path/to/your/config

config fileつまりAccess Tokenも差し替える事ができます.送り先を差し替えられたり出来ます.

$ linenotcat --status
{"status":200,"message":"ok","targetType":"USER","target":"moznion"}

今のTokenの情報を引いてくることも出来ます.


という感じです.まあこんなん無くてもshell scriptで大概解決するんですがバイナリポン置きで動くと楽だなという感じでこさえた次第です.
動くバイナリはGitHub Releasesにあるのでご利用下さい.

github.com

[追記]

homebrewに対応しました.

$ brew tap moznion/homebrew-linenotcat
$ brew install linenotcat

*1:そしてLINEは猫ではない

GuiceとSpringを共存させたい

もともとGuiceを使っているプロジェクトがあってそれをSpringに移植したい,だとか,Guiceをバリバリ使っているコンポーネントをSpringのアプリに組み込むことで資源の再利用をしたい,だとか,そういう事になることがある.あるのです.この前実際になった.

で,"spring guice together" とかそういう適当な検索ワードで調べると大体「そんな事はするな」「そうしたい意味がわからん」「そう考える時点でおかしいのでは」などといった,「とにかくやるな」という結果が引っかかる.確かに.しかしやらなければならない時があるのだ……というわけでそのメモを記す.

TL;DR

やらないで済むならやらないほうが良い.

本編

ご存知の通りGuiceGoogle製のDI Frameworkで,一方のSpring frameworkはWeb Application Frameworkである.この二者間になぜ共存の問題が発生するかというと,Spring Frameworkもその内部にDI Frameworkを持っているから,というかそもそもDI機構がSpringの中核を担っているからである.つまりDI Frameworkが2個ある状態となるなのでそれらを矛盾なく管理する必要が出てくる.どうすれば良いのか.

アプローチ

SpringのDIをメインで利用して,Guiceはサブとして扱うという方法を今回は採用した.簡単に言うと,SpringのDIコンテナに対してGuiceのInjectorをInjectするという手法.
こんな感じ.

@Configuration
public class GuiceModuleConfig {
    @Bean
    Injector injector() {
        return Guice.createInjector(new AppModule());
    }
}

AppModuleGuiceのModuleで,このModuleを元にinjectorを作成し,そのinjectorをSpringのDIにinjectしてSpring DIから取得可能なようにしておく.そんでもって以下のようにAppModuleで提供されているinjecteeをSpringのDIを通じて取得できるようにしてやる.

@Bean
DataSource dataSource(final Injector injector) {
    return injector.getInstance(DataSource.class);
}

@Bean
FluentLogger fluentLogger(final Injector injector) {
    return injector.getInstance(FluentLogger.class);
}

つまりはSpringのDIにinjectしたGuiceのinjectorをSpring DI経由で引っ張ってきて,更にそのinjectorからGuice経由で任意のinstanceを取ってくるという感じ.とりあえずこれで基本的なGuiceSpring FrameworkのDI機構を共存させることが可能となる.なおSpringのDIについてはGuiceがあろうとなかろうといつものように利用できる.

Guiceで言うところのServletScopes.REQUEST使いたいときにどうするのか

@RequestScopeを使う.以下のように.

@Bean
@RequestScope
Connection connection(final Injector injector) {
    return injector.getInstance(Connection.class);
}

しかしGuice経由でinstanceを取ってきている場合はこれだけでは不十分で,GuiceServletContextListenerを継承したClassをServletContextListenerとして登録してやる必要がある.

@WebListener
public class AppGuiceConfig extends GuiceServletContextListener {
    private ServletContext servletContext;

    @Override
    public void contextInitialized(final ServletContextEvent servletContextEvent) {
        servletContext = servletContextEvent.getServletContext();
        super.contextInitialized(servletContextEvent);
    }

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new AppModule());
    }
}

なおSpring-Boot等でweb.xmlを利用しない場合は上記のように@WebListenerを用いてListenerとして認識させてやる必要がある.

雑感

2つのDI Frameworkを共存させ,管理する事となるので複雑さとコストが増すように感じる.また,適切に双方のinjecteeのライフサイクルのハンドリングも正しく行わなければならないのでその点においても複雑さが増大する.やらなくて済むのであればやらないに越したことは無いと思う.

以上です.我々はやっていっています.

[追記]

https://github.com/spring-projects/spring-guice

これ使えば楽に出来そうな雰囲気があるんだけど,maven centralに上がっていなくて面倒だったので採用を見送ったという経緯があります.未検証.