Status Code 303 - See Other

サーバサイド、iOS・アンドロイドアプリ、インフラレベルの話まで幅広くやってます。情報の誤りや指摘・意見などは自由にどうぞ。

Clojure 開発環境を cygwin 上に作る

概要

LISP の流れを組み、Java VM上で動作する関数言語の Clojurecygwin 上で使えるようにする。
少し苦戦したため、それをメモ。

構築で参考にした記事

Windows でも Clojure がしたい! - Qiita

シェルをダウンロード

GitHub - technomancy/leiningen: Automate Clojure projects without setting your hair on fire.
ここにあるシェル(lein script)をダウンロードする。

もし、上記で場所分からなかったら以下をダウンロード。(2016/09/23現在版。古くなっているかも。)
https://raw.githubusercontent.com/technomancy/leiningen/preview/bin/lein
(インストール先)\.lein\bin の中に「lein」って名前で保存。

cygwin 上で起動できない

ここまでやれば起動できるはず!
・・ってことで、下記コマンドを実行してみるが動かない。

$ lein
/cygdrive/p/.lein/bin/lein: 行 364: P:\Java\jdk1.8.0_60\bin\java.exe: コマンドが見つかりません

見つかってないコマンドのパスは、シェルの364行目をみると環境変数「LEIN_JAVA_CMD」で指定されたものらしい。

Javaファイルを認識できていない可能性があるので、

$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)

・・と、すると lein が java のコマンドを認識できていないってことか。

探してみると、Stack Overflow のページに「普通に自分は動いたよ!」っていう方がいて、
その設定を載せていたので、自分の環境に合わせてコピってみる。
clojure - Getting Leiningen & Cygwin Working - Stack Overflow

JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.8.0_05/"
LEIN_JAVA_CMD="${JAVA_HOME}/bin/java"
JAVA_CMD=`cygpath -w "${LEIN_JAVA_CMD}"`

上記を適用すると動いた。どうやら cygwin 流のパスを lein が認識できていないようだ。

.bashrc に追加

・・ってことで、~/.bashrc にラッパースクリプト書いて、起動時にのみ動くようにした。
(各自環境と照らし合わせてjavaのバージョン・パスを変更してください)

LEIN_PATH=`which lein`
function lein() {
        local JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.8.0_60/"
        local LEIN_JAVA_CMD="${JAVA_HOME}/bin/java"
        local JAVA_CMD=`cygpath -w "${LEIN_JAVA_CMD}"`
        "$LEIN_PATH" $@
}

Haskell を cygwin 上で動かすのに苦労した話

概要

Haskell の動作環境を用意する。cygwin上で動かそうとすると少し苦労したのでメモ。

インストーラのダウンロード

以下をURLから、Windows版のHaskell Platform のインストーラのうち環境に適したものをダウンロード。
Download Haskell Platform
なお、私がダウンロードしたのは、Full(64bit)版。

インストール

指示に従っていけば特に問題なし。パスもインストーラが作成してくれる。

Cygwin で ghci 起動

cygwin を起動して、ghci(対話モードのインタプリタ)を起動してみると・・。

$ ghci
WARNING: GHCi invoked via 'ghci.exe' in *nix-like shells (cygwin-bash, in particular)
         doesn't handle Ctrl-C well; use the 'ghcii.sh' shell wrapper instead
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude>

こんなメッセージが表示される。
警告に書いてある通り、Ctrl + C を用いると、プロンプトは出てくれるのだがそこから何も入力できなくなる。
関連プロセス(ghc.exe)はなぜか生存中。このプロセスをタスクマネージャから強制終了すると入力ができるようになる。

これを防ぐために、ghciコマンドで起動している「ghci.exe」ではなく、「ghcii.sh」が起動するように変更する。
このファイルは「(プログラムフォルダ)\Haskell Platform\8.0.1\bin」に存在ある。
「ghcii.sh」→「ghci」にリネームする。そうすると、「ghci.exe」より優先度が高くなり、シェルの方を呼び出してくれる。

変更すると以下のようになる。

$ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude>

ghcコンパイルできなかった

Haskell のソースファイルをghcを使ってバイナリにコンパイルするときに、私の環境では失敗した。

$ ghc fizzbuzz.hs -o fizzbuzz
Linking fizzbuzz.exe ...
fd:5: hGetContents: invalid argument (invalid byte sequence)
ghc.exe: fd:5: hGetContents: invalid argument (invalid byte sequence)

もう一回起動してみると、なぜかメッセージが変わった。

$ ghc fizzbuzz.hs -o fizzbuzz
Linking fizzbuzz.exe ...
realgcc.exe: error: s\Java\▒▒▒K: No such file or directory
P:\Program Files\Haskell Platform\8.0.1\lib/../mingw/bin/windres.exe: "P:\Program Files\Haskell Platform\8.0.1\lib/../mingw/bin/gcc.exe ▒̓X▒e▒[▒^▒X 1 ▒ŏI▒▒▒▒▒܂▒▒▒
`windres.exe' failed in phase `Windres'. (Exit code: 1)

今度は文字化けが発生。
エラーメッセージを調べていたら、
「LANG=C.utf-8 stack build works as intended.」(stack build は環境変数 LANGがC.utf-8だったら意図したとおりに動いてるよ)
とかあったので、文字化けも考慮して、LANGを確認。

$ echo $LANG
ja_JP.UTF-8

C.UTF-8に変更。

$ export LANG='C.UTF-8'

$ echo $LANG
C.UTF-8
$ ghc fizzbuzz.hs -o fizzbuzz
Linking fizzbuzz.exe ...
$ ./fizzbuzz
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
(省略)

動いた!...でも。これが本当に原因か分からないので、動作検証。元に戻してみる。

$ export LANG='ja_JP.UTF-8'

$ echo $LANG
ja_JP.UTF-8
$ touch fizzbuzz.hs
$ ghc fizzbuzz.hs -o fizzbuzz
[1 of 1] Compiling Main             ( fizzbuzz.hs, fizzbuzz.o )
Linking fizzbuzz.exe ...
realgcc.exe: error: icrosoftw▒E
                               ▒▒: Invalid argument
