Google Apps Script カスタムLoggerでログをspreadsheetに書き出す

Google Apps Scriptの中でLoggerで吐くログをシートに書き出すカスタムLoggerを作ってみた。

こういうシートができあがる。

20140618-mylogger

Logger.log()ではなくて恒久的に保存するためのロガーを作る。例えばTriggerで時限式実行するスクリプトのログを保存しておきたいとかの用途。

いつものとおりライブラリ化する。MyLoggerという名前で。

function log_sheet_() {
  var sheet_name = 'log';
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sh = ss.getSheetByName(sheet_name);
  if (sh == null) {
    var active_sh = ss.getActiveSheet(); // memorize current active sheet;
    sheet_num = ss.getSheets().length;
    sh = ss.insertSheet(sheet_name, sheet_num);
    sh.getRange('A1:C1').setValues([['timestamp', 'level', 'message']]).setBackground('#cfe2f3').setFontWeight('bold');
    sh.getRange('A2:C2').setValues([[new Date(), 'info', sheet_name + ' has been created.']]).clearFormat();
    
    // .insertSheet()を呼ぶと"log"シートがアクティブになるので、元々アクティブだったシートにフォーカスを戻す
    ss.setActiveSheet(active_sh);
  } 
  return sh;
}

function log_(level, message) {
  var sh = log_sheet_();
  var now = new Date();
  var last_row = sh.getLastRow();
  sh.insertRowAfter(last_row).getRange(last_row+1, 1, 1, 3).setValues([[now, level, message]]);
  return sh;
}

function debug(message) {
  log_('debug', message);
}

function info(message) {
  log_('info', message);
}

function warn(message) {
  log_('warn', message);
}

function error(message) {
  log_('error', message);
}

function fatal(message) {
  log_('fatal', message);
}

呼び出す側のプロジェクトでは、先ほど作ったMyLoggerのプロジェクトキーを登録して、こうやって呼び出す。初回呼び出し時に”log”という名前でシートを作成し以降は末尾の行にログが追加される。

MyLogger.info('works!');

Google Apps Scriptで行データを連想配列として扱う

Goolge Apps Scriptでspreadsheetの各列にアクセスするときに、列番号を指定してアクセスするコードを書いてると列追加するとスクリプトが壊れる。そこで1行目(ヘッダ行)をキーにアクセスするコードをライブラリにまとめてみた。普通なんだけどぐぐっても出てこなかったので。

以下をUtilsというプロジェクト名で保存。

/**
* returns keys located at top of spreadsheet 
*
* @param {sheet} sh Sheet class
* @return {array} array of keys
*/
function headerKeys(sh) {
  return sh.getRange(1,1,1, sh.getLastColumn()).getValues()[0];
}

/**
* Convert a row to key-value hash according to keys input parameter
*
* @param {array} array
* @param {array} keys
* @return {array} key-value mapped
*/
function rowToHash(array, keys) {
  var hash = {};
  array.forEach(function(value, i) {
    hash[keys[i]] = value;
  })
  return hash;
}

参照側のプロジェクトにて、ライブラリ機能を使ってincludeすると以下のようにキーで行配列を扱える。

// 住所録 名前から住所を取得
function getAddressByFullName(fullName) {
  var sh = SpreadsheetApp.getActive().getSheetByName('addresses');
  keys = Utils.headerKeys(sh);                                                       // ヘッダ行を取得
  var values = sh.getRange(2, 1, sh.getLastRow()-1, sh.getLastColumn()).getValues(); // データ部分(2行目以降)取得
  for (var i = 0; i < values.length; i++) {
    var row = values[i];
    row = Utils.rowToHash(row, keys); 
    if (row['full_name'] == fullName) {
      return row['address'];
    }
  }
}

さあこれで列が追加されても大丈夫。RDBMSだと普通なことGAS何でこんなに大変なの!とか考えたら負けです。

早く卒業したいGASおじさん。つっこみありましたらお手柔らかによろしくお願いします!!

opensslコマンドでSNIな証明書をチェックする

