DevDevデブ!!

プログラミングのこととか書きます。多分。。。

Goでoshiraseというslackのcliコマンドを作ってみた

最近仕事でバッチやら定期実行してる監視スクリプトやらの結果をslackに通知させることが多いんだけど、シェルスクリプトからcurlapi叩くのは面倒なので、goで簡単なcliコマンドを作ってみた

github.com

今のところ機能は以下の3つだけ

  • 指定したチャンネル、グループにメッセージを投稿する (message)
  • チャンネルの一覧を表示する (channel)
  • グループの一覧を表示する (group)

slackの画面上からチャンネルIDとグループIDを調べるのが地味に面倒だったんだよね。。。

ちなみに、最初はslackerという名前にしようとしていたのだけど、github上で検索してみたら同名のリポジトリが600件ぐらいあった。。。

Thorで作った対話型コマンドをテストする方法

最近ThorでCLIコマンドを自作することが多い。 普通にテストするだけなら以下の記事の方法で問題無い

qiita.com

対話型コマンドの場合、テスト時にどうやって入力を与えればいいのか分からず、ちょいとハマってた。

解決方法:標準入力をモックすればよい

解決法は単純で、標準入力をモックすればよかった。

利用したテストダブルのライブラリはRR

stub($stdin).gets { "hoge" }
Hoge.new.invoke :hoge, [] {}

ただ、上記の方法だと何回$stdin.getsしても同じ値が帰ってくるようになるので、ユーザに入力を複数回求める場合は使用できない。 そういった場合は以下のようにする。

stub($stdin).gets.times(1) { "hoge" }
stub($stdin).gets.times(1) { "fuga" }
Hoge.new.invoke :hoge, [], {}

上記のように指定すると、一回目はhoge、二回目はfugaが返却されるようになる。

欲を言えば、配列で指定した順番に返却してくれたりすると嬉しいんだけど、そういう感じの指定方法ってできないのかな?

初PRをキメた話

標準出力、標準エラー出力をキャプチャするgem、arieteにpull requestを出してマージされました

github.com

ariete.rb内にrspecの独自Matcherを定義しているため、arieteはrspec依存しているのだけど、gemspecにはrspecがdevelopment_dependencyで指定されており、かつariete.rb内でrspecがrequireされていなかったために、test-unitで使おうとした際に、require "ariete"するとNameErrorが発生するというもの。

(その前にrequire "rspec"しとくと回避できるけど)

修正箇所は2行。うん。プルリクのコメント書くほうが時間かかったよね。

oracleXEの文字コードをJA16SJISTILDEに変更したDockerfileを作った

github.com

何故作ったのか?

ユニットテストとか実行するときに、Dockerで使い捨てDB建てられないかなーと思ってて、通常のoracleXEのDockerイメージはDockerhubにあるんだけど、今の現場は文字コードがJA16SJISTILDEなのでそのまま使えず、しかたないので自作しましたというお話。

ちなみにまだDockerhubには上げてないよ。 (oracleXEってDockerImageで再配布するのって問題ないんすかね?)

ベースイメージは以下

github.com

ハマったところ

qiita.com

文字コードの変更自体は上記のページの内容をシェルスクリプト化してDockerfile内でRUNするだけなんだけど、それを実行するためには、ビルド時に一度OracleXEを起動する必要がある。

その際の起動にはベースイメージで使ってるシェルスクリプト*1をそのまま使ってるんだけど、その中で、listener.oraのHOSTの部分にコンテナIDをsedで書き込むようになっている。($HOSTNAMEでコンテナIDを参照できる)

#!/bin/bash
LISTENERS_ORA=/u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora

cp "${LISTENERS_ORA}.tmpl" "$LISTENERS_ORA" && 
sed -i "s/%hostname%/$HOSTNAME/g" "${LISTENERS_ORA}" && 
sed -i "s/%port%/1521/g" "${LISTENERS_ORA}" && 

service oracle-xe start

