Jenkinsでログの一部分からメールを作成する方法

Jenkinsのログの一部分からメールを送信する方法がよくわからなかったので、メモ。

最初の目標として、実行された一時ファイルの名前を抽出し、メール送信する。

まずはEmail-ext pluginをインストール。
BUILD_LOG_REGEXを利用する。


BUILD_LOG_REGEXはログを正規表現を利用して抽出できる関数。
${BUILD_LOG_REGEX, regex, linesBefore, linesAfter, maxMatches, showTruncatedLines, substText, escapeHtml, matchedLineHtmlStyle}

使うのは
regex: 正規表現
showTruncatedLines: truncateされた列を表示するかどうか。デフォルトOFFにしてほしい・・・
substText: regexで抽出した文字列だけを表示したいときに利用する。

以下をメールのコンテンツに書く。

以下のテンプファイルを実行しました。

${BUILD_LOG_REGEX, regex=".*/tmp/\(.*\).sh.*", showTruncatedLines=false, substText="$1"}

結果

以下のテンプファイルを実行しました。

hudson3558248437816319717
hudson3558248437816319717

注意点:substTextを使うときはダブルクオテーションで括ること。


=== 2013/05/15 追記 ===

addNewLineなんてオプションができてた。(見落としてた?バージョンアップしたから?よくわからん)

addNewline - If true, adds a newline after subsText.
Defaults to true.

上記の例のように、複数マッチする時は必要ですが、確実に1つマッチするだけということであれば、addNewline=falseを忘れずに。

AntとJUnitインストール方法+Jenkinsの環境変数について

なんかここ数日antをインストールしなおしたりしていたら、
何度も同じところでひっかかったのでメモ

antにjunitを入れる方法

@blog.justoneplanet.infoさん参照
http://blog.justoneplanet.info/2010/12/03/centos%E3%81%ABant%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B/

antのインストール

wget http://ftp.riken.jp/net/apache//ant/binaries/apache-ant-1.8.4-bin.tar.gz
tar -xvf apache-ant-1.8.4-bin.tar.gz
cp -prf apache-ant-1.8.4/ /usr/lib/
ln -s /usr/lib/apache-ant-1.8.4/bin/ant /etc/alternatives/ant
ln -s /usr/lib/apache-ant-1.8.4/ /etc/alternatives/anthome
ln -s /etc/alternatives/ant /usr/bin/ant
ANT_HOMEを設定する。
vim /etc/profile
以下を追加
export ANT_HOME=/etc/alternatives/anthome
反映
source /etc/profile

JUnitの設定

wget https://github.com/downloads/KentBeck/junit/junit-4.10.jar
cp junit-4.10.jar /usr/lib/apache-ant-1.8.4/lib/

しかし、Jenkinsでantを動かしているのだが、上記手順でインストールしていなかったスレーブのほうでビルドしようとするとエラーが出まくる。。。
JAVA_HOMEやANT_HOMEを設定しているので、そっちを見るのかと思っていたのだけど、違った。
確認方法
build.xmlに以下を追加


...

...


...

上の結果がスレーブにログインして行うと

     [echo] /usr/lib/jvm/jre-1.6.0-openjdk
[echo] /etc/alternatives/anthome
JenkinsでSSH経由で実行すると
     [echo] ${env.JAVA_HOME}
[echo] ${env.ANT_HOME}

調べてみると、日々是精進。さんのところにこんな記事が
http://daily-postit.blogspot.jp/2011/12/ssh.html

環境変数が引き継がれないのは/etc/profileではなく、/etc/bashrcに書き込めば良いらしい。

JDBCでAutoCommitがOFFのときのCOMMIT,ROLLBACK後のbeginトランザクションについて

自分が触っているシステムで以下のようなコードを目にした。

...
connection.setAutoCommit(false);
...
connection.rollback();
...
stmt = connection.createStatement();
stmt.execute("begin");
...


トランザクションは結構やっていたが、初めてBEGINコマンドを実行しているコードを見た。
一度ROLLBACKやCOMMITしてトランザクションが終わり、再度新しいトランザクションを開始する場合、BEGINしなくてもいいのだろうか?と疑問が出てきた。
しかし、DBのログを見たところあまりに大量のBEGINコマンドを発行していた。
2回連続発行してあったりした。

それで検証してみた。

以下のコードを実行する。

...
connection.setAutoCommit(false);
stmt = connection.createStatement();
stmt.execute("/* Rollbackの前です。 */");
stmt.close();