P:\Program Files\Haskell Platform\8.0.1\lib/../mingw/bin/windres.exe: "P:\Program Files\Haskell Platform\8.0.1\lib/../mingw/bin/gcc.exe ▒̓X▒e▒[▒^▒X 1 ▒ŏI▒▒▒▒▒܂▒▒▒
`windres.exe' failed in phase `Windres'. (Exit code: 1)

・・・ってことで、これが原因らしい。
なお、touchコマンド使っているのは、ghc は内部でソースファイル、オブジェクトファイルの更新時間を見てるようで、
タイムスタンプ更新しないとコンパイルしてくれないため。

ラッパースクリプトを .bashrc に追加。

毎回 Haskell のためだけに環境設定いじるのが微妙だったり面倒だったりする場合は、cygwin 起動時に読み込まれる「~/.bashrc」に
以下のようにラッパースクリプトを作成する。これだと、ghc を使用するときのみLANGを変更してくれる。

GHC_PATH='which ghc'
function ghc() {
  local LANG='C.UTF-8'
  "$GHC_PATH" $@
}

RaspberryPi に Rest API を簡単に実装

概要

Raspberry Pi に対して情報を取得する際に HTTPを利用してこれを行いたい場合がある。
ruby のウェブフレームワークで有名なのは、Rails だが、Rails は大規模向けであるため
簡素な実装をするにも、構築時間、学習コストが見合わないことがある。
今回は ruby 初心者でも手軽に実装できる、軽量フレームワークSinatraを導入し、Rest APIを実装してみた。

Sinatra 導入

ruby, gem 導入

このあたりで多分導入できる(はず)。
Raspberry piにRubyの最新版をインストールする - Qiita
なお、自身のRaspberryPiにはすでにインストールされていたため、実施していません。
バージョン関連の情報は以下。

$ ruby -v
ruby 2.1.5p273 (2014-11-13) [arm-linux-gnueabihf]
$ gem -v
2.2.2

Sinatra 導入

参考:Sinatra: README (Japanese)

基本的に下記コマンドを実行するだけ。

$ sudo gem install sinatra
$ sudo gem install thin

URI、コンテンツ定義

以下のファイルをどこでもいいので、作成する。(下記を app.rb として保存する)

require 'sinatra'
set :bind, '0.0.0.0' # これを書くと http://localhost:4567 以外からでもアクセス可能。

get '/' do # ルートに GET でアクセスした場合
  "Top!\n" # 返却する内容
end
get '/:name' do |n| # /{name} GET でアクセスした場合
  "Hello #{n}\n"    # {name}の部分を値として使う
end

サーバ起動

$ ruby app.rb

アクセス

※ 192.168.0.30 はRaspberrypiのプライベートアドレス
http://192.168.0.30:4567/
f:id:kouki_hoshi:20160911233644p:plain
http://192.168.0.30:4567/aaa
f:id:kouki_hoshi:20160911233938p:plain

実装例

これだけであれば、インストール除けば5分で作成可能。
メモリとディスクのプロパティを入手する API を作ってみた。

require 'sinatra'
require 'json'

set :bind, '0.0.0.0'

disk = Struct.new("Disk", :name, :path, :total, :used)
get '/disk' do
  disks = `df -l | sed -e "1d"`.split("\n").inject([]) do |list, line|
    arr = line.split("\s")
    list << disk.new(arr[0], arr[5], arr[3].to_i, arr[2].to_i).to_h
  end
  JSON.generate({"disks" => disks})
end

memory = Struct.new("Memory", :total, :used, :free, :shared, :buffers, :cached)
get '/memory' do
  arr = `free | sed -n -e "2p"`.split("\s")
  mem = memory.new(arr[1].to_i, arr[2].to_i, arr[3].to_i, arr[4].to_i, arr[5].to_i, arr[6].to_i)
  JSON.generate(mem.to_h)
end

Cygwin 上で curl 実行できれば、下記コマンドでいける。

$ curl http://192.168.0.30:4567/disk | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   580  100   580    0     0   3505      0 --:--:-- --:--:-- --:--:--  3602
{
  "disks": [
    {
      "name": "/dev/root",
      "path": "/",
      "total": 22113944,
      "used": 6837300
    },
    {
      "name": "/dev/sda1",
      "path": "/media/hdd1",
      "total": 455974524,
      "used": 177392
    },
  ]
}
$ curl http://192.168.0.30:4567/memory | jq .
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    91  100    91    0     0   1925      0 --:--:-- --:--:-- --:--:--  2166
{
  "total": 948012,
  "used": 879992,
  "free": 68020,
  "shared": 28708,
  "buffers": 206668,
  "cached": 267452
}

まとめ

テンプレート利用やルーティング条件などが設定できるため、ある程度難しいこともできそうだ。
もし、バックエンドでのみ使用する API であれば、製造コスト的に選択の一つとしてはいいかもしれない。

シェルスクリプト分岐・ループ構文例

概要

Linux 上で便利な機能やマクロ的に処理を行うためにシェルスクリプトを記述することがある。
その中でも比較的よく見かける分岐・ループ構文をメモ。

ファイル状態によって操作を変える

テストコマンドでファイルの状態を確認できる。他にも様々なことができる。詳しくは下記参照。
Linuxコマンド集 - 【 test 】 条件式の真偽を判定する:ITpro

if [ -e 'aaa.txt' ]; then # ファイル aaa.txt が存在するかどうか
  echo 'exist'            # 存在するならexist出力
fi

条件満たす場合・満たさない場合の処理実行

if [ -e 'aaa.txt' ]; then # ファイル aaa.txt が存在かどうか
  echo 'exist'            # 存在するならexist出力
else
  echo 'not found'        # 存在しないならnot found出力
fi

複数条件および処理実行

if [ -s 'aaa.txt' ]; then   # ファイル aaa.txt が存在して空ファイルでない場合
  echo 'size not 0'         # 存在して空ファイルでないならsize not 0出力