あるサーバの証明書のCNをチェックしようとして、opensslコマンドで確認すると想定と違うCNが返ってくる何で! ということが起きて軽くはまってしまった。ブラウザで証明書確認すると問題ない。

結論としてはサーバ証明書がSNIなやつであれば、-servernameを付けるべし。だった。

$ openssl s_client -connect test.example.com:443 -servername test.example.com 2>&1 < /dev/null 

Google Apps Scriptをつかってみた

某案件でGoogle Spreadsheetを使う機会があって少しかじってみた。浅くメモ。

About Google Apps Script

Google Apps ScriptはGoogleプラットフォーム上で動くサーバサイドjavascript。spreadsheetに限らずGoogle Apps上のデータと連携してアイデア次第で何でもできる言語。例えばGmailでqueryをかけてbodyをパースしてspreadsheetにデータ貯めて、解析してPDF変換してGmailに送る、みたいなこともできる。

Google Apps内に限らず、外部のAPIサーバからデータを取ってきたり、逆に外のサーバにデータを送ることもできる。ただし、Google Platformで実行されるのでファイアウォールを超えてイントラネットに接続するという要件はセキュリティ的に難しい制約はある。

triggerを使うとイベント起因で関数を実行できる。ボタンクリックとかドキュメントを開いたときとか。

triggerの例

ドキュメントオープン時にメニューボタンを追加する例。

function onOpen() { // automatically run this on open a spreadsheet.
  var menuEntries = [ {name: "say hello", functionName: "sayHello"} ];
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  ss.addMenu("my menu", menuEntries);
}

function sayHello() {
  Logger.log('Hello World!');
}

時限式のtriggerでcron的なことも可能で、もちろんMac Bookを閉じた後もGoogleプラットフォーム上で実行される。triggerを使えば定期的にGmailをチェックして何かやるみたいなこともできる。

書いたスクリプトをWebアプリとして公開する設定をするとURLのエンドポイントがもらえる。triggerにはdoGet()というのもあってURLをGETしたトリガで何かできたりする。例えばGETトリガでスクリプトへcallbackされるのでURLのqueryのkey, valueをspreadsheetにためるといったこともできる(簡易WebAPI!)

制約

実行遅い

結構実行遅い。APIのcallを少なくを心がける。ノウハウは Best Practicesを参照のこと。

var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('シート1');

//bad
for (n = 0; n < 2; n++) {
    sh.getRange(n,1).setValue('Value');
    sh.getRange(n,2).setValue('Value');
}

//good
sh.getRange(1,1,2,2).setValues([['Value', 'Value'], ['Value', 'Value']]);

1回の実行 5分まで

実行が5分過ぎるとスクリプトは終了する。

リファレンス

自分はこの辺みてます。

日本語でヒットするサイトはExcelっぽい雰囲気が漂っててあまり見ていない(笑) もちろん参考にさせていただいているけど。

おもったところ

開発環境はブラウザ上のスクリプトエディタを使わざるおえないので、脳がブラウジング脳になるのが難点。viモードほしい。

他のOSSと組み合わせるとアイデア次第で面白いことできそう。Fluentd Dashboard のデモはすごかった!

Viewを作らなくていいので楽!

カジュアルに初めてカジュアルに終えるのが良いかと。それなりの規模であればRailsとかで真面目に作ろう。

Google Apps Scriptの共通関数をライブラリでまとめる

前回に引き続きGoogle Apps Script。共通関数をライブラリにする方法。

共通の処理をライブラリとして独立して管理できるのか調査した。Libraryの機能を使うとできることがわかったのでメモ。公式のドキュメント

spreadsheet -> スクリプトエディタ -> スクリプト作成 の順番でたどるとspreadsheetのバインドされたscriptができあがるが、このスクリプトはライブラリにはできない。ライブラリとして作るにはGoogle Apps Home -> スクリプト作成する必要がある点が最初分かりにくかった。

ライブラリ作成後、spreadsheetのスクリプトエディタ側でライブラリを指定してあげればOK。実際はライブラリにユニークに割り当てられるproject keyを使う側で指定する。

