gradle で Berkshelf こないだの続き

こないだの続きで最終的に Docker のイメージを作るんですけど、途中 Dockerfile じゃなくて Chef で作ってます。同じ構成コードで Docker のイメージ以外も作りたくて。というわけで Berkshelf です。

repositories {
  mavenCentral()
}

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath 'org.gradle.api.plugins:gradle-docker-plugin:0.2'
  }
}

apply plugin: 'docker'
apply from: '../berks.gradle'

import org.gradle.api.plugins.docker.tasks.*
import org.gradle.api.plugins.docker.tasks.container.*
import org.gradle.api.plugins.docker.tasks.image.*

docker {
  serverUrl = 'http://127.0.0.1:5555'
}

こないだのに apply from: '../berks.gradle' ってのを追加しました。他のビルドプロジェクトでも使いたい。検索したらあんま見当たらなかったので作ってますけどみんな一緒のことをしてるからうまいことプラグインして切り出したいです。まだ方法がわからない。

task berks_install(type: Exec, group: 'berks') {
  inputs.file  'Berksfile'
  outputs.file 'Berksfile.lock'
  commandLine  'berks', 'install'
}

task berks_update(type: Exec, group: 'berks') {
  commandLine 'berks', 'update'
}

task berks_vendor(type: Exec, group: 'berks', dependsOn: berks_install) {
  inputs.file berks_install.outputs.file
  outputs.dir 'berks-cookbooks'
  commandLine 'berks', 'vendor'
  doFirst {
    new File('berks-cookbooks').deleteDir()
  }
}
$ gradle berks_vendor
:berks_install UP-TO-DATE
:berks_vendor UP-TO-DATE

BUILD SUCCESSFUL

Total time: 2.606 secs

これで良い感じと思われます。 UP-TO-DATE って言われると気分が良いです。 berks_update はその必要があるときに直接実行…でいいのかな…。ただこれまでこれ毎回やってたので(毎回やらない運用だと、 cookbook の変更を取り込み忘れてその後のビルドにかかる20分くらいを無駄にする上にはまる)、一回一回のビルドずいぶん早くなりました。

Packer で Chef な Docker に Dockerfile 重ねていきたいので Gradle その1

なにしろいま表題のような感じでいろいろ試してるんです。そうしたら、本当に38.9℃の熱を出して2日間寝込んでしまいました。

寝込んでいる間 Kindle Paperwhite で Manning の『 Gradle in Action 』を途中まで読みました。この作者の Benjamin Muschko さん、 Gradleware 社の方なんですが、なんと Docker のプラグインを書いています。

検索すると出てくるもう1本は build に特化したもので、 Muschko さんのは、うたい文句通り動けば Docker API の gradle クライアントです。ちなみに、僕のところではうたい文句通り動いていない部分があるので Groovy 勉強してパッチ見てもらいたいと思います…。

というわけで入れてみました。 Gradle 初めてなので、おかしなことやってるかもしれません。

Gradle のインストール

バイナリを落としてきて、 /opt/gradle に置いて、 /opt/gradle/bin/gradle に /usr/bin/gradle から ln -s しました。手癖です。

Docker デーモンが tcp 聞くように

Muschko さんのプラグインは Java の Docker API クライアントを使ってるんですが、こいつがまた tcp でしか Docker デーモンと話さないので、 /etc/init.d/docker 開けて、立ち上げてるところで

nohup $exec -d -H tcp://127.0.0.1:5555 -H unix:///var/run/docker.sock $other_args &>> $logfile &

しました。

build.gradle

repositories {
  mavenCentral()
}

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath 'org.gradle.api.plugins:gradle-docker-plugin:0.2'
  }
}

apply plugin: 'docker'

import org.gradle.api.plugins.docker.tasks.*
import org.gradle.api.plugins.docker.tasks.container.*
import org.gradle.api.plugins.docker.tasks.image.*

