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

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

proxy-protocol-jsリリースした & Kyoto.js #16で喋ってきました

表題の通りproxy-protocol-jsをリリースしました.

github.com

www.npmjs.com

Kyoto.js #16の発表資料は以下です.

スライドでも簡単に説明しましたが,PROXY protocolというのはHAProxyが提唱しているTransport層 (L4) においてoriginalの送信元を維持したままproxyするためのprotocolになります.HTTPにおけるX-Forwarded-ForのL4版という感じですね.詳しくはこちら: http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt


で,このPROXY protocolをnode.jsで触りたくなったのでこの度ライブラリをこさえた次第です.主なフィーチャーとしては

という感じになっています.nodeでPROXY protocolを取り扱う人はそう多くはない気はしているのですが,もし来るべき時が来た際にはどうぞご利用ください.

CasperJSを使ってGrafanaのグラフのスクリーンキャプチャを撮る

最近はサーバのメトリクス収集にPrometheus,その可視化の為のフロントエンドとしてGrafanaをハードに使っている.Grafanaは予想よりもはるかに格好良いグラフが生成されるのでやる気が出て良い.デモを触るとなんとなく温度感がわかると思う.
そうしているうちに,これは便利なのでサーバのメトリクス以外の情報も入れてみましょうという発想があり,サービスのKPIに関するような情報も入れ始めている.
とまあここまでは良いのだけれど,そうやって収集したデータも,格好良く可視化したグラフも,人に見られなければ一切の意味が無いのでそうした無意味を避けるためにデイリーでグラフを社内チャットに放流することにした.


という事になるとGrafanaで描画したグラフを画像としてエクスポートする必要が出てくる.
Grafanaにはグラフを画像としてシェアする機能があるのだけれど,これはブラウザをポチポチする必要があるみたいで,APIもぱっと見つからなかった (もしあったら教えて下さい) ので利用を見送った.あとどうやらこの画像データはSQLiteに入るみたいでホイホイ使って良いのかどうかあまり判断が付かなかった (間違ってたら教えて下さい).
というわけで今回はCasperJSを使ってGrafanaのグラフをキャプチャすることにした (結局CasperJS使うんだったらそれでGrafanaの画像シェア機能をポチポチすれば良かったんではないか,という意見もあることだろう.確かに!).
CasperJSはご存知の通りPhantomJS (かSlimerJS) を使ってスクレイピングやテストの実行をするのを助ける便利ライブラリ.CSSセレクタで指定すると,その範囲だけスクリーンキャプチャを撮ってくれるという機能があって大変便利なので使うことにした.

以下の様な感じでキャプチャが撮れる.

var casper = require('casper').create();

// login処理
casper.start('https://your-grafana.example.com/dashboard/db/your-dashboard?from=now-24h', function () {
    this.fill('div.login-inner-box > form', {
        username: 'Grafana user name',
        password: 'Grafana password',
    }, true);
});
casper.then(function () {
    this.click('div.gf-form-button-row > button');
});

// キャプチャ撮る
var waitMillis = 2000;
casper.viewport(1536, 1536).wait(waitMillis, function () {
    this.captureSelector('capture.png', 'div.grafana-row');
});

casper.run();

ログイン処理の部分は最初にfillしてからログインボタンをclickするように書いてある.シンプルなフォームだとfillの部分だけで認証が通るのだけれど,Grafanaはリッチな感じ (buttonのng-clickで処理が走るっぽい) なのでマニュアルでclickする処理を入れている.
認証が通ったらキャプチャを撮る処理を走らせる.ここで即座にキャプチャを撮ってしまうとグラフが未描画の場合があるので,気持ち待ってからキャプチャを撮る処理を開始している (例だと2秒).


で,これらをやる上で考えるべき点としては

  • グラフの時間スケール
  • viewportのサイズ
  • 対象となるグラフのCSSセレクタ

というのが挙げられる.