イメージ

20140530-script-lib

最低限必要なこと

  • 利用ユーザへライブラリへのアクセス権限が必要
  • project keyの伝達が必要
  • ライブラリスクリプトは1個以上のバージョニングをつけて保存すること(インクルード側でバージョン指定ができる)

その他

  • インクルードライブラリの識別子(クラス名みたいなもの)は任意の文字列を指定できる。たとえばMyPicasaApiとか。プロジェクト内ではMyPicasaApi.doSomething()で呼べる。もし既存のクラス(たとえばUiApp)と被ると既存クラスがオーバライドされる。
  • privateなメソッドを作るには _ で終わる名前をつけるとautocompleteで出てこなくなる。 e.g. myPrivateMethod_()
  • autocompleteで表示されるドキュメントを与えたいなら、関数の上にJSDoc style documentationでコメントを書く。
/**
* Raises a number to the given power, and returns the result.
*
* @param {number} base the number we're raising to a power
* @param {number} exp the exponent we're raising the base to
* @return {number} the result of the exponential calculation
*/
function power(base, exp) { ... }

GAS力がさらに上がりました!以上!

Google Apps Scriptを速くするためのベストプラクティス

Google DevelopersサイトのBest Practicesを要約してみた。

サービスのcallを最小限に

javascript内で閉じた処理の方がサービスをcallするよりも早い。サービスのcallとはspreadsheetのデータを読み出したり書き出したり、Docsを参照したり、SiteやTranslateやUrlFetchとか使ったり。

バッチオペレーション

spreadsheetのreadとwriteの回数を最小化する。readとwriteは重い処理。1オペレーションでデータを配列にreadし、1オペレーションで配列にwriteすること。forreachの中で毎回callするのは遅いので、配列に溜めて最後に1回で書きだす。たとえばsetBackgroundColor(value)をたくさん呼ぶのではなくsetBackgroundColors(values);1回にする。

Cache Class

Cacheというキャッシュクラスがあるので、頻繁に使うけど遅いデータはキャッシュしとく。key valueでキャッシュできる。

function getRssFeed() {
   var cache = CacheService.getPublicCache();
   var cached = cache.get("rss-feed-contents");
   if (cached != null) {
     return cached;
   }
   var result = UrlFetchApp.fetch("http://example.com/my-slow-rss-feed.xml"); // takes 20 seconds
   var contents = result.getContentText();
   cache.put("rss-feed-contents", contents, 1500); // cache for 25 minutes
   return contents;
}

Using Client Handlers for More Responsive UIs

UI applicationを使う場合でイベントコールバック(たとえばボタンをクリックするとか)を使う場合、ClientHandlerを使うと高速化できる。サーバサイドでイベントキャッチするのではなく、クライアントサイド(ブラウザ)でhandleすることができるので。使う機会がいまのところ無いので詳しくはない。

以上!

Vienna, Salzburg, Zurich, Parisを列車で巡ってみた – 後編

ウィーンからパリまで列車の旅の後半編。前半はこちら

チューリッヒ

  • 物価高い!!!
  • チェックインでトラブった
  • チーズフォンデュは濃厚

物価高い!!evian 500mlペットボトルが400円強、昼飯2人でパスタとコーラ飲んで5000円弱とかで財布寒い。ホテルもベースが高かったのでチューリッヒではGuest Houseにしました。泊まったGH Guesthouse Brauerstrasse。期待していなかったのですが意外、アパートメントタイプでちゃんと専用個室で風呂もトイレも専用。部屋広くてソファもあったりしていい部屋でした。ただしチェックインは難易度高い。フロントは無くてキーボックスからキーを入手する必要があるので事前にキーコードがメールされてきます。僕はキーコードの存在を忘れてて鍵ゲットするまでちょっと手間取りました。

スイスは1泊だけの滞在なのでスイスといえば…チーズフォンデュでしょってことで食べました。本場はチーズ風味が臭いです。味はおいしかったけど濃厚すぎて自分はダメでした。ちなみに本当は冬の食べ物らしいです。日本の鍋的感覚か。