elsif [ -e 'aaa.txt' ];     # ファイルが存在するかどうか
  echo 'empty'              # 空ファイルが存在するならempty出力
else
  echo 'not found'          # ファイルが存在しないならnot found出力
fi

exitコードを利用した処理分岐

bash ではexitコードが0なら真、そうでなければ偽となる。これらを利用して &&, || を利用して分岐を書ける。

  • && は直前のステータスが 0 なら次を実行
  • || は直前のステータスが 0 以外なら次を実行

なお、「{」「}」はコマンドのグループ化であり、各文字前後にはスペース必要。「}」の直前コマンドにはセミコロンが必要。

{ [ -s 'aaa.txt' ] && echo 'size not 0'; } || { [ -e 'aaa.txt' ] && echo 'exist'; } || echo 'not found'

上記を branch.sh として作成してテストすると下記のように出力内容が変化する。

$ rm aaa.txt             # ファイルが存在しないようにする
rm: 'aaa.txt' を削除できません: No such file or directory
$ . branch.sh
not found
$ touch aaa.txt          # サイズ 0 のファイル作成
$ . branch.sh
exist
$ echo "aaa" >> aaa.txt  # ファイルに aaa を追加してサイズを増やす
$ . branch.sh
size not 0

変数の中身に応じて操作を変更する

NUM=100
case $NUM in              # NUMの値が
  100) echo 'ok';;        # 100 なら
  0)   echo 'not work';;  # 0 なら
  *)   echo '???';;       # それ以外
esac

ある範囲の数値に対する操作

SUM=0
for NUM in `seq 1 100`; do  # 1-100までに対して操作
  SUM=`expr $SUM + $NUM`    # 加算
done
echo $SUM                   # 結果出力

bashならば以下の記法が使える。

SUM=0
for ((i=1; i<=100; i++)) do
  SUM=`expr $SUM + $i`
done
echo $SUM

ある条件を満たすまで操作する

合計が5000超えるまで計算。

SUM=0 NUM=0
while  [ $SUM -lt 5000 ] && NUM=`expr $NUM + 1`; do
  SUM=`expr $SUM + $NUM`
done
echo $SUM  # 合計値

引数に END が出るまで引数を出力する

while [ -n "$1" ] && [ "$1" != "END" ]; do  # 現在処理中の引数が空文字でも END でもないなら処理を行う
  echo "$1"                                 # 位置パラメータ1番目出力
  shift                                     # 位置パラメータをシフト
done

シェル引数に対する処理

# シェル引数は何もしなければ位置パラメータに入っている
for ARG in "$@"; do  # 各位置パラメータに対して操作
  echo "$ARG"        # 出力
done

標準入力から各要素を取り出し操作

区切り文字としてカンマ(,)が使われた場合の例

read INPUT               # このコマンドで入力待ちになる
IFS=','                  # 区切り文字の指定
set -- $INPUT            # 位置パラメータに入力内容をセット
for ELEMENT in "$@"; do  # それぞれに対して操作
  echo "$ELEMENT"
done

特定ファイル群に対する操作

for FILE in *.txt; do  # カレントディレクトリのテキストファイルに対して操作
  echo "$FILE"         # ファイル名出力
done

直前のコマンド処理結果を操作

cat 'aaa.txt' | grep "AAA" | while read LINE; do  # aaa.txt 内にある AAA が存在する行に対して操作
  echo "$LINE"                                    # 内容出力
done

ファイル各行に対する処理

IFS=','                      # 区切り文字指定。今回はCSVを処理するためカンマ指定
while read LINE; do          # 一行読み込み
  set -- $LINE               # 位置パラメータに一行情報セット
  echo "(1)$1, (2)$2, (3)$3" # 各列情報出力
done < 'test.txt'            # 読み込むファイルはリダイレクトで受け取る

RaspberryPi で複数 Wifi 環境に個別設定を行う方法

概要

勉強会参加するときに RaspberryPi3 を持ち歩いているのだが、
自宅・外出先で Wifi のプライベートアドレス構成が微妙に異っており、事前に設定しておかないといけなかった。
もし設定を忘れた場合、その場でディスプレイ・マウス・キーボードを借りて設定するという若干手間になっていた。

文献がない

RaspberryPi に固定 IP を設定する方法はかなりの数存在する。

しかし、これが複数 Wifi に設定を適応するとなると途端に記事は少なくなる。
その中でも見つけたのがコレ。
Raspberry Piで複数Wifiで個別の固定IPを指定する方法 – 1ft-seabass.jp.MEMO

設定方法をザックリいってしまえば。

  • Wifi の設定は /etc/wpa_supplicant/wpa_supplicant.conf
  • ネットワーク設定は /etc/network/interfaces

そして、wpa_supplicant.conf にネットワークごとの識別子(id_str要素)を作成しておき、
それを interfaces で認識して各々設定するというもの。

設定例

/etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB

network={
        ssid="SSID-xxxx"
        psk="pass_phrase"
        key_mgmt=WPA-PSK
        id_str="home"
}
network={
        ssid="SSID-yyyy"
        psk="pass_phrase"
        key_mgmt=WPA-PSK
        id_str="out"
}
/etc/network/interfaces
auto wlan0
allow-hotplug wlan0
 
iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp
 
iface home inet static
address 192.168.1.100
netmask 255.255.255.0
gateway 192.168.1.1
 
iface out inet static
address 192.168.20.30
netmask 255.255.255.0
gateway 192.168.20.1

ネットワーク設定構成が異なる場合(dhcpcd.conf も設定する場合)

RaspberryPi 3 を購入したとき最新の raspbian はネットワークの設定が変わっていた。

  • Wifi の設定は /etc/wpa_supplicant/wpa_supplicant.conf
  • ネットワーク定義 /etc/network/interfaces
  • 個別ネットワーク詳細設定 /etc/dhcpcd.conf

に分かれてしまっており、どうやら dhcpcd.conf は別の仕組みで動くらしい。