docker {
  serverUrl = 'http://127.0.0.1:5555'
}

task info(type: DockerInfo)

次に build.gradle 書きました。一旦ちゃんと動くのか確かめたかったので info を試します。 root のスコープにも repositories 書いてるのは、 gradle-docker-plugin が依存している Docker API Java クライアントを落としてくる用。 serverUrl は http であっております。

$ gradle info
:info
Retrieving Docker info.
Debug                : false
Containers           : 0
Driver               : devicemapper
...

Docker Hub

次にプライベートなイメージを pull とか push とかしたいと思いましたがもう記憶に無いですがログインしろよとか出た気がします。 docker -H :5555 login します。 $HOME/.dockercfg が出来ます。ちなみに上の設定だと socket 経由なら sudo しますよね。もし万が一自分しかユーザのいないホストなら… sudo docker login もしておくとはまらないがちですが、やらないほうが良さそうです。

次に、ここがなんかアホな話なんですが、 Java クライアントが $HOME/.docker.io.properties を作れというので作ります。中身は Docker Hub のアカウント情報です。生パスワード様ご登場なので chmod 600 です。

docker.io.username=oogatta
docker.io.password=unko
docker.io.email=oogatta@gmail.com

これで理論上 pull と push ができますが、ちなみに、自分のところでは push は出来ても pull 失敗します。一見成功してるように見えるんですが、 /var/log/docker 見ると失敗してます。同じ状態で docker -H :5555 pull したら成功するので、 API 経由だと失敗するみたい。 Docker デーモンを -D で起動してログ見ると、全く同じリクエストを Docker Hub に飛ばして、 API からのだけこけているという。良い感じですね。

そもそも Docker Gradle プラグインが依存している Java クライアントのバージョンが古いので、つぶされてないバグがありそうです。この辺は気づいた人がつぶそうって感じでしょうか。ちなみに pull / push はそれぞれ

task pull(type: DockerPullImage) {
  imageId = 'oogatta/kidoibuki'
}

task push(type: DockerPushImage) {
  imageId = 'oogatta/kidoibuki'
}

こんな感じです簡単です。設定できる項目はプラグインのソース見るのが早そうです。

そうそうあと、最後になってしまったんですが、僕のところでは build も動かないんですよね…。これも一見通ってるんですが、 write tcp 172.31.2.33:46951: connection reset by peer みたいな感じです。なんか API クライアントのせいっぽいですね。

まあそんな感じでぐだぐだなんですが、 gradle の依存設定便利だし groovy 読みやすいし berkshelf なんかも一緒に出来たし得たこともありました。が、思ったより長くなってもう寝る時間なので続きは明日。

Dockerfile でどうやるのと思って調べた小さい話

このところ Docker やってます。前にやってたのが去年の10月でさすがに半年以上立ってるので進化していて Docker Hub ができてほんと助かります。 private な image の共有はサーバ自分で立てろ、とかだったので…。

Dockerfile 内で、 build を実行している環境の環境変数を参照したい

docker - Get environment variable value in Dockerfile - Stack Overflow

cat Dockerfile | envsubst | docker build -t my-target -

これ冴えてますよねえー。 envsubst yum も apt も gettext 入れると一発で入りました。ただ、標準入力から Dockerfile の内容を受け取る上の書式だと context 渡せないので、これで一回作って、さらに重ねる感じになりますかね…。

Dockerfile 内で、 build を実行している環境から SSH のエージェントフォワーディングをして RUN git pull とかしたい

コマンドの run ならできる。というか中に sshd 上がってるイメージに対して ssh -A すればいいんですけど、

How to SSH agent forward into a docker container

docker run --volume $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent ubuntu ssh-add -l

自分、 Docker コンテナの作成は今 EC2 上の Amazon Linux でやっているんですが、例えば build 中にコンテナ中で仕事の private リポジトリを git clone したいときに、鍵は手元の OSX にしかなくて、もちろんコンテナにも入れたくないし、という感じなのですよね…。 Dockerfile の VOLUME はホストとコンテナの共有には対応していないので、何かいい方法があるに違いない。