IMG_5429IMG_5448

パリ

  • お洒落な街、でも地下鉄とっても汚い
  • エッフェル塔、凱旋門
  • ショッピング

パリは2泊だけの滞在で駆け足。観光はエッフェル塔、凱旋門、ノートルダム寺院の3カ所。親戚の子供の洋服土産を探したりで町歩きしながらセーヌ川を眺めるそんな感じでした。パリは多人種な街ですね。セーヌ川沿いのBrasserieでワイン飲みながらってのはなかなか良いものでした。

DSC_8903DSC_8936

お金, 手配の方法など

誰かの参考になるかもしれないので書いてみる。ツアーは使ってなくてすべて個人手配です。

2人で飛行機24万円、列車代が9万円、ホテルが10万くらい、その他食費等で合計55万くらい。飛行機は安かったけどユーロ高め(1ユーロ142円くらい)だったので結構財布寒い感じですね。結構食費は節約系でした。

飛行機はエールフランスのサイトで直接調べたら安く行けることが分かって、さらに安くならないかなとトラベルコちゃんで調べたらHISが同じ便で1万円くらい安かったので申し込み。行きはシャルルドゴール空港経由パリ行き、帰りはパリから成田への直行便です。

ホテルはExpediaで。選定方法はまず値段みて、あとGoogle Mapみながら交通の便が良い立地かどうかで選ぶ感じ。(このあたりネット系でコンシェルジュ的(楽々幹事さん的のり)なサービスあってやってくれるサービスとかないですかねえ。)

列車RAILEUROPEで列車時刻調べてチケットを事前に購入しました。ユーロレイルパスは使ってません(個別購入の方が安かったので)

まとめ

ウィーン、ザルツブルク、チューリッヒ、パリと列車で巡ってきた。弾丸な日程だったけど充実した旅でした。次は1都市滞在でゆっくりした旅がしたい!(けどかなり先だろう)

Vienna, Salzburg, Zurich, Parisを列車で巡ってみた – 前編

5月の前半、ウィーンからパリまで列車の旅をしたので簡単にまとめてみます。

今回は嫁と二人旅、世間的にはいわゆる新婚旅行というやつです。旅行先の選定は、まず嫁希望でオーストリアのウィーンとザルツブルクが決まって、僕がパリ行ってみたかったのでパリが決まって、列車の旅にしようということが決まって、ウィーンからパリの間でちょうどチューリッヒがあったのでチューリッヒに寄りました。

ウィーン

  • 音楽の都
  • オペラ座ツアーオススメ
  • 観光スタートダッシュ

僕が大学時代オーケストラやっていて嫁が現在チェロ習い中ということもあって 楽友協会 (Wiener Musikverein)へクラシックコンサートききにいきました。黄金ホールが新年のウィーンフィルのニューイヤーコンサートで有名です。日本でクラシックコンサート聞く場合だと結構ジーパン、Tシャツってのもありではありなんですが、さすがヨーロッパ、みなさまお洒落紳士淑女なんですね。僕らは昼間観光した後にいったんホテル戻って綺麗な服に着替えて出かけました。普段クラシック聞かない人でも雰囲気満点で楽しめるとおもいます!

IMG_5300IMG_5306IMG_5319

ちなみにチケットは公式ページからクレジットカードで買えます。レシートをA4に印刷して当日会場のチケット預かりで提示すればチケット引き換えできます。

IMG_5303

また、昼間にやってるオペラ座ツアーは5ユーロでみれるので安くてお得でオススメです。オペラ座内部をガイドさんが40分くらいかけて案内してくれます。開始時刻は公式ページを参照。予約無しで開始15分くらい前に行って5ユーロ支払えばOKです。

その他、ハプスブルク家関連。ホーフブルク宮殿、シシィ博物館、美術史美術館、シェーンブルン宮殿、と弾丸で巡りました。2日で巡ったのですが時間足りなかった(笑) シェーンブルク宮殿は街から少し離れたところにあって宮殿の裏手に丘があってそウィーンの街の眺めオススメです。

