ほげほげ見聞録

技術メモ、備忘録、使い方はそのうち覚える

Jenkinsに何かとPOSTする簡易サーバを作るメモ


前に書いたBacklogからJenkinsを実行する記事の補足。
Backlogの情報をJenkinsに投げる部分についてメモ。
dwmemo.hatenablog.com

前回までのあらすじ

JenkinsのCSRFチェックを外さずにジョブを走らせたい、しかしBacklogからのPOST値取得にはcrumbも一緒にPOSTしなければならなかった…。
そこで、簡易サーバを噛ませてJenkinsを走らせるという遠回りな方法を選んだのであった。
別に簡易サーバ作ってみたかったとかそういうわけではない。

流れ

  • Backlogでリポジトリへのプッシュを検知、簡易サーバに情報をPOST
  • 簡易サーバでJenkinsのcrumbを取得、Backlogの情報と併せてJenkinsにPOST
  • Jenkinsで簡易サーバの投げたデータを取得、解析して各種ジョブを実行

Node.js簡易サーバを作る

Node.jsで作ると簡単ダヨーと聞いたので、Node.jsを使う。
ドキュメント読みつつ各種サイト見つつ見よう見まねなのでエラー処理とか真面目にやってない。

とりあえず簡易サーバ作る

画面に「Hello Server!」を表示するだけのものを作成。

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Server!\n');
}).listen(【サーバのポート】, '0.0.0.0'); // localアクセスだけなら127.0.0.1でも可

まずはサーバのローカルアクセスできるか確認。
http://127.0.0.1:【サーバのポート】

次に、外部から該当ポートを使用できるようFireWallに設定追加。
外部からアクセスできるか確認。
http://【ホスト】:【サーバのポート】

これを元に、POSTデータの取得、crumbの取得、取得データをまとめてJenkinsに投げる…を行う。

POSTデータの取得

var http = require('http');
var querystring = require('querystring');

http.createServer(function (req, res) {
    var body = '';
    var jsonString = '';

    if (req.method != 'POST') {
        res.write('Fail!\n');
        res.end();
        req.connection.destroy();
        return;
    }

    req.on('data', function (data) {
        body += data;
    });

    req.on('end', function () {
        var post = querystring.parse(body);
        jsonString = post.payload;    // BacklogのJSONはpayloadに入っている

        try {
           jsonString = JSON.parse(post.payload);
           res.writeHead(200, {'Content-Type': 'text/plain'});
           res.write('Hello Server!\n');
        } catch (err) {
           res.writeHead(500, {'Content-Type': 'text/plain'});
           res.write('Error.\n');
           res.end();
           req.connection.destroy();
           return;
        }

        res.end();
    });

}).listen(【サーバのポート】, '0.0.0.0');

先程試した「http://【ホスト】:【サーバのポート】」に「payload["data":"{}"]」をPOSTして、200が帰ってくるか確認。

crumbの取得

crumbは以下のURLから取得する必要がある。

【JenkinsURL】/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)

安直にhttp.getで取得してみると、この処理を待たずに先の処理を実行する事態が発生。

javascript - NodeJS wait for HTTP request - Stack Overflow

Promiseを使うと非同期処理できるらしい。
「module request-promise」はnpmでインストール。

npm install --save request
npm install --save request-promise

crumb取得URLを叩いて、取得出来たらthen句を実行するという流れ。 promiseの外に書くと、上と同じで処理を待たずに実行してしまう。

        promise('【JenkinsURL】/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,%22:%22,//crumb)')
            .then(token => {
                var crumb = parse_crumb(token);    // crumb部分を取得する適当な関数

                const options = {
                    method: 'POST',
                    hostname: 【Jenkinsホスト】,
                    port: 【Jenkinsポート】,
                    path: '/job/【ジョブ名】/buildWithParameters?payload=' + encodeURIComponent(post.payload),
                    headers: {
                        'Content-Type': 'multipart/form-data',
                        'Jenkins-Crumb': crumb    // k-vでなくtokenそのまま書けば行けるかもしれない
                    }
                };

「http://【ホスト】:【サーバのポート】」に「payload["data":"{}"]」をPOSTして、Jenkinsが実行されpayloadに値が入っているか確認。
プッシュしたブランチの情報などはここで整形して投げるのもありだが、簡易サーバが止まったりする場合を考えてPOST受け取り・投げる以上の事はしない方が良さそう。

余談:簡易サーバを起動させ続ける

Node.jsのサーバを起動させ続けるにはどうするか、forever使うとできるらしい。
GitHub - foreverjs/forever: A simple CLI tool for ensuring that a given script runs continuously (i.e. forever)
公式の通り導入したら起動させたいファイルを指定すれば終わり。

forever server.js


わざわざ調べたが、リモートアクセス使うので普通にコマンドから起動して放置で良いのではと考えて使用は止めた。

Promise参考

作業中に見たり、後から調べたりして参考になったURL。