Turnip, Cucumber などを使った end-to-endテストのテストデータに付いて
Turnip, Cucumber, RSpec feature などを使った end-to-end テスト のテストデータをどう作るかについて、今までの経験を書いてみます。
RSpecなどを使ったモデルのテストであれば、テストデータは factory_girl 等を使い、テストに必要なデータを it, describe単位でテストで作成する事が多いと思います。
しかし、end-to-endテストでは人がそのアプリを使う手順、シナリオに沿ってのテストにり、モデルのテストのように個々のテストで個別のデータを必要になることはあまりなく、いくつかのデータを作っておけば済みます。また、モデルのテストでは必要になるモデル(テーブル)は少数ですがend-to-endテストではほとんどのモデル(テーブル)にデータが必要になります。
そこで、今まで作ったアプリのend-to-endテスト用のデータをどのように準備したかを、まとめてみました。またテストデータではありませんが、開発時に使う db_seed についてもふれています。
1. Cucumber + fixture + db_seedなし
Rails 2の頃に作ったアプリで標準の fixture を使っています。なんとモデルも fixture を使っているので fixture はかなりカオスな状況になっています ^^;
また、db_seed については知らなかったので、 rake db:fixtures:load
を使っていました。 fixture は人手でデータを作るので テストや開発に必要な意味のあるデータを書くのには向いていると思います。
ただし、テスト用にデータを100件準備するとかには向きません(ERBが書けるので出来ないわけではないですが)。
2. RSpec feature + factory_girl + db_seed
この開発では end-to-endテストは RSpec feature を使い、テスト用のデータは factory_girl を使って作っていますが、helperメソッド内で全モデル用のデータを作り全end-to-endテストで同じものを使っています。 db_seed はActiveRecordのメソッドだけで別に作りました。
3. RSpec feature + fixture + db_seedなし
これは、1. のシステムをRails4にバージョンアップする際に管理画面の end-to-endテストを追加した際に作りました。従ってテストデータは 1. と同じです。
4. RSpec feature + factory_girl + factory_girl を使ったdb_seed
db_seed と end-to-endテストのデータは同じもで良いのでは? または db_seed は end-to-endテストのデータ + アルファ なのでは? という仮定のもとに db_seedもfactory_girl を使って作りました。 同じようなものを2つ作らずに済んだので良いアプローチだったと思います。
また、end-to-endテストを作る際には実際にブラウザーを操作し、画面や DOM を見ながらテストを作るのでdb_seed と end-to-endテストのデータが同じだと作業がはかどりました。
5. Turnip + factory_girl + db_seed
この案件は既存のアプリの改善だったので、既に db_seed はあるが end-to-endテストは無い状態だったので end-to-endテストのデータは 2. と同じように作りました。
ただし、 4. に書いたようにテストを書く際にテスト用データでアプリを動かせると便利なので、それを行うツールを作りテストアプリ作成に役立てました。
ツールは以下のようなコードで、load_turnip_seed で Turnip 用に書いたテストデータ作成メソッド turnip_seed_data() を呼び出していま。turnip_seed_data() は通常のfactory_girlを使ったコードです。 テストデータを消すための truncate というメソッドも作っておきました。
# Usage: # rails runner Tasks::DbTool.truncate # rails runner Tasks::DbTool.load_turnip_seed $LOAD_PATH << "#{Rails.root}/spec" # TODO: もっと良いやり方 require 'turnip_seed_data' class Tasks::DbTool extend FactoryGirl::Syntax::Methods def self.load_turnip_seed self.truncate turnip_seed_data() end def self.truncate ActiveRecord::Base.connection.tables.reject{|t| t == 'schema_migrations'}.each do |table| # MySQL only ActiveRecord::Base.connection.execute("TRUNCATE TABLE `#{table}`") end end end
Turnip を使ったテスト作成のノウハウ
現在、バリバリと Tunip + Capyabara + PhantomJS (poltergeist) で end-to-end テストを書いてます。 そこで知ったノウハウを書きます、今回はテストの構成分けなどの上流側ではなく、ツールや steps 側になります。
Turnipに限らず Cucmber, RSpec Feature など Capybara を使った、テストの大部分は
の繰り返しです。
ツール
Chrome等のDevTools
まずDOM上のエレメントを特定するための正しく、かつメンテンス生の高いCSSセレクターを決めないと行けません。これは ブラウザーの開発ツール (Cmd+Opt+I)のElements画面で、エレメント選択(Cmd+Shift+C)で画面上の目的の場所をクリックして目的の CSSセレクター を探していけると思います。
また開発ツールの Console を使うと、document.querySelector() や jQuery でCSSセレクターが 正しいかを確認できます。 よくあるのは CSSセレクターがユニークでは無く違う要素が選択されてしまう事があるで、単純なCSSセレクター以外は試してみるのが良いです。
正し、jQueryのCSSセレクターには拡張があります、これはCapybaraでは動作しないので注意して下さい。
Snapshot
テストが上手く行かない時は表示要素の特定用CSSセレクターの間違だけではなく、対象のページまで来てなかったりJavaScriptが正しく動作してないなどの事もよくあります。 Capybara-WebkitやPoltergeistには現在のDOM(画面)を画像やHTMLとして保存できます。
これを使い以下のようなメソッドやstepsを定義すると、stepsの中や feature の中から呼び出す事で画面を確認できます。
- helper
def take_screenshot(save_type = :image, path = nil) path = Time.now.strftime("/tmp/%y%m%d-%H%M%S-%L.png") unless path if save_type == :image page.save_screenshot(path) else File.write(path, page.html) end end
- steps
step 'HTML保存' do take_screenshot(:html) end step '画面画像保存' do take_screenshot end
失敗時の Snapshot
以下のような設定を書くと、テストが失敗した時に自動的に Snapshot をとる事ができ失敗の原因追求にとても役立ちます。
RSpec.configure do |config| ・・・ config.after(type: :feature) do |example| DatabaseCleaner.clean take_screenshot if example.exception.present? end
ログ出力
steps は Ruby のコードなので、 p 等でデバック情報をコンソールに出力出来ます。
また、テスト対象の画面にあるJavaScript中に console.log() を書くとやはりコンソールに出力されるのでAjaxからみの複雑な機能のテスト作成に役立ちます。
steps ノウハウ
findの match オプション
page.find()メソッドはデフォルトでは多数がマッチするとエラーになります。CSSセレクターを工夫することでも対処しなくとも、絶対に最初にマッチしたものが正解の場合は以下のように match オプションを指定して逃げる事も出来ます。
page.find("form input[name='email']", match: :first)
findの visible オプション
page.find()メソッドはデフォルトでは表示されている要素しかマッチしませんが、非表示の要素の値を変更したい場合がありますが、そのような場合は以下のように visible オプションを指定することで可能になります。
page.find("form input[name='email']", visible: :all)
他のステップの利用
step はrubyのメソッドでは無いので、単純には呼び出せませんが、 Calling steps from other steps に書いてあるように、
の2つを適宜、使い分けると良いと思います。
step 'メール :email パスワード :password ででログイン' do |email, password| ... end step '社員Aのログイン' do step "メール 'a@kaisya.com' パスワード 'PDB4oq4X' ででログイン" end step 'ある社員のログイン' do send 'メール :email パスワード :password ででログイン', "e#{rand(1000)}@kaisya.com", "PDB4oq4X" end
その他
画面サイズ
アプリによってはCapyabara-Webkit画面サイズ(1024 x 768)では一部表示されない場合があるかもしれません。画面サイズは以下のようにして指定できます。
Capybara.register_driver :poltergeist do |app| Capybara::Poltergeist::Driver.new(app, window_size: [1280, 1024]) end
ここに書いたノウハウは、検索すると stackoverflow, qiita, ブログ等に書かれています。それらの情報を書いて頂いた方に感謝いたします。
昨年のまとめと今年の抱負
明けましておめでとうございます。
昨年のまとめと、今年の抱負を書きたいと思います。
2015年のまとめ
お仕事
昨年も開発と教育のお仕事を半々くらいしたでしょうか
- スタートアップ企業でのRuby on Rails教育
スタートアップ企業を中心にいろいろなな会社で Ruby on Railsの教育を行わせていただきました。現在スタートアップ企業では圧倒的なエンジニア不足に悩んでいます。スタート直後はともかく、サービス・プロダクトをリリースし資金調達に成功した会社は将来に備えエンジニアの育成にも前向きに取り組ん行ってほしいと願います。
- iOSアプリ+バックエンド開発
2014年末より、あるスマフォ系の会社でiOSアプリとそのバックエンドの開発を行わせて頂きました。今までたくさんの人に使ってもらえるようなiOSアプリを作ったことの無い私には、リリースされれば確実に多数の人が使うだろうアプリに気合を入れて取り組みました。そのバックエンド手慣れたRubyを使いGrapeでAPIサーバー、 Ruby on Railsで管理画面を作り、最近はやりのAPIサーバー+スマフォアプリを経験できました。
ただし、アプリのビジネスモデルの関係上 Appleの審査ををなかなか通らず、途中で大きな変更が入ったり、画像をに使った凝ったデザインのiOSアプリ作成に手こずりました。 最終的には残念ながら経営判断でリリースには至りませんでした。もちろん開発費は頂いたので文句はないのですが、自分の作ったものが日の目を見なかったのでは残念です。
- ボロライディングショップのリニューアル
長年、開発・メンテナンスを続けている ボロライディングショップ のリニューアルを行いました、プログラム的にはクロネコヤマトがカード決済のAPIをサポートしたのでそれを使うことにより使いやすいカード決済を追加しました、実は大きく時間が掛かったのは新デザインへの対応でした。
今までも、デザインはお客様が作ったPhotoshop画像を元に、私の方でHTML、CSSを作っていましたが、今回はお客様が画像、CSS、一部HTMLのブラッシュアップを行いました(最初のHTML、CSSは作りましたが)。お客様は車の会社でデザイナーをされてきた方デザインはベテランですが HTML、CSS は独学で勉強しただけですが、今回のリニューアルは自分でやりたいとの要望もあり共同で開発する事になりました。
お客様の方でGitを使うことは無理そうだったので、デザイン用のサーバーをクラウド上に作り、お客様のMacで作ったCSS、HTML、画像をSFTPするとすぐに確認できるようにしました。昼間はそれぞれの環境で作業を行い、夜に私の方で変更されたCSS、HTML、画像を取り込みGitにマージし結果をデザイン用サーバーにデプロイを行うサイクルで開発を進めていきました。
たった二人で、しかもある程度分業しているのに、離れた場所で作業を行うとコミニュケーションが問題になったりすることもありましたが、最終的には良いものが出来たと思います。 また、今回はカード決済が入った以外に購入ページの流れも変えたのですが、お客様がデザインを変更するためにページを細かくチェックするので、たくさんのバグが早い時点で発見されたのは良い事でした。
- 青森でのRuby on Rails教育
以前 会社のブログ に書きましたが、青森のSIerさまで Ruby on Railsの教育を行いました。普段は東京での仕事が多いのですが、たまには地方に行ってみと発見が多いですね。ニュース等で地方都市の空洞化という話題が上がっていますが、実際にそれを見てその切実さを感じました。
- CookPad 非エンジニア向け教育
これも 会社のブログ に書きましたが、クックパッド様で 非エンジニアの新人にプログラミングの教育を行いました。成長しているネット・IT系企業ではエンジニアだけでなく社員全員がプログラミングについて知っている事は企業の強みにつながると思います。
- Ruby on Rails+JS案件
年末から、大阪のある会社からRuby on Rails + JavaScriptのシングルページアプリのメンテナンスの仕事を引き受けました。JavaScriptの部分は jQueryの上に作られたトータル1万行近いアプリでメンテナンス性が良くないので、ReactJS等の最新のフレームワークの導入を提案したところ通ってしまい、近々ReactJSの置き換えが始まります。現在は置き換え作業を担保するためのEnd to EndテストをTunip + Capybara + PhantomJS で書いています。
スケジュール的にはきついのですが、RecatJSなど新しいものに触れられられのを楽しみにしています。
その他
仕事以外では
- Macが壊れた
8月に 2011年に買った MacBookPro (15inch)、12月に 2012年に買った Mac mini が壊れました。 MacBookProは無いと仕事が進まないので2015年春のモデルを買いました。Mac mini はCIサーバー等で使っていたのですが、クラウドへ移行して買わずに済ませました。
- 美食系イベントに参加
ソフトウェアエンジニア界の有名人(?) ogijun さんが主宰する会に参加し、時々おいしいものを食べてます。その結果、体重が着実に増えてます・・・・
- 久しぶりにワイン会に参加
若い頃にワインにはまり、いろいろなワイン会などの参加していたのですが、この10年くらいは遠ざかっていました。しかし Facbookの東京ワイン倶楽部というのに参加したのをきっかけに久しぶりにワイン会に参加してみました。素晴らしい料理と数々ワインに感動したり、最近のワイン事情を聞けて楽しかったのでが、帰りの駅で貧血になってしまい大変な思いをしました。
参加者の方に助けてもらったのですが、昨年一番の反省事項です。その後は注意して飲むようにしています・・・
2016年の抱負
毎年いろいろと抱負を書いてますが、あまり実現されてません。そこで今年は実現できそうな事を書きます。
- 情報を発信する
最近、ブログをあまり書いていません。 しかし、上に書いたようにいろいろと新しい事に挑戦したりはしています。最先端ではありませんが、新しい技術を使ってハマった点やその技術について分かり易く書いたりして行きたいと思います。また時には教育の事など普通のプログラマーは経験しない事なども書いていけたらと思います。
いろいろな技術系のイベントの二次会等で、今でもたまに「Yuumiさんのブログの記事で助かりました」という声を聞くことがあります。やはりアウトプットは重要です、地味ではありますが今年はブログなどのアウトプットを続けていきたいと決意しました。
- ブートキャンプ教育的なビジネス
現在、シリコンバレー では より実践的なプログラミング教育を行う MakeSchool のようなブートキャンプ教育がブームになっているようです。 一昨年の TechCrunch Tokyo のイベントで会った MakeSchool の創始者 Jeremy Rossmann のプレゼンに影響を受け、私も ブートキャンプ教育的なビジネス に付いてある方とお話をしました。しかし、それ切りになってしまいました。
昨年末の楽天のイベントで Jeremy Rossmann に再会できたこともあり、今年はこれをもう少し形あるものにしたいと思っています。
DockerでRuby on RailsのCI環境を作ろうとした話
少し前にCIサーバーとして使っていたMac miniがお亡くなりになり、CIサーバーどうしようと考えていました。 Dockerを使いCI環境を作り、さくらのクラウドにあるステージング用のサーバーで動かせば良いのでは! と思い立ち土日に挑戦してみました。
Mac上でCI環境を作る
Docker ToolboxをインストールすればDockerMachineやDocker Client(dockerコマンド)、VirtualBoxなどがインストールされます。
とりあえず、何も考えず docker-machine コマンドで Docker用のサーバー(環境)がVirtualBox上に立ち上がります。
$ docker-machine create -d virtualbox
オプションを指定してDockerサーバーメモリーやCPU数を増やしたりもできます。
後は docker コマンドを使い、Dockerイメージを作ったり、走らせたりして環境を作ります。 Dockerイメージへのアプリのインストール等の作業はDockerファイルに書きます。
CI環境作り
今回作るのは普通のRuby on RailsアプリのRSpecをJenkins上で実行します。そこで必要になるものは、
- Jenkns
- RDB: PostgreSQL
- Ruby、Ruby on Rails, Capybara, 等をインストールするのに必要なライブラリー
- PhantomJS 等のコマンド
- a_JP.UTF-8 ローケール
Rubyのインストールは Jenknsの rbenv Plugin がインストールしてくれるので環境には、OS標準のRubyを入れておけば良いです。
Ruby on Railsはご存知のように bundler がインストールしてくれるので、C言語拡張ライブラリーが構築できる環境があればOKです。
1. Jenkinsの公式Dockerイメージ
Jenkinsの公式Dockerイメージ が公開されています。これをベースにすれば楽じゃないか!
当然、Jenkinsは動きますが PostgreSQLインストールで ja_JP.UTF-8 ローケールを作ろうとしたのですが上手くいきません・・・ いろいろとトライしたのですがベースのUbuntuが通常とは異なるようであきらめました。
2. Java Runtime Environment がインストール出来ない !
JenkinsはJavaで出来ているので Java Runtime Environment (JRE) が必要です。apt-getでJenkinsをインストールすると依存関係から OpenJDK がインストールされるのですがインストール中の
Setting up ca-certificates-java ....
で止まってしまいます。仕方ないので、まずは OracleのJREを以下のようなDockerfileインストールしていました。
RUN apt-get install -y software-properties-common RUN add-apt-repository -y ppa:webupd8team/java RUN apt-get update RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections ENV DEBIAN_FRONTEND noninteractive RUN apt-get install -y oracle-java7-installer
でもなぁ〜 と思い、いろいろ検索していたら qiita に解決方法がありました。 Dockerサーバーを作るさいに以下のようなオプションを指定すれば上手くいきます。
$ docker-machine create --driver virtualbox --engine-storage-driver overlay dev
3. やっぱりsshdは必要
Docker内にはsshdを立ち上げてsshログインしたりせず、ログ・ディレクトリー等をホストのディレクトリーにバインドシして使うべきだ! という考え もあるようですが、やはり上手くいかない時の調査ようにsshログインできた方が良いですよね。
USER root RUN apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo 'root:パスワード' | chpasswd RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd ENV NOTVISIBLE "in users profile" RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"]
を Dockerfileの最後に書いておくとsshログイン出来ます。rootでログインするのは気持ち悪いですが・・・ 最終的な環境でネット上からはsshできないようにiptables(Firewall)等で制限しておいて下さいね。
4. 出来ました!
Dockerfile は以下のようになりました
FROM ubuntu:14.04 USER root # IMPORTANT http://qiita.com/moutend/items/18682948811175ed0e1d # docker-machine create -d virtualbox --engine-storage-driver overlay NAME RUN apt-get update RUN locale-gen ja_JP.UTF-8 RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime ## Libs RUN apt-get install -y wget git-core ruby build-essential bison flex sqlite3 autoconf \ libreadline6-dev zlib1g-dev libssl-dev libyaml-dev libxml2-dev libxslt1-dev libncurses5-dev libsqlite3-dev ## Tools RUN apt-get install -y fonts-takao-mincho libfontconfig1-dev libicu-dev libfreetype6 libpng-dev libjpeg-dev RUN wget -O /usr/local/bin/phantomjs https://github.com/Pyppe/phantomjs2.0-ubuntu14.04x64/raw/master/bin/phantomjs RUN chmod 755 /usr/local/bin/phantomjs ## PostgreSQL RUN apt-get install -y postgresql-9.3 libpq-dev postgresql-client-9.3 postgresql-contrib-9.3 RUN sed -i 's/local\s*all\s*all\s*peer/local\tall\tall\tmd5/' /etc/postgresql/9.3/main/pg_hba.conf USER postgres RUN /etc/init.d/postgresql start &&\ psql --command "CREATE USER テストアカウント WITH CREATEDB PASSWORD 'パスワード';" &&\ psql --command "CREATE DATABASE テスト用データベース WITH OWNER テストアカウント TEMPLATE template0 ENCODING 'UTF8' LC_COLLATE 'ja_JP.UTF-8' LC_CTYPE 'ja_JP.UTF-8';" ## Jenkins RUN wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - RUN sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list' RUN apt-get update RUN apt-get install -y jenkins ## SSH RUN apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo 'root:パスワード' | chpasswd RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd ENV NOTVISIBLE "in users profile" RUN echo "export VISIBLE=now" >> /etc/profile EXPOSE 22 ## Startup COPY startup.sh /usr/local/bin/startup.sh ENTRYPOINT ["startup.sh"]
- startup.sh は
#!/bin/bash /etc/init.d/postgresql start /etc/init.d/jenkins start /usr/sbin/sshd -D
5. クラウド上のUbuntuで動かない!
クラウド上の UbuntuサーバーにDocker環境 を作り、Mac上で作ったDockerイメージをsave, load したのですがなぜか PostgreSQLが起動しません (Jenkins, sshd は起動されます)。
# /etc/init.d/postgresql start Starting PostgreSQL 9.3 database server * The PostgreSQL server failed to start. Please check the log output: 2014-12-01 17:41:37 UTC FATAL: could not access private key file "/etc/ssl/private/ssl-cert-snakeoil.key": Permission denied
調べてみると、ここにissue がありました。 しかし、解決しませんでした・・・・ 疲れた ・・・
Dockerの完成度はこんなものなのでしょうか???
今回の結論
というわけで、ステージングサーバーにJenkinsを直接インストールしました ^^);
ステージングサーバーのruby(gem)は /usr/local/ にインストールされているので、Jenkinsのrbenvとは独立しています。RDB(PostgreSQL)は共有されますがtest用databaseは別なので問題なく同じサーバーで動きますした。
Jenkins CI環境でドラック&ドロップのテストが落ちた時にやった事
いまだに Jenkins で消耗してます ^^;
2015-12-17 追記
現象
現在のメンテしているプロジェクトは下のようなCI環境で行っています、 この中のエンドツーエンド テストのうち、jQuery-UIのSortableを使った並べ替え部分のテストが通っていない事に気がつきました。
- OS: Mac miniの上のVMWareで動くUbuntu 14.04 server
- CI: Jenkins
- Test: RSpec feature + Capybara + PhantomJS (poltergeis)
ドラック&ドロップの部分で例外等は発生してないのですが、全く処理が無視されていて結果としてRSpecが失敗になります。それ以外のJavaScriptを使うUIのRSpecは通っています。
もちろん開発環境のMacではRSpecは通りますし、Mac/Ubuntuで動くアプリも正常に動作します。
原因追求
ログをチェックするなど基本的な事は行い、後はGoogle先生に問い合わせた先のIssueを読んだりで半日近い時間が過ぎましたが解決へのヒントは全く得られず・・・・
古いバージョンのPhanotomJSはXvbfなどが無いとheadless環境でテストが出来ないようでししたが、今は問題無いです。試しに Xvbfを入れてみましたが結果は同じでした。
次の日の朝、ふとGUI(X Window System)のあるUbuntuで動かしたらどうだろうと思い、VMWare上に環境を作り、RSpecを実行すると・・・・ 通ります !!
ということで、CI環境の Ubuntu を Server版ではなく Desktop版 にすれば良いことがわかりました。 幸運なことに(?) Mac mini + VMWare でUbuntuを動かしているので Desktop変えることは出来ます。 しかし、作業時間も掛かるるし、非力な Mac mini 上で Desktop版を動かすのはねぇ・・・ と悩みました。
もう少し検討
本当にGUI(X Window System)が動いてないとPhanotomJSは正しく動作しないのでしょうか?
試しに、GRUBの設定を変え GUI(X Window System) が起動しないようにして、 GUIの動いてないUbuntuにSSHでログインしてRSpecを実行すると・・・・ 通りました ^^)/
多分、PantommJSでドラック&ドロップを動作させるには、Xやマウスなどの何らかのライブラリー等のインストールが必要なのだと思われます。Server版ではこのライブラリーが入らないのではないでしょうか?
疲れたのこれ以上は追求しませんでしたが・・・
判りました! 日本語フォントをインストールすれば良いようです。
対処結果
現在の Server版をやめてDesktop版を入れるのは面倒なので Ubuntu/Ubuntuサーバーにデスクトップをインストールする手順 を参考に現行の Ubuntu serverにDesktopをインストールし、GRUBの設定を変え GUI(X Window System) が起動しないようにしました。
Ubuntu なら
$ sudo apt-get install fonts-takao-mincho
めでたしめでたし・・・・
山口小夜子の映画「氷の花火」を見た個人的な記憶
めずらしく、技術的ではないことを書きました。
「山口小夜子」覚えてますか? 知ってますか? ファッションとは全く無縁な私がこんなことを書くのは恥ずかしいのですが、若いころ山口小夜子は憧れの人でした。
先週の土曜日に、映画 「氷の花火」 を見て気まました。映画は山口小夜子の遺品の整理風景から始まり、彼女と一緒に仕事をした人たちへのインタビューから彼女の一生を描き、最後に現在のモデルを使い山口小夜子を再現するというプロジェクトという内容でファンとしてはとても見応えのある内容でした。
幾つかのメモ:
- 山口小夜子といえば黒髪のおかっぱに切れ長の目ですが、実はくっくりとした目で切れ長はメークで作っていた!
- 昔、デーモニッシュな資生堂のCMで知った セルジュルタンス のモロッコ・マラケシュの邸宅でのインタビューや小夜子と映った昔の画とは妄想が膨らむ収穫だった
- 資生堂で小夜子の出たCMを作った方々へのインタビューを見ると、かなりアーティスティックなCMを作れる、それを認めてくれる経営者のいた経済成長中の日本があったんだなぁ〜 というノスタルジー
- 遺品の整理シーンに出てきた蔵書。コクトー、寺山修司、タルコフスキー、世阿弥・・・私も持っていたものが多い、彼女の書いた本を読んだりして影響を受けていたんだろうな
彼女は軽薄な女よ:
私が若い頃、不思議な画廊のマダムと友達になり、画廊にも出入りしたり、マダムと食事に行ったり、お芝居に行ったり・・・そんな中で私が山口小夜子のファンだと言うと、「彼女は軽薄な女よ・・・・」と答えたことはずっと記憶に残っていました。マダムは芸術家の芸術にせっする態度のには厳しい哲学を持った人でした、当然ながらたくさんの美術、お芝居、それらに関係するパーティーの中で暮らしている方でした。
山口小夜子はファッションモデルとして世界一の座を射止めた人です、高田賢三が「かぐや姫が降りてきたという感じ・・・」とインタビューの中で語っていましたが、たしかにランウェイの彼女には神がった美しさがありました。
しかし、トップモデルをずっと続けることはできません。モデルを辞めた後の彼女は、舞踏、お芝居、若い芸術家とのコラボなど、いろいろなものに取り組んでいる画像、共演者たちのインタビューが流れながら映画は進んでいきました。 そこに映る彼女には心ときめく山口小夜子を発見できませんでした。 彼女自身の頑張りはわかります、しかし表現者(芸術家)としてまだまだだったのでしょうか。画廊のマダムの言っていた事がわかった気がしました。
一度頂点を極めてしまった人のその後は辛い・・・・ しかし、彼女が山口小夜子という素晴らしい世界を見せてくれた事は事実です。
ありがとう、山口小夜子さん。
プロビジョニングツールをChef-soloからItamaeに替えてみました
Chef から Itamae ?
EY-Officeではサーバーソフトの構築を行うプロビジョニングツールとして現在は Chef-solo を使っています。しかし Chef-solo は終了するらしいのですが、その移行先が良いように思えず悩みながら使い続けてきました。
そんなとき登場してきた Itamae はシンプルでとても魅力を感じました。詳しくは クックパッド開発者ブログのItamae記事 をご覧下さい。
今回時間があったので、EY-Officeの開発支援サーバー (Redmine, Git/Gnatara, CI ...)の全プロビジョニングをChefからItamaeに置き換えてみました。書き換えたレシピは約700行です、そこで感じた事を書きます。
Itamaeの良いところ、良くないところ
1. 情報が貧弱
まず良くない点ですが、Chefのドキュメント に比べると情報の量、質ともに低いです。どうしても思ったように動作しない場合は Itamae の実装を読むことが度々ありました。
公式なドキュメントは
今回、参考になった情報は
2. Chefとは動作モデルが違うぞ!
最初に戸惑ったのでは、Chefとの動作モデルの違いです。
Chef-solo の利用イメージは
- Chefを開発環境にインストール
- 利用開始時にChefのRubyインタプリターを含む実行環境をターゲットにインストール (knife solo prepare コマンド)
- レシピの作成・修正
- 実行 (knife solo cooke コマンド)
- レシピの文法チェック等
- レシピを含むcookbookをターゲットへ転送
- レシピをターゲットで実行
- 問題があれば 3.に戻る
ですが、Itamaeは
- Itamaeを開発環境にインストール
- レシピの作成・修正
- 実行 (itamaeコマンド)
- レシピに対応する操作をsshを使いターゲット上で実行
- 問題があれば 2.に戻る
ChefのレシピのRubyコードはターゲット上で実行されますが、Itamaeはレシピは開発環境で実行されます。 したがって、ターゲットの状況によって処理を変更する部分、たとえば not_if や only_if にはRubyのコードは書けず、shellのコードを書く必要があります。
例
Chef
execute "apt-get-update" do command "apt-get update" only_if { File.mtime('/var/lib/apt/lists') < Time.now - 86400 } end
Itamae
execute "apt-get-update" do command "apt-get update" only_if "exit $(( `stat -c %Y /var/lib/apt/lists` > (`date +%s` - 86400) ))" end
実行時にターゲット上の情報を動的に取得できないので、今回はレシピ(コード)で自動的に取得するのをあきらめnode で指定するようにした部分もありました。
3. Chefに似ているけど・・・
Itamae のResource (DSL, 命令?)はChefと似た作りになっていて、そのまま動くものもありますが、少し違う部分もあります。また無いものもあります。
- Chef の
cooke_file
はremote_file
- Chef の
remote_file
に相当するものは無い? - ファイル等のモード指定に8進数が使えない
user
のaction: :modify
が出来ないcron
Resource が無い- ・・・
4. defineを使おう
無いResourceを簡単に作れる Definitions があり、これを使って足りないResourceを補いました。
# URL指定でネット上のファイルをダウンロードするResource define :external_file, url: nil, owner: nil, group: nil, mode: nil do p = params execute "download #{p[:name]} from #{p[:url]}" do cmd = "curl -s -f -L -o #{p[:name]} #{p[:url]}" cmd += " && chown #{p[:owner]} #{p[:name]}" if p[:owner] cmd += " && chgrp #{p[:group]} #{p[:name]}" if p[:group] cmd += " && chmod #{p[:mode]} #{p[:name]}" if p[:mode] command cmd not_if "test -e #{p[:name]}" end end # crontab 設定用 Resource define :cron_for, crontab: nil do user = params[:name] crontab = params[:crontab] crontab_path = "/tmp/crontab.#{rand(10000)}" file crontab_path do content crontab not_if "crontab -u #{user} -l" end execute "crontab -u #{user}" do command "crontab -u #{user} #{crontab_path} && rm #{crontab_path}" only_if "test -e #{crontab_path}" end end
利用レシピ
ruby_package = "ruby-2.2.2" external_file "/usr/local/src/#{ruby_package}.tar.bz2" do url "http://cache.ruby-lang.org/pub/ruby/2.2/#{ruby_package}.tar.bz2" end cron_for "root" do crontab <<-EOH # PATH=/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # 30 5 * * * /usr/local/bin/backup.sh >> /var/tmp/bakup.log 2>&1 EOH end
Chef の cron
Resource は crontab形式を知らなくてもDSLで設定出来るようになっていましたが、define であまり高度な機能を作る事は考えない方が良いと思いました。やってみると直ぐに限界が見えます。
どうしても高度な Resourceが必要な場合は Quirra: itamae resourceの書き方 を参考に Resource を作った方が良いと思います。
5. シンプルで早い
動作モデルの違いやResourceの充実度の違いはありますが、Itamaeはレシピが実行されるまでの時間が早くとても快適です!
Chef-soloを利用していて、複雑化するChefの推奨移行環境に疑問を持つ方は、ぜひ一度 Itamaeを試してみると良いと思います。
今回作ったレシピ
今回作ったレシピの一部です(ubuntu 14.04用です)
execute "apt-get-update" do command "apt-get update" only_if "exit $(( `stat -c %Y /var/lib/apt/lists` > (`date +%s` - 86400) ))" end execute "ensure ja_JP.UTF-8" do command "locale-gen ja_JP.UTF-8" not_if "grep -c 'ja_JP.UTF-8' /var/lib/locales/supported.d/local" end execute "ensure JST" do command <<-EOH echo "Asia/Tokyo" | sudo tee /etc/timezone sudo dpkg-reconfigure --frontend noninteractive tzdata EOH not_if "date | grep -c 'JST'" end # shell を zsh に変更 (ユーザーは node[:user]で指定) file "/etc/passwd" do action :edit block do |content| content.gsub!("/home/#{node[:user]}:/bin/bash", "/home/#{node[:user]}:/bin/zsh") end end
postgreSQLをインストールし DBのencoding, localeを変更する
%w{postgresql-9.3 libpq-dev postgresql-client-9.3 postgresql-contrib-9.3}.each do |pkg| package pkg do action :install end end service "postgresql" do action [:start, :enable] end link "/usr/local/bin/psql" do to "/usr/local/pgsql/bin/psql" end execute "initdb" do user "postgres" cwd "/var/lib/postgresql/9.3/" command <<-EOH rm -rf main /usr/lib/postgresql/9.3/bin/initdb -D main --encoding=UTF-8 --locale=ja_JP.UTF-8 rm main/pg_hba.conf main/pg_ident.conf main/postgresql.conf ln -s /etc/ssl/certs/ssl-cert-snakeoil.pem main/server.crt ln -s /etc/ssl/private/ssl-cert-snakeoil.key main/server.key EOH action :nothing end execute "re-initdb with ja_JP.UTF-8" do command "echo re-initdb" notifies :stop, 'service[postgresql]', :immediately notifies :run, 'execute[initdb]', :immediately notifies :start, 'service[postgresql]', :immediately not_if "/usr/lib/postgresql/9.3/bin/psql -U postgres -c \"select * from pg_database where datname ='template0'\" " + "| grep -c 'ja_JP.UTF-8'" end
注) specinfra 2.41.1 で Ubuntu14.04でPostgreSQL起動中判定の問題が対処されたので暫定パッチは不要になりました :-)