あとグルメ的にはウィーンにいったらウィーナーシュニッチェル、ザッハトルテは食べとけとのことでした。ザッハトルテってHotel Sacher Viennaというところのカフェのチョコケーキだからザッハトルテ(sacher torte)というのは今回知りました。

DSC_8694DSC_8705DSC_8704DSC_8617DSC_8688

 

 

 

 

ザルツブルク

  • サウンドオブミュージック
  • ザルツ = 塩、ブルク = 城。モーツァルトが生まれた街
  • 郊外にはRedBullの本社があるらしい

ザルツブルクはけっこう田舎で、市より小さくて村より大きい感じの規模でした。市内観光と併せて郊外まで連れて行ってくれるツアーにjoinするのもオススメです。

嫁が映画サウンドオブミュージックのファンでロケ地を巡る半日ツアーに参加しました。利用したのは現地のツアー会社PANORAMA ToursのOriginal Sound of Music Tour、Webサイトから予約できます。英語ガイドさんつきのバスツアーなんですがなかなか陽気なおばちゃんで英語分からないところもありましたが楽しめました。なお、英語ツアーに日本人が参加するのは珍しいらしく参加するともれなくガイドさんイジってくれます(汗) ちなみに、日本語ツアーもあります。

IMG_5361DSC_8790DSC_8815

列車での移動

今回はオーストリア国鉄 OBB 運行のRajetというのと、フランス国鉄運行のTGVにのりました。TGVは結構狭い。Rajetは列車内Wifiついてて快適でした!

DSC_8762IMG_5460IMG_5398

次回へ続く

オーストリア編はここで終わり。1ポストで収まらなかったので続きは次回へ

test-kitchen使ったメモ

最近はImmutable Infrastracture盛り上がりでChefやPuppetが語られる機会が少なくなって気がしますが、それはChef/Puppetが成熟してきた証拠? test-kitchenを使ってみたメモです。

GETTING STARTED GUIDE を写経。詳細はリンク先の原文を参照してください。

拙作のrbenv-cookbookをtest-kitchenをつかってVagrant上の仮想マシンでテストするところまで進めました。なお、Vagrantのバージョンは1.5.1を使いました。

インストール

$ gem install test-kitchen
$ kitchen -v
Test Kitchen version 1.2.1

また、あらかじめcookbookをgit cloneしておきます

$ git clone git@github.com:niku4i/rbenv-cookbook.git
$ cd rbenv-cookbook

kitchen init

$ kitchen init --driver=kitchen-vagrant
      create  .kitchen.yml
      create  test/integration/default
      create  .gitignore
      append  .gitignore
      append  .gitignore
         run  gem install kitchen-vagrant from "."
Fetching: kitchen-vagrant-0.14.0.gem (100%)
Successfully installed kitchen-vagrant-0.14.0
Parsing documentation for kitchen-vagrant-0.14.0
Installing ri documentation for kitchen-vagrant-0.14.0
1 gem installed

.kitchen.ymlが作られた。中身は以下のとおり。ubuntuはひとまず不要なのでplatformから削除。またinitで作成されたyamlに登録されているcentos-6.4ではkitchen createしたときにvagrant upできなくてエラーになってしまったので、boxイメージが悪いのかなと判断して6.5を使うように変更した。

---
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
#  - name: ubuntu-12.04
#  - name: centos-6.4
  - name: centos-65

suites:
  - name: default
    run_list:
      - recipe[rbenv-cookbook::default]
    attributes:

kitchen listコマンドで確認できた。

$ kitchen list
Instance           Driver   Provisioner  Last Action
default-centos-64  Vagrant  ChefSolo     <Not Created>

kitchen create

kitchen createでインスタンスをアップ

 $ kitchen create default-centos-65