packer の既存 VM から box を作り直すやつは罠が多い

virtualbox-ovf

Ubuntu precise では、おそらく

Waiting for network configuration

が出てネットワークインターフェースを見失う。

sudo rm /etc/udev/rules.d/70-persistent-net.rules

消して、シャットダウンし、また起動すると再生成されてしまうので起動しないまま export して ova を作り、それを元に virtualbox-ovf builder を動かすと順調に遷移した。 packer 側が行っていると推測する再生成に、 OS 側がついていけてないということなのかな。いやいやちがった、 mac address 再生成しているのは virtualbox の clone 処理だと言うことです。

vmware-vmx

packer が使っている既存 vmx の clone コマンドが VMware Fusion 6 にはない。 VMware Fusion 6 Professional にはある。

patch 書いて送るか、あきらめるか、 Pro を買う。

ACM

もっと日記っぽいことも書いてゆきたいと思っております。

こないだ ACM のライブラリ付きの subscription 購入したんです。特にそんときは何も考えて無くて(たまにゃーアカデミックな論文も読みたいけど専門家がキュレートしたやつがいいなあ)てなもんだったんですが、アカデミックなプログラムの話、たとえば「パラダイム」とか「考え方」を勉強するの実は具体的なレイヤーで楽して仕事していくにも必要で、考え方わからないとそういうことができるってのがわかんねえとか、こう書きたいなあって思ってもなんて呼ぶかもわからねえから検索したり人に聞いたりも出来ない。(なんか目の外側がしくしくして胸が前にたたまれるような気がする…)って思っても「悲しい」って言葉知らないと辛い。

しかし、何年も同じ環境同じ言語でやってればそんなの勝手に身につくよって話もあるけれど、言語ってソフトウェアが求められる社会的環境やら、それを手助けできるパラダイムやらで勝手に変わっていくから、そこでまた「悲しい」って表現できるかでキャッチアップの速度が変わってくる。さらに言語環境の金銭面を含めた「楽さ」って、言語そのものがどうこうじゃなくて社会の需要とか、さらには「人気」なんてものに強く影響されるので、楽にいきたければそれだけアカデミックなレイヤーもやっとけってことになるんだなあ。

ちなみにこういうことに全く関係なく突き進む人々をまねすると「バカ」か「病人」になるんだよなあ。これも辛い。許された天才の枠数ってのは社会が決めている。あと、言語が勝手に新しいドメインを獲得して活躍しだしたって受け止められるようなたとえば JS みたいのは例外ってやつなのであんまりそれを未来に渡って期待するのは厳しい感じ。そもそもブラウザ JS から node って最初はかなりジャンプありましたよね…。

Vagrant の box を適当に作る

Vagrant Cloud 良いですよね。何が良いって private もあるところと、ゲストをインターネットに一発公開のあれです。 というわけで、横着していままでストックの precise64 から毎回大量の provisioning して作っていた環境を box にしてみました。あ、もちろん provider は VirtualBox です。

まっさらな状態から一回作る

$ vagrant destroy
$ vagrant up
$ vagrant halt

書き出し

$ vagrant package --base hogehoge_default_1222333344444_55555

hogehoge_default_1222333344444_55555 の部分は VirtualBox の管理するところの VM 名です。ここを間違うと

$ vagrant package --base oogatta-test
==> oogatta-test: VM not created. Moving on...

とかすごい悲しい感じのエラーになるです。

これでコマンドを実行したディレクトリに package.box が誕生しました。

box として登録

この box を Vagrant Cloud にあげるわけですけど、テストのため、あるいは自分一人だからこれで十分とかであれば

$ vagrant box add --name oogatta-test package.box

で追加できました。