個別の設定部分を dhcpcd.conf に書いてみるが動かない・・・。(違う仕組みなので当然なのだが)
しかし、デフォルトの設定構成を変えるような interfaces を変更したくない。
といっても、該当する文献が見つからなかった。・・・ということで、dhcpcd.conf マニュアルを眺めてみたら。

$ man dhcpcd.conf

なんか気になる設定要素発見。

     ssid ssid
             Subsequent options are only parsed for this wireless ssid.

設定したら、あっさりできてしまった。

/etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB

network={
        ssid="SSID-xxxx"
        psk="pass_phrase"
        key_mgmt=WPA-PSK
}
network={
        ssid="SSID-yyyy"
        psk="pass_phrase"
        key_mgmt=WPA-PSK
}
/etc/network/interfaces
iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet manual
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
/etc/dhcpcd.conf
interface wlan0
ssid SSID-xxxx
static ip_address=192.168.1.100/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

ssid SSID-yyyy
static ip_address=192.168.20.30/24
static routers=192.168.20.1
static domain_name_servers=192.168.20.1

どこでも気軽にもっていけるようになった!

普段持ち歩いている無線 Wifi に繋げるようにしておき、
新しい環境でもノート PC と無線 Wifi があれば一度設定すればいつでも使用可能に。

  1. 無線 Wifi にノート PC と RaspberryPi 接続
  2. 無線 Wifi のプライベートIP経由で SSH 接続
  3. 外出先のネットワーク設定を行う(接続優先度は無線 Wifi より低く設定)
  4. 無線 Wifi 停止・RaspberryPi 再起動
  5. 動かなかったら無線 Wifi 使って再度 SSH ログインして調査。

仮にネットワーク設定ミスってしまっても、無線 Wifi にはいつでもつなげるのがいいですね。

VirtualBox+Vagrant+Chef(knife-solo) 環境構築(Windows 7+Cygwin) 苦戦記

前回記事
VirtualBox+Vagrant 環境構築(Windows 7+Cygwin) 苦戦記 - Status Code 303 - See Other

前回記事概要

Windowsでコンソールとして Cygwin を利用し、VirtualBox + Vagrant で仮想環境を作成できるようにした。
このとき、苦戦した内容と解決に至るまでをメモった。

この記事で記述すること

Chef を導入し、前回の仮想環境上にインフラ環境をセットアップする。
しかし、Cygwin で行うために少し面倒な設定が必要になる。

環境

前回に引き続き、Windows + Cygwin 環境で行う。

ソフトウェアについて

VirtualBoxVagrantCygwin

前回記事参照。

Chef?

Chef 社が提供。インフラ設定をコードで記述、設定したいサーバにコード内容を実行するためのツール。
サーバの構築内容をコードとして管理することができる。(Infrastructure as Code)
Chef には大きく2通りの形態があり(Chef-solo/Chef-Server + Chef-Client)、
今回は小規模向けである Chef Solo を用いる。この中でも拡張ユーティリティの knife-solo を用いる。
その他の Chef の種類や特徴については下記を。
あなたに合ったChefはどれ? 〜 おすすめ構成確認チャート #getchef - クリエーションライン株式会社

なんのため?

  • 本番を想定したテスト環境を作成したり、その上で簡単なテストしたりするため。
  • 継続的デリバリー(CD)を実現するため。

 (参考:DevOps時代の開発者のための構成管理入門(終):継続的デリバリ/デプロイを実現する手法・ツールまとめ (1/2) - @IT)

環境構築

今回使用環境

Operating System

  • Windows 7 Professional Service Pack1(64bit)

Console

Application

導入

  • Chef Development Kit Version: 0.13.21
  • chef-client version: 12.9.41
  • berks version: 4.3.2
  • kitchen version: 1.7.3

Ruby gems

  • berkshelf (4.3.2)
  • knife-solo (0.6.0)

手順

前提環境

※自身のCドライブが小さいためデータをEドライブに保存しています。そこは適宜ご自身の環境に置き換えてください。

Chef インストール 手順

gem を使ってインストールする方法もあるようだが、現在ではインストーラを使うのが主流らしい。
基本的に手順は以下を参考にさせてもらった。
Chef for Windows
Windows7にChef(11.6.0)とVagrant(1.7.2)を入れてプロビジョニングしてみた - カタカタブログ

つまずいたところ

インストール先の変更

インストーラがインストール先の選択が出る。ここで「E:\opscode\」を選択。
f:id:kouki_hoshi:20160622013513p:plain
そうするとインストールは完了するが、Cygwin 上で実際に使ってみるとなぜか動かない。
調べるとどうやら、インストーラのバグらしい。(c:\opscode にあることを前提としたコードがある?)
Windows can't install into anything other than C:\opscode · Issue #68 · chef/chef-dk · GitHub

しかたないので、シンボリックリンクを作成し C:\opscode を E:\opscode に関連付けしてから再インストールした。
(再インストールしなくても動くかもだけど念のため)
※ 以下のコマンドに限り、管理者モードで起動したコマンドプロンプトを使用。

> mklink /d C:\opscode E:\opscode

[Windows] シンボリックリンクの作成と削除

Cygwin上のパスを制御する

CygwinLinux 環境をWindows上に作るだけあってそのフォルダ構成が異なる。
これが原因でエイリアスを設定しないと動かない。(コマンドが実行できない)

$ chef -v
C:\opscode\chefdk\embedded\bin\ruby.exe: No such file or directory -- /cygdrive/c/opscode/chefdk/bin/chef (LoadError)

関連するエイリアスを全て設定に書き込み、その設定を現在のセッションに適用する。
(下記ではEドライブだが、Cドライブのファイル名で指定してもOK)

$ echo "alias chef='E:/opscode/chefdk/bin/chef'" >> ~/.bashrc
$ echo "alias knife='E:/opscode/chefdk/bin/knife'" >> ~/.bashrc
$ echo "alias gem='E:/opscode/chefdk/embedded/bin/gem'" >> ~/.bashrc
$ source ~/.bashrc

これで次回から Cygwin を起動するたびに設定される。

knife solo の障害

Chef コマンドは動くようになった。次は knife solo コマンドを使うため下記を実行する。