グラフの時間スケールと言うのは,どの時間スケール (直近5分とか直近24時間とか) のグラフを取得するかという点.Grafanaは時間スケールの情報はダッシュボード自身と紐付いていて,自分あるいは自分以外のユーザが時間スケールを変更すると全てのユーザに影響する.
そうした状態で時間スケールを特に指定せずにグラフを取得してキャプチャを撮ってしまうとめちゃくちゃになってしまうのでちゃんと指定してやる必要がある,という感じ.
例で言う所の https://your-grafana.example.com/dashboard/db/your-dashboard?from=now-24h?from=now-24h の部分がそれに当たる.


viewpointのサイズというのはCasperJSの話.要は仮想的なブラウザの大きさをどうするかみたいなもので,デフォルトの状態のままだとかなり小さめに設定されている.従ってほぼ確実に調整が必要になる部分だと思う.
Grafanaのグラフの大きさはブラウザの画面サイズに応じて可変なので,良い感じの数値を探っていくという泥臭い作業が要求されるのだけれど,まあ適当に妥協しましょう.


対象となるグラフのCSSセレクタというのは若干厄介.
今回は div.grafana-row というのを指定しているのだけれど,これはGrafanaのダッシュボードのrowそのものを表しているので,そのrowに属するグラフ等のパネルを全てキャプチャできる.
div.panel-container というのを指定すると個別のパネルをキャプチャできるので,用途に応じて使い分けると良い……という感じなのだがそう簡単には済まない.
なぜかというと,例えばrowが2つ有ると div.grafana-row というCSSセレクタも2つhtmlに表れてしまう為,先に出た方が優先されてしまう.つまりどちらか片方しかキャプチャができない.id要素などが付いているわけではない,本当に同一のCSSセレクタが出てきてしまうので面倒くさい……という感じ.もちろんパネルが複数個あればその個数分だけ div.panel-container が出てくるので問題は変わらない.
まあここら辺をなんとかする方法としては

  • 気合
  • キャプチャ用のダッシュボードを新たに作る

などといった解決方法があるので頑張れば問題がない.頑張りましょう.



こんな感じでGrafanaのグラフをキャプチャできるようになった.それを社内Gyazoに投げつけた上で社内チャットに毎日放流するという事が自動行えるようになって便利!
なお自動化はcronでやってます.

location-utilってやつ書いた

JavaScriptのライブラリです.

npmとbowerにも出しておいた.
https://www.npmjs.org/package/location-util


URLを食べて,

  • よしなにURLの一部分だけ抜き出したり
  • よしなにURLの一部分を書き換えたり
  • よしなにURL組み立てなおしたり

出来る君です.使い方などはSynopsis読めば大体理解して頂けるのではないかと思います.

Angular.jsの$locationが便利っぽいなーと思って,似たような機能を手で書いてみた次第.

他に対する依存が無いのと,ブラウザでもnode.jsでも使えるのでまあまあ便利かなと思います.とにかく自分が欲しかったから書いた……

追記

というか,

という極めて優れたモジュールがあるので,まずこれを使うと良いと思います.

location-utilはAngularが提供しているようなインターフェースをパクって,いくらかパワフル (?) なメソッドを余分に提供しているんですが,それが必要なかったらmicro-locationが良いと思います.趣味の話っぽい.

追記2

