taiyoh's memorandum

@ttaiyoh が、技術ネタで気づいたことを書き溜めておきます。

nagiosで通知先を追加する、もしくはnagiosのalertをhubot or ikachanに渡してIRCに通知させる

 hubotをIRC連携するのは「Ukigumo + hubot-ircでビルド結果を通知 - taiyoh's memorandum」を参照。ikachanなら元からインターフェイスがあるので省略。
 これを承前として、/etc/nagios/objects/commands.cfgに以下を追加。

define command{
        command_name    notify-service-by-hubot
        command_line    /usr/bin/curl -s 'http://localhost:5000/hubot/say' -d 'channel=#hoge' -d "message=** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **"
        }

 別にhubotじゃなくてもいいんだけど。。。そこは環境に合わせて。
 次に/etc/nagios/objects/contacts.cfgを編集して、IRC通知用のcontactを作成する。

define contact{
        contact_name                    hogefugahubot
        use                             generic-contact
        alias                           MyHubot
        service_notification_commands   notify-service-by-hubot
        }

 あとはcontacts.cfgのcontactgroupに、作ったcontact(ここだとhogefugahubot)を追加すれば、IRCにも通知されるようになる。使ってるメッセージはメール送信時のsubjectをそのまま使ってるけど、よしなに変えればいいと思う。

nagiosでsocket.ioのレスポンスを監視する

 nagiosのcheck_httpだとhttp通信の監視しかできないので、websocketというか、socket.ioの通信を監視するのはだいぶ厳しいなー、と思ったので、以下のようなスクリプトを作った(当然部分的にボカしてる)。socket.io-clientとoptimistを予めnpm installしておく必要がある。

 んで、/etc/nagios/objects/commands.cfgに以下の記述を加えてコマンドを登録する。

define command{
        command_name    check_node
        command_line    /path/to/socketio_watch.js -h $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -u $ARG3$
        }

 最後に、/etc/nagios/objects/localhost.cfgに記述を加えて監視サービスを追加する。

define service{
        use                             local-service
        host_name                       localhost
        service_description             NODE
        check_command                   check_node!80!500!/
        notifications_enabled           1
        }

 あとはnagiosを再起動すれば、監視対象に加わる。
 予期しないデータ構造が返ってきたり、レスポンス時間が長くなってくると警告出るようにしてるので、だいぶ捗るのではないかと思います。socket.ioのresourceとか、レスポンス時間の調整はサービス登録するところで変更すればいいようにしているので、多少汎用性はあるはず。

Ukigumo + hubot-ircでビルド結果を通知

 hubotをIRC通知するやり方はあちこちで書かれてるが、僕としてはhubot-irc-runnableを使った方がカジュアルなのでオススメ。
 →jgable/hubot-irc-runnable · GitHub
 その上で、scripts/httpd.coffeeに以下のコードを追加する

  #http://chobie.hatenablog.com/entry/2012/02/26/125532
  robot.router.post "/hubot/say", (req, res) ->
    room = req.body.channel
    message = req.body.message
    #user = robot.userForId room # これだと動かない
    user = robot.adapter.userForId room
    user.room = room
    user.type = 'groupchat'
    robot.send user, "#{message}"
    res.writeHead 200, {'Content-Type': 'text/plain'}
    res.end 'OK'

こうすると、POST /hubot/sayにリクエストを送ればmessageパラメータの内容をchannelパラメータで指定したチャンネルに対して投稿してくれる。これでhubotにもikachanの機能が追加される。
なので、

$app->push_notifier(
    Ukigumo::Client::Notify::Ikachan->new(
        url     => 'http://example.com/hubot',
        channel => '#your-channel',
        method  => 'say'
    )
);

 Ukigumo::Clientのpush_notifierでNotify::Ikachanがそのまま使えるようになる。methodの指定が必要だから、同梱されてるukigumo-client.plとは別でスクリプトを用意しないといけないけど、/hubot/noticeのパスで受け取れるようにしておけばそのまま使える。

socket.ioで下層パスで通信する

 メモ書き。例えば/path/toなんてパスの下でコンテンツの表示もsocket.ioの通信もしたい時。

// in node
var app_path = "/path/to/app";
io.set('resource', app_path);
io.of(app_path).on('connect', function() {
  // do something
});

 とやって、表示するHTMLでsocket.io.jsを取得する際に

<script src="/path/to/app/socket.io/socket.io.js"></script>
<script type="javascript">
var socket = io.connect('http://' + location.hostname, {
  'resource': 'path/to/app'
});
</script>

 という感じで指定する。
 キモはnode側でresourceとofメソッドの引数に同じものを入れることと、クライアント側でio.connectのオプションにnode側と同じ(でも頭の/はない)resourceを入れておくことか。
 別のパスで別のsocket.ioのアプリを立てたい時なんかに使える。

Perlでのfactory_girlもどきのもの

 ちょっと作ってみた。
 → taiyoh/p5-Test-Factory · GitHub
 PerlだとO/R Mapperが乱立しまくってて何かに依存したモジュールにするのは汎用性が低すぎてないわー、って感じだったので、DBISQL::MakerでDB毎の処理は抽象化させて、SQL的にはinsertだけに絞って機能を提供するようにした。その点ではRubyActiveRecordの影響力が絶大で楽。
 factory_girlほど高機能じゃないけど、作ったパターンをベースに統一されたインターフェイスからデータオブジェクトを生成できる(&DBにそのデータを突っ込む)、ってことについてはとりあえずできる。

package Foo;

use Test::Factory;

my $foo_counter = 0;

factory foo => {
    col1 => 'hoge',
    col2 => 'fuga',
    col3 => 'piyo',
    col4 => sub { ++$foo_counter }
};

package main;

# O/R Mapperではないので、内部でDBI使って自前でinsertする
Foo->dsn("dbi:mysql:dbname;host=localhost", 'db username', 'db password', {});

my $f1 = Foo->build('foo');
print $f1->created; # => 0 (false)
print $f1->col4; # => 1


my $f2 = Foo->build('foo');
print $f2->col4; # => 2

$f1->save; # insert into foo (col1, col2, col3, col4) values (?, ?, ?, ?) /* hoge, fuga, piyo, 1 */

my $f3 = Foo->create('foo');
print $f3->created; # => 1 (true)
print $f3->col4; # => 3

Amon2のコントローラをRailsっぽくしてみる

 ただしルーティングはDispatcher::RouterSimple限定だけど。

これをコントローラクラスでuseしておくと、before_filterとafter_filterが使えるようになる。(ちゃんとテスト書いてないので思いつきレベル)
ただ、Module::Functions::get_public_functionsを使ってる都合で、フィルタとして使う関数はprivate methodとしてprefixに「_」を付けておかないと、public methodと見なされて登録されてしまうのが注意点。

自作のオートマトンのモジュールをCPANに上げた

 承前→ 有限オートマトンの習作として - taiyoh's memorandum
 半年くらい前にこんなエントリ書いていたんですが、気づいたら2月中旬に知らない人がFSM::Simpleって名前でCPANに上げてて、しかも僕の実装より全然シンプルじゃなくて腹が立ったので、"FSM::Tiny"ってモジュール名に変更してアップしました。
 → http://search.cpan.org/~taiyoh/FSM-Tiny/
 ホントはこんなyak shavingじゃなくて、FSM::Tinyを使ったネタがあるんだけど、ブログに書くにはちょっと長いし説明するのがメンドイ。