connection.rollback();

stmt = connection.createStatement();
stmt.execute("/* Rollbackの後です。 */");
stmt.close();
...

結果

/* Rollbackの前です。 */
ROLLBACK
begin
/* Rollbackの後です。 */

4行目の"begin"はコードには書いていないが、DBではしっかり発行していた。
setAutoCommit(false)はTransactionの暗示的な開始とのこと。
だから、BEGINコマンドは発行していない。
しかし、ROLLBACKやCOMMIT後に再度トランザクションを開始しようとすると、
自動でBEGINが実行される。

感想:
JDBC様ありがとうございました!

JavaでDBの予約語の文字をResultSetから取り出す方法

SELECT * FROM TEST WHERE `FROM` < CURRENT_TIMESTAMP...

のように「`」を使う。

JavaのResultSetがこれをうまく扱ってくれない。

ResultSet rs = null;
...
String col = "`FROM`";
String rtn = rs.getString(col);

`FROM`が見つかりませんとエクセプションが投げられる。

String rtn = rs.getString(col.replaceAll("`", ""));

こんな感じでエスケープキーを削除しないといけない。
setEscapeProcessingが予約語のエスケープでもしているんだろうか・・・。
もうちょっと検証する価値あり。

Javaのプロパティの値の最後の文字をスペースにするには。

かなり初歩的だけど、はまったのでメモ書き。

プロパティの値でスペースは以下の方法で利用できる。


foo=bar\ hoo
最後の文字をスペースにするとしても

foo=bar\ 
これでいける。

ただしEclipseで設定を変えずに保存すると最後のスペースが消える。
普段ならありがたい機能なのだけど、こういうときもある・・・。

JMeterスケジューラの開始、終了時間を一発で変更するシェル

JMeterで負荷テストをするのはいいのだけど、時間を区切って行うことが多い。
その都度、開始時間、終了時間をすべてのシナリオ変更するのは大変。
さらにJMXファイルで書かれている時間はLinuxタイム。

そこで、開始、終了時間を一発で変換できるシェルを作ってみた。

■ファイル名
scenario_modifier.sh