あと,location-utilはプロトコル (http:// みたいな) を省略したURLでも扱えるという裏機能があるんですけど推奨はしません!!!

PhantomJS + QUnit (with qunit-tap.js) + prove を使ってJavaScript のテストをTAP で回す

QUnit とTAP を用いた JavaScript のテスト環境構築の話です。

前提として、QUnit でテストを実行出来る環境が構築できていることとします。
QUnit の実行のやり方等は以下のエントリが詳しくてマジ最高なので、ご参照下さい。
QUnitの基本的な使い方 - Block Rockin’ Codes

さて、今回例として扱うディレクトリの構成としてはこんな感じです。

.
├── lib
│    ├── qunit.css
│    └── qunit.js
├── main.js
├── test.html
└── test.js
  • main.js: テストの対象となる JavaScript ファイル
  • test.html: テストの結果を表示するための html ファイル
  • test.js: JavaScript のテストコード
  • lib以下: QUnit 本体

この段階でtest.html をウェッブブラウザで開くとテストが走り、結果を閲覧出来るはずです。

沸き立ち上がる要求

  • ウェッブブラウザで test.html を開かないとテスト結果を確認できなくてちょっとだるい
    • さらに言うと目視で確認しなければならないのでちょっとだるい
  • 俺は CLI でやっつけたいんだ!!!!!
    • そうすれば終了ステータスで成功したか失敗したかも分かるし便利
  • そしてそれを CI で回したい

PhantomJS + QUnit (with qunit-tap.js) + prove

今回は、上記の要求をこの構成で実現しました。

手順1. PhantomJS のインストール

まず、PhantomJS をインストールします。
ご存知の事とは思いますが、PhantomJS は WebKit ベースのヘッドレスブラウザ (つまりGUI の無いブラウザ) です。
もの凄く雑な (本当に雑な) 説明をすると、コマンドライン上で動くブラウザという感じでしょうか。

PhantomJS はソースコードから make してインストールしようとすると (まじでマジで真剣で) ダルい上に、
コンピュータが暖房機器と化してしまうのでオススメ出来ません。この時期は特に!
なので、特別な理由が無い限りは各プラットフォームに対応したバイナリをインストールするのが良いと思います。
(Debian 系だと apt-get あるいは aptitude、RHEL 系だと yum、Mac OS X だと brew でインストールすると良いでしょう)

インストールの方法は以下が詳しいのでご参照ください
http://phantomjs.org/build.html

手順2. run-qunit.js をダウンロード

run-qunit.js をダウンロードして配置します。

run-qunit.js
https://raw.github.com/ariya/phantomjs/master/examples/run-qunit.js
(「名前をつけて保存」とか、wget とか curl とかでダウンロードすると良いと思います)

run-qunit.js は PhantomJS で QUnit を動作させるためのスクリプトです。
任意の場所に配置して良いですが、プロジェクトディレクトリ以下に配置すると便利かもしれません。
今回の例では vendor/ というディレクトリを掘ってそこに配置する事とします。

実はこの時点で、PhantomJS によるテストは可能となっています。
$ phantomjs vendor/run-qunit.js test.html
とタイプして実行してやるとテストが走ります。走りましたね?
ただ、このままだとちょっと見づらいので、以降の手順で視認性を上げます*1

手順3. qunit-tap.js をダウンロード

qunit-tap.js をダウンロードして配置します。

qunit-tap.js
https://raw.github.com/twada/qunit-tap/master/lib/qunit-tap.js

qunit-tap.js は t_wada さんが開発した、QUnit の出力を TAP 形式にするためのプラグインです。

TAP については以下が詳しいので、興味のある方はご参照ください。
TAP(Test Anything Protocol) : ash
第1回 Perlにおけるテストの概要/TAPとは?:Happy Testing Perl|gihyo.jp … 技術評論社
404 Blog Not Found:「同じコード」の同じって何さ - TAPのススメ

なお、こちらの qunit-tap.js も任意の場所に配置して大丈夫です。
こちらのファイルも、今回は vendor/ 以下に配置する事にします。

現時点での tree は以下のようになっています。
.
├── lib
│    ├── qunit.css
│    └── qunit.js
├── main.js
├── test.html
├── test.js
└── vendor
      ├── qunit-tap.js
      └── run-qunit.js

手順4. test.html を書き換える

文章で説明するのもアレなんで、 コードで説明します。

test.html は初期状態だとこんな感じになっていると思います (QUnit の test/index.html より拝借)。
https://gist.github.com/moznion/6001797/8084086c6eb33bc28e7d4cf3de03b9de3389c9cb

これに c68243e のコミットの変更 (つまり最新のコミットの内容) を書き加えます。
https://gist.github.com/moznion/6001797/revisions
(qunit-tap.js のパスについては、環境に合わせて適宜書き換えて下さい)
(ファイル末尾の の変更に関しては無視して下さい)

この時点で、PhantomJS 上かつ TAP 形式でテストを実行することができます。
先ほどと同様に、
$ phantomjs vendor/run-qunit.js test.html
と実行してやると、TAP 形式で結果が出力されるはずです。

手順5. Test::Harness (prove) の更新

古い Test::Harness (prove) を使うと悲劇が起こるかもしれないので、このモジュールを更新します。
$ cpanm Test::Harness
とか
$ cpan Test::Harness
と実行してやればOKです。

手順6. prove でテストを実行する

$ phantomjs vendor/run-qunit.js test.html
↑のコマンドではなく、prove でテストを実行すると色々と良い事があります。

良い事っていうのは主に “state” オプションのことで、この state を使いこなすと、
「前回失敗したテストのみを実行する」だとか、「変更があったものだけを実行する」といったインテリジェントな処理が可能になります。
使い方はまだまだたくさんあるので、詳しくは以下をご覧ください。
prove についてのおさらい - Perl Advent Calendar Japan 2011 Test Track

prove でテストを実行するときには
$ prove --exec="phantomjs vendor/run-qunit.js" test.html
とすると走るはずです。走りましたでしょうか?



これで CLI から JavaScript のテストを実行できる上に、CI でもガンガン JS のテストを回せますね!!!!!
ハピネス!!!!!!!

*1:まあ、視認性だけが目的ではありませんが……

JSLint をCLI から手っ取り早く使う方法

node で動くjslint-reporter を使えば手っ取り早くて良いと思いました。
https://github.com/FND/jslint-reporter

$ node wrapper.js --upgrade
$ node wrapper.js target.js
としてやるとベロッとJSLint の結果が出てくるので良い感じですね!
(追記: $ node wrapper.js --upgrade は初回と更新したい時だけ実行すれば良いです。
最新のjslint.js を引っ張ってくるだけなので。)


もしくは、jslint4java を使うという手もあります。
http://code.google.com/p/jslint4java/
でも、なんだか重い気がします。

Gist のClone URL をよしなに表示してくれるChrome 拡張を書きました

Gist、UI が変わる前は"SSH" とか"Git Read-Only" スタイルのClone URL が表示されていたと思うんですけど、
UI が刷新されてからは"https" から始まるURL しか表示されなくなりましたよね。

地味に面倒臭いですね!!
git clone する時に、ブラウザからコピーしてきた"https://gist.github.com/1234567.git" というURLを
"git@gist.github.com:1234567.git" に書き換えねばならないのは地味に面倒です。

というわけで

そこら辺を上手い事やってくれるChrome 拡張を書きました。
https://github.com/moznion/ShowMoreGistUrl

このChrome 拡張をインストールすると、https スタイルのClone URL だけではなく
SSH やGit Read-Only 形式でのClone URL も表示されるようになります。

インストールの方法はGitHub のREADME をご覧下さい。
或いは以下をご覧頂くとわかりやすいと思います。
http://blog.fenrir-inc.com/jp/2012/09/jquery-chrome-extension.html

Appendix

Gist はAjax を使ってるようで、拡張を書くに当たって色々と苦労しました。
その影響で、
https://github.com/moznion/ShowMoreGistUrl/blob/master/js/script.js#L2
みたいな感じの何とも言えないコードによって機能が実現されていて何とも言えないですね!
もっと良い実装の方法があればアドバイスを頂戴したいところです。

dojox.form.uploader.plugins.IFrame で問題が生じる原因とその解決方法を書いておく

2012.6.6 追記しました

Dojo のdojox.form.Uploader で詰まったので書いておく - その手の平は尻もつかめるさ
の続きみたいな感じです。

とりあえず今回の諸悪の根源

Dojo(バージョン1.6) のdojo.io.iframe は、「POST メソッドが使える」と公式のドキュメントに書いてあるけど、
どうやら使えないっぽい! 強制的にGET メソッドにされてる感じがする!!


そのお陰でdojox.form.uploader.plugins.iframe を使うと不具合が表出します。
IE だけに現れる不具合かと思ったら、Firefox でもChrome でも発生します。
(FirefoxChrome で意図的にエラーを発生させたい場合は、dojox.form.uploader にforce:"iframe" を指定すると良いでしょう。
ただ、FirefoxChrome でIFrame を使うメリットはほぼ無いと思います。HTML5 を素直に使えば良いと思います)

さておき

最近、Dojo の話題にしか触れていない気がしますがご機嫌如何でしょうか。

先日、「HTML5 に対応してないブラウザでdojox.form.uploader を使いたい時はIFrame プラグインを使うと幸せになれるよー」的な
エントリを書きましたが、それに関連した問題が新たに生じてきたのでそれについて記します。

おや! 405 エラーが出る

dojox.form.uploader をIFrame で利用すると、エラーを吐いて正常に動作しなくなりました。
エラーメッセージには、405 エラーの文字列が。
どうやら、dojox.form.uploader のインスタンスのurl プロパティにセットされている内容の位置にGET メソッドでアクセスを試みた所拒否されたようです。

だったらPOST メソッドを使おう、と言うことでdojox.form.uploader.plugins.iframe のソースコードを見てみると、
(前略)
dojo.io.iframe.send({url:this.getUrl(),form:this.form,handleAs:"json",error:dojo.hitch(this,function(_4){
……
}
(後略)
となっている為、dojo.io.iframe view /dojo/io/iframeを参考にして、
(前略)
dojo.io.iframe.send({url:this.getUrl(),method:"post",form:this.form,handleAs:"json",error:dojo.hitch(this,function(_4){
……
}
(後略)
としてやれば動くんじゃね? と思ってソースを書き換えて再度実行してみたものの、エラーは変わらず。
その上「GET メソッドは駄目です」みたいなメッセージも依然健在。いやいや、ちゃんと'method:"post"'って指定してあるんですが……
(というか、そもそもドキュメントを見ると「method プロパティを指定しなかった場合は、こっちが気を利かせてPOST メソッドをデフォルトに設定してあげるよ!」
と書いてある。いったいなんなんだ……)

そこでこのエントリの上に戻る

ちょっと調べてみると、
internet explorer 8 - dojo.io.iframe.send file upload sends GET request in IE8 - Stack Overflow
IFrameを使った呼び出し: Dojo/JavaScript Toolkitのメモ
Dojo Toolkit - dojo.io.iframe.send ignores method parameter?
と出てくるわ出てくるわ……
これらにも書いてあるように、やっぱりdojo.io.iframe(dojo.io.iframe.send) はPOST メソッドが使えないようです。

解決方法

  • dojo.require('dojox.form.uploader.plugins.IFrame') をdojo.require('dojox.form.uploader.plugins.Flash')に置換する
    • なんだか良く分からないけど、これをするだけで使えるようになります。他の部分は据え置きで。
(補足: ここではFlash のプラグインをrequire していますが、実際に使われるのはFlash ではなくIFrame となっています)

[2012.6.6 追記分]
とてつもない勘違いをした上にとてつもない嘘をつきました。
上記の解決方法で解決すると、IFrame では無くFlash を使ったUploader が実装されます。(昨日、改めて確認しました)
冷静に考えてみたら普通にそうなりますよね……
dojo.form.uploader.plugins.IFrame でPOST メソッドが使えないが為に詰んでいる時には、
dojo.form.uploader.plugins.Flash を使って我慢してね、という事みたいです。
Flash を使ったUploader だと万事解決する(つまり、HTML5 で実装したUploader と同等の働きをする)ので。
[追記ここまで]

もしかしたら、dojox.form.uploader のインスタンスのurl プロパティに「GET メソッドでも参照可能な位置」を渡してあげれば
良いのかも知れませんが、確かめてないので分かりません。(そもそもGET メソッドでも参照可能な位置とは……?)


と言うわけで以上です。お気をつけ下さい。