$ vagrant box list
oogatta-test (virtualbox, 0)
precise64    (virtualbox, 0)

わーい。

RubyMine と Ruby 1.9

RubyMine のテストランナーを便利に利用する

RubyMine のテストランナーは進捗もゲージが増えていって爽快だし、テスト対象のファイルと項目がツリーになって表示されてどんどん緑ランプが付いて消えていくのも楽しいし、落ちたら赤いランプが付いてそのまま居残り、クリックしたらテスト及びテスト対象コードに飛べるし、飛んだ後ブレークポイント打って今度は右クリックすると対象のテストだけデバッグ実行できて(これこれ…これだ…)感あります。

ただし Ruby 1.9 だと、同梱されているものではない、新しい 'test-unit' gem ( Test::Unit 2 )を入れないとなりません。 Gemfile に書いておきます。

Test::Unit Special Notes

これしないと、あの悪名高き empty test suite. エラーです。 Ruby 2.0.0 に付いてる test-unit は 2.0.0 だったので2系が必要ってことなんでしょうか。

test-unit と RSpec

test-unit ってソース読むと Kernel#at_exit に引っかけて駆動してます。それは全然良いんですが、自分の環境だと RubyMine と CircleCI で RSpec の実行後に突然ユニットテストを実行しようとしてこけて全体が fail するという惨めな状況になりまして、検索したら RSpec を動かすコード中に require 'test-unit' している箇所があるとそうなるよと言われており、自分のコード中にそんなところは無く(多分)、 RubyMine がやってんのかなあ…。とほげーと思って結局 test-unit 側に「おめーこのあと実行走らすなよ」ってフラグを spec_helper.rb に書いておきました…。

Test::Unit::AutoRunner.need_auto_run = false if defined? Test::Unit::AutoRunner

相当切ないです。テスト混ぜるなやってことですか。そうですか。

trello の board の権限メモ

trello 使ってます。

trello の board の権限設定はファイルのパーミッションに似ていて、 Read と Write がある。

Read のことを "Visibility" 、 Write のことは board の "Member" と呼んでいる。

Visibility / Read

  • Private
  • Organization
  • Public

があり、 Organization を選択した場合どの Organization に公開するか選択するダイアログが出る。 Public はそのまま全世界公開です。

デフォルト

自分向けに board を新規作成すると private 、 Organization 向けに新規作成すると "Org Visible" 。

Member / Write

メンバーかそうで無いか、で決まる。

デフォルト

なんだろうが最初は自分だけ。 Org でばんばん board 立ち上げてると毎回 member 追加するのちょっと面倒なような、ひょっとしたら自分の知らない策があるのかどうなのか…。

"My Org Boards" と "Other Org-Visible Boards"

ログインした後の自分のトップページに出てくる Organization 所属の board は、自分が member となっている "My Org Boards" だけのようだ。所属する Organization の、自分が member でない board こと "Other Org-Visible Boards" は、その Organization のフロントページに行って初めて出てくる。

Anker の多ボタンマウスを OSX で使う

Windows で RubyMine を操作するために安い多ボタンマウスをと思い Anker の Anker 8200 DPI High Precision Laser Gaming Mouse を購入して使ってみたら、とても具合が良かったです。メインの2つに加え、戻る進むと僕の苦手なホイールクリックを除いてもあと4つボタンがあります。 "Intention Actions" や "VCS Popup" と "Related Files" なんかのポップアップものに当ててます。 DPI 切り替えボタンは様子見中です(その後 "Run" にしました)。

さてこれを会社の OSX でも使いたいです。が、オフィシャルのアプリはありません。 Anker のこのマウスは設定をマウス本体に保存するタイプなので、困った…戻る進むも動かないや。と思いきや会社にも Windows ありますよね。 KeyRemap4MacBook の EventViewer で見てみると、いかにもゲーミングマウスって感じでメインの3ボタン以外はキーボードデバイスとして振る舞ってました。