gem install knife-solo
gem install berkshelf
chef gem install knife-solo

インストール完了後 knife solo コマンドを動かすがどうも動かない。knife configure も同様だ。
どうやら gem で使用するライブラリ(net-ssh)との依存関係が原因のようだ。

$ knife solo init .
C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:2288:in `raise_if_conflicts': Unable to activate knife-solo-0.5.1, because net-ssh-3.1.1 conflicts with net-ssh (< 3.0, ~> 2.7) (Gem::ConflictError)
        from C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems/specification.rb:1408:in `activate'
        from C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems.rb:200:in `rescue in try_activate'
        from C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems.rb:193:in `try_activate'
        from C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:126:in `rescue in require'
        from C:/opscode/chefdk/embedded/lib/ruby/site_ruby/2.1.0/rubygems/core_ext/kernel_require.rb:40:in `require'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.5.1/lib/chef/knife/cook.rb:1:in `<top (required)>'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:100:in `load'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:100:in `block in load_commands'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:100:in `each'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:100:in `load_commands'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:110:in `load_command'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife/core/subcommand_loader.rb:124:in `command_class_from'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:153:in `subcommand_class_from'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:214:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/knife.rb:148:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/knife:25:in `<top (required)>'
        from E:/opscode/chefdk/bin/knife:61:in `load'
        from E:/opscode/chefdk/bin/knife:61:in `<main>'

knife solo ユーティリティの既知の障害みたい。 Unable to activate knife-solo-0.5.1 · Issue #811 · chef/chef-dk · GitHub
直す方法を探したが、ダウングレード記事が多くあまり気が進まなかった。
ここでしばらく諦めモードに。

しかし、ver 0.6.0 でこの問題が直ったためアップデートして 0.5.1 -> 0.6.0 にアップデートすると動いた

$ knife configure
WARNING: No knife configuration file found
Where should I put the config file? [E:/cygwin/home/hoshikouki/.chef/knife.rb]
Please enter the chef server URL: [https://hoshikouki-PC.flets-east.jp:443]
Please enter an existing username or clientname for the API: [hoshikouki]
Please enter the validation clientname: [chef-validator]
Please enter the location of the validation key: [/etc/chef-server/chef-validator.pem]
Please enter the path to a chef repository (or leave blank):
*****

You must place your client key in:
  E:/cygwin/home/hoshikouki/.chef/hoshikouki.pem
Before running commands with Knife

*****

You must place your validation key in:
  E:/etc/chef-server/chef-validator.pem
Before generating instance data with Knife

*****
Configuration file written to E:/cygwin/home/hoshikouki/.chef/knife.rb
rsync コマンド失敗

Cookbook を作成し、debian-jessieホストを以下のコマンドで設定。

$ vagrant ssh-config --host debian-jessie >> ~/.ssh/config

リモート先で Chef 実行しようと knife solo bootstrap コマンド(prepare + cook コマンド)を実行。

$ knife solo bootstrap debian-jessie
Bootstrapping Chef...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 19602  100 19602    0     0  17371      0  0:00:01  0:00:01 --:--:-- 17377
debian 8 x86_64
Getting information for chef stable 12.9.41 for debian...
downloading https://omnitruck-direct.chef.io/stable/chef/metadata?v=12.9.41&p=debian&pv=8&m=x86_64
  to file /tmp/install.sh.8361/metadata.txt
trying wget...
sha1    2f8503b7177437418337e7ad01064b77aefdafad
sha256  3f54dd6121acaea52620a58cd06f4192cd7b0e6c82f2047f10876494c93ac89d
url     https://packages.chef.io/stable/debian/8/chef_12.9.41-1_amd64.deb
version 12.9.41
downloaded metadata file looks valid...
downloading https://packages.chef.io/stable/debian/8/chef_12.9.41-1_amd64.deb
  to file /tmp/install.sh.8361/chef_12.9.41-1_amd64.deb
trying wget...
Comparing checksum with sha256sum...
Installing chef 12.9.41
installing with dpkg...
(Reading database ... 63807 files and directories currently installed.)
Preparing to unpack .../chef_12.9.41-1_amd64.deb ...
Unpacking chef (12.9.41-1) over (12.9.41-1) ...
Setting up chef (12.9.41-1) ...
Thank you for installing Chef!
Running Chef on debian-jessie...
Uploading the kitchen...
WARNING: Local cookbook_path 'E:/cygwin/home/hoshikouki/cookbooks' does not exist
WARNING: Local role_path '.\roles' does not exist
WARNING: Local data_bag_path '.\data_bags' does not exist
WARNING: Local environment_path '.\environments' does not exist
Generating solo config...
rsync: link_stat "/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160605-18680-1tgoomz" failed: No such file or directory (2)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]
ERROR: RuntimeError: Failed to launch command ["rsync", "-rL", "--chmod=ugo=rwX", "--rsh=ssh vagrant@debian-jessie", "--delete-after", "-zt", "--exclude=revision-deploys", "--exclude=.git", "--exclude=.hg", "--exclude=.svn", "--exclude=.bzr", "/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160605-18680-1tgoomz", ":~/chef-solo/solo.rb"]

最後の行から察するに rsync か。またかい。
原因の切り分けをするために、knife solo prepare コマンド実行。(リモートに Chef solo 環境作成)
処理は成功。次に、knife solo cook コマンド(リモートに cookbook 適用)を実行すると同じエラーが出た。
どうやらここが原因らしい。

そして、調べてみると -VV (Vが2個) がデバッグモードになるらしく、これを使って調べてみた。

$ knife solo cook -VV debian-jessie
INFO: Using configuration from E:/cygwin/home/hoshikouki/chef-repo/.chef/knife.rb
Starting 'Run'
Running Chef on debian-jessie...
Checking Chef version...

(長いので中略)