-----> Starting Kitchen (v1.2.1)
-----> Creating <default-centos-65>...
       Bringing machine 'default' up with 'virtualbox' provider...
       ==> default: Box 'opscode-centos-6.5' could not be found. Attempting to find and install...
           default: Box Provider: virtualbox
           default: Box Version: >= 0
       ==> default: Adding box 'opscode-centos-6.5' (v0) for provider: virtualbox
           default: Downloading: https://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box
       ==> default: Successfully added box 'opscode-centos-6.5' (v0) for 'virtualbox'!
       ==> default: Importing base box 'opscode-centos-6.5'...
       ==> default: Matching MAC address for NAT networking...
       ==> default: Setting the name of the VM: default-centos-65_default_1395719095721_73691
       ==> default: Fixed port collision for 22 => 2222. Now on port 2201.
       ==> default: Clearing any previously set network interfaces...
       ==> default: Preparing network interfaces based on configuration...
           default: Adapter 1: nat
       ==> default: Forwarding ports...
           default: 22 => 2201 (adapter 1)
       ==> default: Running 'pre-boot' VM customizations...
       ==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...           default: SSH address: 127.0.0.1:2201
           default: SSH username: vagrant
           default: SSH auth method: private key
           default: Error: Connection timeout. Retrying...
       ==> default: Machine booted and ready!
       ==> default: Checking for guest additions in VM...
           default: The guest additions on this VM do not match the installed version of
           default: VirtualBox! In most cases this is fine, but in rare cases it can
           default: prevent things such as shared folders from working properly. If you see
           default: shared folder errors, please make sure the guest additions within the
           default: virtual machine match the version of VirtualBox you have installed on
           default: your host and reload your VM.
           default:
           default: Guest Additions Version: 4.3.8
           default: VirtualBox Version: 4.2
       ==> default: Setting hostname...
       Vagrant instance <default-centos-65> created.
       Finished creating <default-centos-65> (11m27.39s).
-----> Kitchen is finished. (11m27.70s)
  • vagrantでインスタンスを起動する
  • boxが登録されてなければネットワークから探してくる
$ kitchen list
Instance           Driver   Provisioner  Last Action
default-centos-65  Vagrant  ChefSolo     Created

kitchen converge

レシピをインスタンスに適用する

$ kitchen converge default-centos-65
-----> Starting Kitchen (v1.2.1)
-----> Converging <default-centos-65>...
       Preparing files for transfer
       Preparing current project directory as a cookbook
       Removing non-cookbook files before transfer
-----> Installing Chef Omnibus (true)
       downloading https://www.getchef.com/chef/install.sh
         to file /tmp/install.sh
       trying wget...
       Downloading Chef  for el...
       downloading https://www.getchef.com/chef/metadata?v=&prerelease=false&p=el&pv=6&m=x86_64
         to file /tmp/install.sh.1851/metadata.txt
       trying wget...
       url      https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-11.10.4-1.el6.x86_64.rpm
       md5      3fe6dd8e19301b6c66032496a89097db
       sha256   edd5d2bcc174f67e5e5136fd7e5fffd9414c5f4949c68b28055b124185904d9f
       downloaded metadata file looks valid...
       downloading https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chef-11.10.4-1.el6.x86_64.rpm
         to file /tmp/install.sh.1851/chef-11.10.4-1.el6.x86_64.rpm
       trying wget...
       Comparing checksum with sha256sum...
       Installing Chef
       installing with rpm...
       warning: /tmp/install.sh.1851/chef-11.10.4-1.el6.x86_64.rpm: Header V4 DSA/SHA1 Signature, key ID 83ef826a: NOKEY