#!/bin/sh
if [ $# -ge 3 -o $# -le 4 ]
then
#startの取得。dateが0以外の戻り値だった場合、終了
start=`date +%s%N -d "$1" | cut -b1-13`
if [ $? -ne 0 ]
then
echo "正しいスタートタイムを指定して下さい。"$1
exit -1
fi
echo "start_time: "$start

#endの取得。dateが0以外の戻り値だった場合、終了
end=`date +%s%N -d "$2" | cut -b1-13`
if [ $? -ne 0 ]
then
echo "正しいエンドタイムを指定して下さい。"$2
exit -1
fi
echo "end_time: "$end

#START_TIMEの置換。
#もし4つ目のパラメーターがあった場合、それを拡張子にする。それ以外は.bak拡張子にする。
echo "スタートタイムの置換を行います"
if [ $# -eq 4 ]
then
sed -i.$4 "s/ThreadGroup.start_time\">\(.*\)$start\(.*\)$start\(.*\)$end

感想:
それぞれのシナリオで開始時間、終了時間を変えたい場合。
この場合はファイルを分けるべきか?
それにファイル数がかなり多くなったときに一発で全部変えたい!
今のところ、こういったシナリオ作ったことがないので、その時になったら考えることにする。

Yahoo JapanとOAuth(signpost利用)してみる。

Yahoo JapanとID連携したいという仕事があった。
Yahooが終わった後には他の所とも連携するらしく拡張性がないとだめ。
さらにJavaでやるとのこと。Javarの自分としては面白そうだったので首をつっこんでみた。

まずは拡張性ということでどこでも使えそうなライブラリーを探してみた。

Googleコード
http://oauth.googlecode.com/svn/code/

signpost
http://code.google.com/p/oauth-signpost/

scribe
http://oauth.net/code/

色々触ってみて、signpostが一番綺麗に書けそうなので、signpostを利用。

inquisitorさんの所にサンプルがあった。
http://blog.unfindable.net/archives/788

これを使ってみた。

動かない・・・。

こんなメッセージが返ってくる。

oauth_problem=parameter_absent&oauth_parameters_absent=oauth_consumer_key,oauth_signature_method,oauth_signature,oauth_timestamp,oauth_nonce
要するにパラメーターがついていませんよ。

そんなことないはずだと思い、全く同じコードでTwitterさんにリクエストを投げてみる→動いた。

送り先のURLをhttpsからhttpに変更して、パケットキャプチャしてみた。

0000   41 75 74 68 6f 72 69 7a 61 74 69 6f 6e 3a 20 4f  Authorization: O
0010 41 75 74 68 20 6f 61 75 74 68 5f 63 61 6c 6c 62 Auth oauth_callb
0020 61 63 6b 3d 22 6f 6f 62 22 2c 20 6f 61 75 74 68 ack="oob", oauth
0030 5f 63 6f 6e 73 75 6d 65 72 5f 6b 65 79 3d 22 64 _consumer_key="d

パラメーター間にスペースが入っている。
signpostのほうを見てみる。

public class AuthorizationHeaderSigningStrategy implements SigningStrategy {

private static final long serialVersionUID = 1L;

public String writeSignature(String signature, HttpRequest request,
HttpParameters requestParameters) {
StringBuilder sb = new StringBuilder();

sb.append("OAuth ");

// add the realm parameter, if any
if (requestParameters.containsKey("realm")) {
sb.append(requestParameters.getAsHeaderElement("realm"));
sb.append(", ");
}

// add all (x_)oauth parameters
HttpParameters oauthParams = requestParameters.getOAuthParameters();
oauthParams.put(OAuth.OAUTH_SIGNATURE, signature, true);

Iterator iter = oauthParams.keySet().iterator();
while (iter.hasNext()) {
String key = iter.next();
sb.append(oauthParams.getAsHeaderElement(key));
if (iter.hasNext()) {
sb.append(", ");
}
}

String header = sb.toString();
OAuth.debugOut("Auth Header", header);
request.setHeader(OAuth.HTTP_AUTHORIZATION_HEADER, header);

return header;
}

}

Yahoo専用のSigningStrategyを作ってみる。

public class YahooAuthorizationHeaderSigningStrategy implements SigningStrategy {

private static final long serialVersionUID = 1L;

public String writeSignature(String signature, HttpRequest request,
HttpParameters requestParameters) {
StringBuilder sb = new StringBuilder();

sb.append("OAuth ");

// add the realm parameter, if any
if (requestParameters.containsKey("realm")) {
sb.append(requestParameters.getAsHeaderElement("realm"));
sb.append(",");
}

// add all (x_)oauth parameters
HttpParameters oauthParams = requestParameters.getOAuthParameters();
oauthParams.put(OAuth.OAUTH_SIGNATURE, signature, true);

Iterator iter = oauthParams.keySet().iterator();
while (iter.hasNext()) {
String key = iter.next();
sb.append(oauthParams.getAsHeaderElement(key));
if (iter.hasNext()) {
sb.append(",");
}
}

String header = sb.toString();
OAuth.debugOut("Auth Header", header);
request.setHeader(OAuth.HTTP_AUTHORIZATION_HEADER, header);

return header;
}

}

見にくいけど、スペースを削ってある。

使い方(inquisitorさんのほぼコピペ。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.basic.DefaultOAuthProvider;

public class OAuthTest {
public static void main(String[] args) throws Exception {
OAuthConsumer consumer = new DefaultOAuthConsumer(
"consumer-key",
"consumer-secret");

consumer.setSigningStrategy(new YahooAuthorizationHeaderSigningStrategy());

OAuthProvider provider = new DefaultOAuthProvider(
"https://auth.login.yahoo.co.jp/oauth/v2/get_request_token",
"https://auth.login.yahoo.co.jp/oauth/v2/get_token",
"https://auth.login.yahoo.co.jp/oauth/v2/request_auth");

String authUrl = provider.retrieveRequestToken(consumer,
OAuth.OUT_OF_BAND);
System.out.println("このURLにアクセスし、表示されるPINを入力してください。");
System.out.println(authUrl);
System.out.print("PIN:");

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String pin = br.readLine();

provider.retrieveAccessToken(consumer, pin);
System.out.println("Access token: " + consumer.getToken());
System.out.println("Token secret: " + consumer.getTokenSecret());
String guid = provider.getResponseParameters().get("xoauth_yahoo_guid")
.first();
System.out.println("GUID: " + guid);
}
}

GUIDがあるので、あとはこれを使ってやればYahooさんのAPIを使える。
その時にもYahooAuthorizationHeaderSigningStrategy使う必要あり。

感想:
見やすいようにしたのかなぜスペース入れたんだろう。
そしてなぜスペースくらい許可してくれないんだろう。