まず Windows の公式アプリで、ボタン4から8に単一キーでAからEを割り当てます。

KeyRemap4MacBook は設定をデバイスに限定することが出来るので、

https://github.com/oogatta/hcidevicesettings/blob/master/osx/private.xml#L28

<item>
  <name>Anker Mouse Test</name>
  <identifier>private.oogatta_anker</identifier>
  <device_only>DeviceVendor::Anker,DeviceProduct::Anker8200AsKeyboard</device_only>
  <autogen>__KeyToKey__ KeyCode::A, KeyCode::BRACKET_LEFT, ModifierFlag::COMMAND_L</autogen>
  <autogen>__KeyToKey__ KeyCode::B, KeyCode::BRACKET_RIGHT, ModifierFlag::COMMAND_L</autogen>
  <autogen>__KeyToKey__ KeyCode::C, PointingButton::LEFT, KeyCode::ENTER, ModifierFlag::OPTION_L</autogen>
  <autogen>__KeyToKey__ KeyCode::D, KeyCode::CURSOR_UP, ModifierFlag::CONTROL_L | ModifierFlag::COMMAND_L</autogen>
  <autogen>__KeyToKey__ KeyCode::E, KeyCode::V, ModifierFlag::CONTROL_L</autogen>
</item>

それを利用しました。これで終わりです。 "Intention Actions" はカーソル位置にある識別子を見て候補を出すので、左クリックを発生させています。

Google Chrome の Extension 書いて詰まったりおっと思ったりしたところ

今朝、クロネコさんの荷物問い合わせ結果画面をリロードしつつ「配達中」ステータスになるのを待っていたら、なんかこれは人間のやることじゃないぞと思い立ち、 Chrome の Extension を書きました。

それがこれ、クロネコヤマタブルです。

しかしこの Extension 自体はどうでも良く、詰まったりへえと思ったところを書きます。

background の js では cross origin な XHR が使える

https://github.com/oogatta/kuronekoyamatable/blob/master/main.js#L25
普通に使えちゃうんですね。

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.pushover.net/1/messages.json', true);
xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
xhr.send('token=' + encodeURIComponent(values.apiToken) + '&user=' + encodeURIComponent(values.userKey) + '&message=' + encodeURIComponent(packageInfo + '\n' + message));
}

これが実行コードで、 manfest が
https://github.com/oogatta/kuronekoyamatable/blob/master/manifest.json#L25

  "content_security_policy": "default-src 'none'; style-src 'self'; script-src 'self'; connect-src https://api.pushover.net",
  "permissions"      : [
//...
    "https://api.pushover.net/*"
  ],

こんなです。一応 CSP 設定したので接続先2回書いてますがデフォルトなら permission だけでいいはず。

content に inject されるスクリプトは特別な sandbox に入っている

https://github.com/oogatta/kuronekoyamatable/blob/master/content/main.js#L68

window.debugRun = function () {
  previousStatus = sessionStorage.getItem('previousStatus');
  message = __checker();
  sender();
}

本来インターバルで動作するものをテストするため手動で動かしてみたいので、適当に関数を露出させてみたんですが存在しません。 DOM にはアクセスどころか変更もかけられるのに。

そうだよ。

ってドキュメントに書いてあるのでそうなのです。 dev tools の Console のあれ、あれですよ、 <page context> ってプルダウンで自分の拡張を選択したら、 window.debugRun いました。

Extension 用のストレージ、相当便利

https://github.com/oogatta/kuronekoyamatable/blob/master/options/main.js

chrome.storage.sync.set({
  apiToken: apiToken,
  userKey:  userKey
}, function() {
  vex.dialog.alert('保存しました。');
});

保存これだけですよすごい。これで、 Chrome アカウントに紐付いて別のマシンにも飛んでいくんですよ!。文明開化だわー。

以上です。オプションページはやりたい放題出来て楽しいですね。