Preparing...                #####  ########################################### [100%]
   1:chef                          ########################################### [100%]
       Thank you for installing Chef!
       Transfering files to <default-centos-65>
       [2014-03-25T03:51:33+00:00] INFO: Forking chef instance to converge...
       Starting Chef Client, version 11.10.4
       [2014-03-25T03:51:33+00:00] INFO: *** Chef 11.10.4 ***
       [2014-03-25T03:51:33+00:00] INFO: Chef-client pid: 1948
       [2014-03-25T03:51:34+00:00] INFO: Setting the run_list to ["recipe[rbenv-cookbook::default]"] from JSON
       [2014-03-25T03:51:34+00:00] INFO: Run List is 
] [2014-03-25T03:51:34+00:00] INFO: Run List expands to [rbenv-cookbook::default] [2014-03-25T03:51:34+00:00] INFO: Starting Chef Run for default-centos-65 [2014-03-25T03:51:34+00:00] INFO: Running start handlers [2014-03-25T03:51:34+00:00] INFO: Start handlers complete. Compiling Cookbooks... Running handlers: [2014-03-25T03:51:34+00:00] ERROR: Running exception handlers Running handlers complete [2014-03-25T03:51:34+00:00] ERROR: Exception handlers complete [2014-03-25T03:51:34+00:00] FATAL: Stacktrace dumped to /tmp/kitchen/cache/chef-stacktrace.out Chef Client failed. 0 resources updated in 0.458583313 seconds [2014-03-25T03:51:34+00:00] ERROR: Cookbook yum-epel not found. If you're loading yum-epel from another cookbook, make sure you configure the dependency in your metadata [2014-03-25T03:51:34+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1) >>>>>> Converge failed on instance <default-centos-65>. >>>>>> Please see .kitchen/logs/default-centos-65.log for more details >>>>>> ------Exception------- >>>>>> Class: Kitchen::ActionFailed >>>>>> Message: SSH exited (1) for command: [sudo -E chef-solo --config /tmp/kitchen/solo.rb --json-attributes /tmp/kitchen/dna.json --log_level info]

エラーでこけた… 。rbenv-cookbookはyum-epelに依存してるせい? metadata.rbは既に作ってるのでBerkshelfファイルを作成しmetadataを読み込むようにしたらエラーをパスできた!

#Berksfile
source "https://api.berkshelf.com"

metadata

再実行。gitが無いので止まった。これはレシピが悪い。

#recipes/default.rb
...snip...
+ package "git"
...snip...

再実行!無事終了!

kitchen convergeでやること

  • インスタンスにChefをインストール
  • 依存関係のレシピをインストール
  • .kitchen.ymlのrun_listを実行

手動で確認する

ここまででレシピをエラー無くインスタンスに適用できることを確認した。手動でログインし確認する。

$ kitchen login default-centos-65
Last login: Tue Mar 25 03:59:38 2014 from 10.0.2.2
[vagrant@default-centos-65 ~]$

ログインできた。rbenvインストールされている。

$ rbenv
rbenv 0.4.0-95-gf71e227
Usage: rbenv <command> [<args>]

テストを書く

ServerSpecを使いたいところではあるが、ここではチュートリアルに従ってBusserというテストフレームワークでテストを書く。

テスト用のディレクトリを作成

mkdir -p test/integration/default/bats

今回は実行ファイルが存在するかどうかだけチェックした。

#test/integration/default/bats/rbenv_installed.bats
#!/usr/bin/env bats

@test "executable rbenv command is found" {
  run test -x /usr/local/rbenv/bin/rbenv
  [ "$status" -eq 0 ]
}

テストを流す

$ kitchen veryfy default-centos-65  

-----> Starting Kitchen (v1.2.1)
-----> Verifying <default-centos-65>...
       Removing /tmp/busser/suites/bats
       Uploading /tmp/busser/suites/bats/rbenv_installed.bats (mode=0644)
-----> Running bats test suite
 ✓ executable rbenv command is found

       1 test, 0 failures
       Finished verifying <default-centos-65> (0m1.30s).
-----> Kitchen is finished. (0m2.12s)

kitchen test

いままでやってきたことを全工程流す。

$ kitchen veryfy default-centos-65  
  • 既存のインスタンスがあれば廃棄する
  • インスタンス作成
  • Chefのインストール
  • レシピのインストール
  • テストの実行
  • インスタンスの廃棄

感想

test kitchenよい。でも初回遅い。boxが最初ないと更に遅い。この辺はVagrantをDockerに変えるというアプローチがあるとおもうが気軽ではない感じかも。しばらく使ってみる!