ほんじゃあDockerfileのCMDでservice oracle-xe restartするだけじゃん?って思うんだけど、上記で書き込まれるコンテナIDというのが実はワナでして、

このときに書き込まれるコンテナIDは、イメージビルド時のコンテナID なんですね。 (ベースイメージでは上記スクリプトがDocker runしたときに走るので、問題無い)

つまり、そのままCMDでservice oracle-xe restartすると、listener.oraに記載されるコンテナIDと、実際にDocker runでコンテナを起動する際のコンテナIDが齟齬ってしまう。 それで何が発生するかというと、listener.oraで指定している1521ポートについて、ホストとコンテナ間のポートマッピングが正常に動作しなくなってしまう。(ここらへんあんまり仕組み理解してない)

よって、コンテナ外からoracleXEに接続することができず、使い物にならなくなる。

解決方法

解決方法は単純明快で、以下のようにCMD行で再びlistener.oraをsedで書き換えて上げればよい

(sshdについてはベースイメージのやつをそのままパクってる)

CMD sed -i "s/HOST = .*)(/HOST = $HOSTNAME)(/g" /u01/app/oracle/product/11.2.0/xe/network/admin/listener.ora  && service oracle-xe restart && /usr/sbin/sshd -D

単純明快っつったけど、原因に気づくの時間かかりましたね(´・ω・`)

その他注意点

Docker関係ないんだけど、oracleXE文字コードを変更すると、DBインスタンスの再作成になります。 その際以下のような問題があります。

k-muratadevdesign.hatenablog.com

はい。DBMS_METADATA.GET_DDLが使えなくなります。

私の場合は上記の影響でDBFluteのreplaceschemaを叩いた際のデータロードがこけるようになりました。(ロードデータリバースは動くみたい)

感想

はー、つらい。

文字コードUTF-8に統一されたやさしい世界に住みたいなあ

JavaのCalendarクラスのプロパティ areFieldsSetについての疑問

表題の件について、Javaで以下のようなコードを書いていたときのこと、

private Calendar getStartOfTheDay(Date date) {
    Calendar cal = Calendar.getInstance();  // (1)
    cal.setTime(date);  // (2)
    return DateUtils.truncate(cal, Calendar.DATE);
}

(1)の時点では、calは現在時刻を指しており、(2)が実行された時点でメソッド引数に渡したdateと同一時刻を指すようになるはずなのだけど、現在時刻を指したままという事象が発生した。

デバッガで確認してみると、calのプロパティareFieldSetがfalseになっていた。
areFieldSetはJDK6のCalendarクラスのapidocでは以下のように説明されていた。

fields[] が現在設定されている時刻と同期をとっている場合は true です。false の場合は、次にフィールドの値を取得しようとしたときに、time の現在値からのすべてのフィールドが再計算されます。

timeはCalendarクラスのインスタンスが保持しているUnixタイムで、DAYとかMONTHとかのfieldとの同期がとれていないというのは分かった。
で、その事象はどういう条件下で発生するの??
上記のコードの場合、デバッガで何回か確認しているうちに、いつの間にか発生しなくなってしまった。

また、areFieldSetがfalseの場合に同期をとるにはどうしたらいいのか?

false の場合は、次にフィールドの値を取得しようとしたときに、time の現在値からのすべてのフィールドが再計算されます。

Calendar.get(Calendar.DATE)とか適当に実行してやればいいってことなの?
そもそもareFieldSetはprotectedなので、プログラム中で参照できないんですけど。
どうしたらいいんだこれ?

勉強会資料作成童貞を捨てることにします

またまた一年越しの更新です。物事が長続きしないことに定評があります。
この一年でプライベートで何か最後までやったのってメタルマックス4だけじゃないかな?
娯楽でもこの体たらくなので、ブログなんてとてもとてもw

表題の件について

社内勉強会の当番が周ってきたので、渋々資料を作成しているというだけの話です。勉強会の資料作るのって初めてなので、どんな感じの粒度で作ったらいいのか分かりかねてるんですよね。
パイセンからは社内勉強会なのでカジュアルな感じで全然良いって言われてるんだけど、しょっぱい資料作って社内のコアな連中からdisられたら嫌じゃないですか、ねえ?

テーマは何よ?

Ansibleでございます。このエントリ内にはAnsibleについての情報は一切ないので期待している方は回れ右でお願いします。
テーマ自体は各自自由選択で、なぜAnsibleを選択したのかというと、前職でやったサーバ構築手順書の作成作業がつまらなすぎて死ぬかと思ったという過去がありまして、プロビジョニングツールに興味を持っていたためでございます。

なんでchefじゃなく、Ansibleを選択したのかというと、chef難しそうだな−と二の足を踏んでいたところにシンプルさを謳ったAnsibleが登場したため。俺っちchefノータッチw
だからchefとの比較とかが書けないんだよなー。今からchefかじって比較とかする気ないし。

そもそも今の現場、インフラ専任部隊がいるので自分でサーバ構築とかする必要がなく、Ansibleもさわりのほうしかやってないので、あんま大したことが書けないんですよねー。
聞き手側もこれからAnsibleバリバリ使おうってわけじゃないんだから、そんなコアな資料書く必要も無いんですけど。

そもそも俺が資料作成する必要あるかというお話

Ansibleも結構メジャーなツールだから、ググったら普通にslideshareとかに良い紹介資料見つかるわけですよ。そんな中たいして分かってない俺が資料作る意味ってあるんだろうか?それって車輪の再発明(劣化)にあたるんじゃなだろうか?とか思うんですよねー。

良さ気なスライド出して、「これ読んでねー」でよくね?とか思っちゃう。実務でバリバリ使ってるんならこんな使い方すると捗りますよ−みたいな事例紹介ができるんだけどな。
まあ実務でバリバリ使ってるんなら社内勉強会のテーマにする必要自体が無くなってしまうわけですがw

markdown記法はじめ〼

1. なぜmarkdown記法なのか

流行ってるらしいからDeath!!

というのもあるけど、以下のような理由

1. githubのReadmeはmarkdownで書ける

2. Qiitaの記事もmarkdownで書ける

3. 会社のコードのReadmeで使えるかも?

4. はてなブログもmarkdown対応している

1,2はアカウント作っただけでろくに使ってないんですけどね。。。 あ、Qiitaのほうはjavaの記事に編集リクエスト送ったことあるわ。誤字の指摘だけどw

3番はmarkdownのビューアがあれば使えるね。windows対応のやつ。でもリアルタイムプレビューがないと難しいか。

2. つーかmarkdown記法って何よ?

Let's google!!

ええ、markdownの紹介記事とかいっぱいあるんだから、俺がいまさら駄文書くことも無いでしょうと。 ちなみにホッテントリ入りしてたこのエントリを参考にしました。 エディタはQiitaの運営会社が提供しているMacアプリのkobitoを使った。 以下のように、リアルタイムプレビューできるよ。

kobitoの画面

見ればわかるとおり、左側にmarkdown記法で書いた内容の出力結果が右側でリアルタイムにプレビューできる。

3. 何がうれしいんどす?

プレーンテキストでちゃかちゃか書いていって、整形(というのはおかしいか?)された文章が出力されるからかな? texとかと同じかな。texって何って?ググ(ry

本当のプレーンテキストそのまんまだと、斜体書いたり、リスト書いたりめんどくさいけど、かといってWord使っちゃうと可搬性が損なわれるし、scmで差分管理できないし、というかWord使いたくないし。 HTML直接書くのはたるいよねって感じか

4. 最後に

使ってみて確かに便利だと思った。 はてなブログ書くくらいなら別に見たまま編集のやつでいいかと思うんだけど、適度に装飾されたReadmeとかが簡単に作成できるっていうのは良い。 リアルタイムプレビューできるエディタと、チートシートがあれば基本的な部分は覚えるのも簡単だしね。