sent 124 bytes  received 11 bytes  270.00 bytes/sec
total size is 0  speedup is 0.00
DEBUG: 'encrypted_data_bag_secret' not set
DEBUG: ["rsync", "-rL", "-v", "--chmod=ugo=rwX", "--rsh=ssh vagrant@debian-jessie", "--delete-after", "-zt", "--exclude=revision-deploys", "--exclude=.git", "--exclude=.hg", "--exclude=.svn", "--exclude=.bzr", ".\\environments/", ":~/chef-solo/environments"]
building file list ... done

sent 124 bytes  received 11 bytes  270.00 bytes/sec
total size is 0  speedup is 0.00
Generating solo config...
DEBUG: ["rsync", "-rL", "-v", "--chmod=ugo=rwX", "--rsh=ssh vagrant@debian-jessie", "--delete-after", "-zt", "--exclude=revision-deploys", "--exclude=.git", "--exclude=.hg", "--exclude=.svn", "--exclude=.bzr", "/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160607-9868-1vtkfpf", ":~/chef-solo/solo.rb"]
building file list ...
rsync: link_stat "/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160607-9868-1vtkfpf" failed: No such file or directory (2)
done

sent 81 bytes  received 11 bytes  184.00 bytes/sec
total size is 0  speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]
C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/knife-solo/tools.rb:4:in `system!': Failed to launch command ["rsync", "-rL", "-v", "--chmod=ugo=rwX", "--rsh=ssh vagrant@debian-jessie", "--delete-after", "-zt", "--exclude=revision-deploys", "--exclude=.git", "--exclude=.hg", "--exclude=.svn", "--exclude=.bzr", "/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160607-9868-1vtkfpf", ":~/chef-solo/solo.rb"] (RuntimeError)
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:279:in `rsync'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:233:in `upload'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:258:in `write'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:229:in `generate_solorb'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:97:in `block in run'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:212:in `time'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:79:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:421:in `block in run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/local_mode.rb:44:in `with_server_connectivity'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:420:in `run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:219:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/knife.rb:148:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/knife:25:in `<top (required)>'
        from E:/opscode/chefdk/bin/knife:61:in `load'
        from E:/opscode/chefdk/bin/knife:61:in `<main>'

やはり rsync のようだ。最初は "Program Files の半角スペースが原因でコマンドが認識できていないのかと思ったが、
配列からコマンドを作成するこの記述方法なら問題なさそうだ。・・・原因分からねえ。

そして、ある時閃いた。

  • cygwin 直下のこのパスってtmpフォルダなんてあったっけ? →残念あった
  • Program Files 以下って書き込み権限あったっけ?     →なかった

ユーザの書き込み権限を「書き込み」許可すると動いた。(tmp フォルダ上で右クリック->プロパティのセキュリティタブ)
どうやら「/cygdrive/C/Program Files/cygwin64/tmp/solo.rb20160607-9868-1vtkfpf」に書き込みができなくてダメだったようだ。
ユーザの書き込み権限をエクスプローラから「書き込み」許可すると先ほどの場所は抜けた。

謎のエラー

rsync は直ったようだが、まだエラーが残っていた。次の問題部分。

DEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout:
        DEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout: from /usr/bin/chef-solo:51:in `<main>'
from /usr/bin/chef-solo:51:in `<main>'DEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout:


C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:315:in `cook': chef-solo failed. See output above. (RuntimeError)
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:99:in `block in run'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:212:in `time'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:79:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:421:in `block in run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/local_mode.rb:44:in `with_server_connectivity'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:420:in `run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:219:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/knife.rb:148:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/knife:25:in `<top (required)>'
        from E:/opscode/chefdk/bin/knife:61:in `load'
        from E:/opscode/chefdk/bin/knife:61:in `<main>'
  • VV オプション付けるとメッセージが違う。不思議。
Generating solo config...
Running Chef: sudo chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json
/opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/config_fetcher.rb:47:in `read': Is a directory @ io_fread - /home/vagrant/chef-solo/solo.rb (Errno::EISDIR)
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/config_fetcher.rb:47:in `read_local_config'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/config_fetcher.rb:36:in `read_config'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application.rb:107:in `load_config_file'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application.rb:85:in `configure_chef'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application.rb:47:in `reconfigure'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/solo.rb:210:in `reconfigure'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application.rb:56:in `run'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/chef-solo:25:in `<top (required)>'
        from /usr/bin/chef-solo:51:in `load'
        from /usr/bin/chef-solo:51:in `<main>'
ERROR: RuntimeError: chef-solo failed. See output above.

/home/vagrant/chef-solo/solo.rb はディレクトリだよ?
パスを見るに 仮想環境上のようだ。乗り込んで確かめてみると、
たしかに「/home/vagrant/chef-solo/solo.rb」はディレクトリだった。
なんでこんなことになっていたのか・・・。謎のディレクトリを削除してみると、進んだ。

$ sudo rm -rf /home/vagrant/chef-solo/solo.rb
リソースが見つからない。

Cookbook に作成した hello リソースが見つからないらしい。

/usr/bin/chef-solo:51:in `<main>'
[2016-06-07T15:43:59+00:00] ERROR: Cookbook hello not found. If you're loading hello from another cookbook, make sure you configure the dependency in your metadata
DEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout: [2016-06-07T15:43:59+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

[2016-06-07T15:43:59+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:315:in `cook': chef-solo failed. See output above. (RuntimeError)
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:99:in `block in run'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:212:in `time'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:79:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:421:in `block in run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/local_mode.rb:44:in `with_server_connectivity'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:420:in `run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:219:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/knife.rb:148:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/knife:25:in `<top (required)>'
        from E:/opscode/chefdk/bin/knife:61:in `load'
        from E:/opscode/chefdk/bin/knife:61:in `<main>'

ためしに dstat リソースを新しく作成して同様の手順を行う。しかし、結果は同じ。

DEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout: [2016-06-07T15:54:55+00:00] ERROR: Cookbook dstat not found. If you're loading dstat from another cookbook, make sure you configure the dependency in your metadata
[2016-06-07T15:54:55+00:00] ERROR: Cookbook dstat not found. If you're loading dstat from another cookbook, make sure you configure the dependency in your metadataDEBUG: sudo -p 'knife sudo password: ' chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json -l debug stdout:

[2016-06-07T15:54:55+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:315:in `cook': chef-solo failed. See output above. (RuntimeError)
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:99:in `block in run'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:212:in `time'
        from C:/Users/hoshikouki/AppData/Local/chefdk/gem/ruby/2.1.0/gems/knife-solo-0.6.0/lib/chef/knife/solo_cook.rb:79:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:421:in `block in run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/local_mode.rb:44:in `with_server_connectivity'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:420:in `run_with_pretty_exceptions'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/knife.rb:219:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/application/knife.rb:148:in `run'
        from C:/opscode/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/bin/knife:25:in `<top (required)>'
        from E:/opscode/chefdk/bin/knife:61:in `load'
        from E:/opscode/chefdk/bin/knife:61:in `<main>'

作成したリソースを Chef が認識していないようだ。

  1. Berkshelf が悪さしている? (違った)
    chef (11.6.0) + knife-solo (0.3.0.pre5)は相性が悪いっぽい? - じゅにゃくんのはてブロ。
  2. cookbook の設定を書く場所が悪かった (ビンゴ)
    knife-soloによるChefの実行 - Qiita
以下を .chef/knife.rb (knife configure を実行したフォルダ直下)に記述。

cookbook_path    ["cookbooks", "site-cookbooks"]
node_path        "nodes"
role_path        "roles"
environment_path "environments"
data_bag_path    "data_bags"
#encrypted_data_bag_secret "data_bag_key"

knife[:berkshelf_path] = "cookbooks"

※ これを ~/.chef/knife.rb (ホームディレクトリ直下)に書いてたから動かなかった

そして、 knife solo cook が成功。(gitリソースまで作成してから行ってます)

$ knife solo cook debian-jessie
Running Chef on debian-jessie...
Checking Chef version...
Installing Berkshelf cookbooks to 'E:/cygwin/home/hoshikouki/.berkshelf/knife-solo/741e61ac3fba69a26087f2d0c26ffc30e967a5f2'...
Resolving cookbook dependencies...
Uploading the kitchen...
WARNING: Local cookbook_path 'E:/cygwin/home/hoshikouki/.berkshelf/knife-solo/741e61ac3fba69a26087f2d0c26ffc30e967a5f2' does not exist
Generating solo config...
Running Chef: sudo chef-solo -c ~/chef-solo/solo.rb -j ~/chef-solo/dna.json
Starting Chef Client, version 12.9.41
[2016-06-07T17:10:27+00:00] WARN: The cookbook(s): dstat exist in multiple places in your cookbook_path. A composite version has been compiled.  This has been deprecated since 0.10.4, in Chef 13 this behavior will be REMOVED. at /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/chef-12.9.41/lib/chef/cookbook_loader.rb:89:in `load_cookbooks'
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 3 resources
Recipe: dstat::default
  * apt_package[dstat] action remove (up to date)
  * apt_package[git] action install (up to date)
Recipe: hello::default
  * log[hello world!!!!!] action write


Running handlers:
Running handlers complete

Ruby on Rails チュートリアル2章

概要

Ruby の Web Framework である Ruby on Railsチュートリアルについてまとめる。
対象読者としては、チュートリアルを一度は読んだけどチュートリアルからいちいちコマンドなどを再確認するのが面倒な場合など。

チュートリアル
第1章 ゼロからデプロイまで | Rails チュートリアル

前回記事
Ruby on Rails チュートリアル1章 - Status Code 303 - See Other

使用環境

※ 全部基本的に無料

IDE (Integrated Development Environment)

Cloud 9 (Cloud9 - Your development environment, in the cloud)

  • クラウド上で作業できる開発環境、最近はこんなサービスもあるんですね・・
  • 基本的にコンソール上で作業するので、GUI しか使えない人は厳しいかも?
  • ファイルの編集には vi を使用

バージョン管理

Git

  • おなじみのバージョン管理ツール
  • SVN(subversion)との違いは、ローカルでも自由に作業できたり、ネット不通でも開発できる環境だったり。

Gitリポジトリ

Bitbucket

  • 少人数開発であればプライベート環境(他人に公開しない)が無料で使える。
  • Github と基本的に提供機能は同じ。

事前準備 (前回記事とほぼ同じ)

今回は、前回のアプリは扱わないため、新しくプロジェクトを作成する。
なお、今回は チュートリアルに習い、プロジェクト名を toy_app として記述する。

プロジェクト作成
$ cd ~/workspace
$ rails _4.2.2_ new toy_app
$ cd toy_app/
Gemfile 置換
$ rm ~/workspace/toy_app/Gemfile
$ vi ~/workspace/toy_app/Gemfile

以下をコピペで貼付けて作成。

source 'https://rubygems.org'

gem 'rails',        '4.2.2'
gem 'sass-rails',   '5.0.2'
gem 'uglifier',     '2.5.3'
gem 'coffee-rails', '4.1.0'
gem 'jquery-rails', '4.0.3'
gem 'turbolinks',   '2.3.0'
gem 'jbuilder',     '2.2.3'
gem 'sdoc',         '0.4.0', group: :doc

group :development, :test do
  gem 'sqlite3',     '1.3.9'
  gem 'byebug',      '3.4.0'
  gem 'web-console', '2.0.0.beta3'
  gem 'spring',      '1.1.3'
end

group :production do
  gem 'pg',             '0.17.1'
  gem 'rails_12factor', '0.0.2'
end
本番環境用のgemを除いたローカルgemをインストール
$ bundle install --without production
Gitの管理下におく
$ git init
$ git add -A
$ git commit -m "Initialize repository"
Bitbucket 登録

リボジトリ作成 (bitbucket のサイトで作成)
次に以下のコマンドで作成

$ git remote add origin git@bitbucket.org:${USER_NAME}>/toy_app.git
$ git push -u origin --all 
Applicationコントローラにhelloを追加
デフォルトページを変更する

2章内容(名前変更必要)

作成するデータ定義

ユーザ情報 (users)

  • id :integer
  • name :string
  • email :string

マイクロポスト (microposts)

  • id :integer
  • content :text
  • user_id :integer

Userリソース作成

Railsのscaffoldを用いて作成する。

$ rails generate scaffold User name:string email:string

Rakeを使用してデータベースをマイグレート(データベース更新・usersデータモデル作成)
※ rake db:migrate でもいける場合もあるがシステム依存らしい

$ bundle exec rake db:migrate

サーバ起動して追加リソースを見る

サーバ起動

ローカル

$ rails server  # ローカル環境
$ rails server -b $IP -p $PORT  # Cloud 9
リソース先にアクセス

下記のRestful APIが自動生成される。
アクセス可能なページ(URL)一覧

メソッドURLアクション用途
GET/usersindexすべてのユーザーを表示するページ
GET/users/1showid=1のユーザーを表示するページ
GET/users/newnewユーザーを新規作成するページ
GET/users/1/editeditid=1のユーザーを編集するページ
その他利用可能なアクション。
メソッドURLアクション用途
POST/userscreateユーザーを作成するアクション
PATCH/users/1updateid=1のユーザーを更新するアクション
DELETE/users/1destroyid=1のユーザーを削除するアクション

(筆者追記) 動作実行前との差分を知りたければ $ git status を実行する。

リソースの自動生成について

挙動説明

プログラムとDBがどのように連携しているかの説明。本記事は手順書的な意味合いが強いので割愛。

挙動確認・変更方法

上記リソースのアクセス先ルーティングを変更したい場合、routers.rbを変更する。

vi config/routes.rb
Rails.application.routes.draw do
  resources :users
  # The priority is based upon order of creation: first created -> highest priority.
  # See how all your routes lay out with "rake routes".

  # You can have the root of your site routed with "root"
  root 'users#index' # 'application#hello から変更
(以下省略)

もし、さきほどの Restful API の各挙動を知りたければ、app/controllers/users_controller.rb を参照する。

$ less app/controllers/users_controller.rb
自動生成したリソースの欠点
  • データ検証がされていない
  • 誰でも作成・参照・更新・削除可能
  • 業務要件を満たすテストが存在しない
  • レイアウトやスタイルはデフォルト
  • 理解が困難

micropostsリソース作成

microposts リソース作成

基本的にUserリソースと同じ。

$ rails generate scaffold Micropost content:text user_id:integer
$ bundle exec rake db:migrate
動作確認

挙動変更

マイクロポストの文字数に制限をかける。

vi app/models/micropost.rb
class Micropost < ActiveRecord::Base
  validates :content, length: { maximum: 140 }  # 追加
end

データモデル同士の関連付け

※ユーザとユーザ ID が一致した microposts を事前に作成しておくこと。

user.rb を編集する
$ vi app/models/user.rb
class User < ActiveRecord::Base
  has_many :microposts  # 追加
end
micropost.rb を編集する
vi app/models/micropost.rb
class Micropost < ActiveRecord::Base
  belongs_to :user  # 追加
  validates :content, length: { maximum: 140 }  # 追加
end
関連付け確認方法

rails console を使用して確認する。

$ rails console
>> first_user = User.first
>> first_user.microposts

コンソール実行例 (この例では、ユーザが二人いて二人目がデータ持っていた)

2.3.0 :003 > second_user = User.second
  User Load (0.4ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1
 => #<User id: 8, name: "XYZ", email: "xyz@aaa.com", created_at: "2016-07-02 07:37:50", updated_at: "2016-07-02 07:37:50"> 
2.3.0 :004 > second_user.microposts
  Micropost Load (0.3ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ?  [["user_id", 8]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 2, content: "あああああ", user_id: 8, created_at: "2016-07-02 07:44:23", updated_at: "2016-07-02 07:44:23">, #<Micropost id: 3, content: "AAAAAAAAAAAA", user_id: 8, created_at: "2016-07-02 07:44:56", updated_at: "2016-07-02 07:44:56">]>

Ctrl + D で終了する。
※ なお、user.rb で設定していないと例外が発生する。

2.3.0 :002 > second_user.microposts
NoMethodError: undefined method `microposts' for #<User:0x007faa00082788>
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activemodel-4.2.2/lib/active_model/attribute_methods.rb:433:in `method_missing'
        from (irb):2
        from /usr/local/rvm/gems/ruby-2.3.0/gems/railties-4.2.2/lib/rails/commands/console.rb:110:in `start'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/railties-4.2.2/lib/rails/commands/console.rb:9:in `start'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/railties-4.2.2/lib/rails/commands/commands_tasks.rb:68:in `console'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/railties-4.2.2/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/railties-4.2.2/lib/rails/commands.rb:17:in `<top (required)>'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:274:in `require'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:274:in `block in require'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:240:in `load_dependency'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:274:in `require'
        from /home/ubuntu/workspace/toy_app/bin/rails:9:in `<top (required)>'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:268:in `load'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:268:in `block in load'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:240:in `load_dependency'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/activesupport-4.2.2/lib/active_support/dependencies.rb:268:in `load'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/commands/rails.rb:6:in `call'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/command_wrapper.rb:38:in `call'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:180:in `block in serve'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:153:in `fork'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:153:in `serve'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:128:in `block in run'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:122:in `loop'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application.rb:122:in `run'
        from /usr/local/rvm/gems/ruby-2.3.0/gems/spring-1.1.3/lib/spring/application/boot.rb:18:in `<top (required)>'
        from /usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
        from /usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
        from -e:1:in `<main>'2.3.0 :003 > 

継承階層

作成したモデル(Users, Microposts) は ActionController::Base を継承する。
このため、作成したモデルはデータベースにアクセスしカラムをあたかもRubyの属性のように扱える。
また、作成したモデルコントローラ(UsersController, MicropostController) は ApplicationController を継承する。
このため、ApplicationControllerで定義したルールはすべてのコントローラに反映される。

デプロイ

ローカルに変更をコミットし、bitbucket に push する

$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push

herokuにデプロイ。

$ git push heroku    # デプロイ
$ heroku run rake db:migrate   # マイグレーション