Chefのメモ（ファイルコピー、環境変数、パッケージ）

最近ChefとServerspecを始めたので、備忘録としてメモを記載。
なるべく`bash`や`execute`以外のResourcesを書く方向で。

# 未解決

## 追加したyumリポジトリを参照しない場合がある

`rpmforge`を追加してgitをインストールする。

```rb
include_recipe 'yumrepo::rpmforge'

# git-1.7.12.4-1がインストールされる 
package "git" do
  action :install
end
```

古いgitをアンインストールして、その後にgitをインストールする。

```rb
# git-1.7.1-3をアンインストール
yum_package "git < 1.7.10" do
  action :remove
end

include_recipe 'yumrepo::rpmforge'

# rpmforgeではなくbaseを見て、git-1.7.1-3がインストールされる
package "git" do
  action :install
end
```

リポジトリ追加を最初にするとgit-1.7.12.4-1がインストールされる。

```rb
include_recipe 'yumrepo::rpmforge'

# git-1.7.1-3をアンインストール
yum_package "git < 1.7.10" do
  action :remove
end

# git-1.7.12.4-1がインストールされる
package "git" do
  action :install
end
```

素直に`action :upgrade`を使えばいいんですが、理由がよく分からなかったのでメモとして記載。最初に`yum_package`を実行したタイミングで、以降に使用するリポジトリが固定されてしまうのかな？

```rb
include_recipe 'yumrepo::rpmforge'

# git-1.7.12.4-1がインストール／アップデートされる 
package "git" do
  action :updrade
end
```

# 解決済み

## 認証の必要なプロキシ経由でberkshelfを実行するための設定
1. 環境変数http_proxyにプロキシ情報を設定

```bash
export http_proxy=http://＜ユーザID＞:＜パスワード＞@＜プロキシホスト＞:＜ポート番号＞
```

2. open-uri.rbを編集

```rb
# open-uri.rbの161行目付近（OpenURI.open_loopメソッドの先頭）
options = {:proxy_http_basic_authentication => ["プロキシホスト", "ユーザID", "パスワード"] }
```

## remote_fileのキャッシュ
　remote_fileリソースで、Chef::Config['file_cache_path']（/var/chef/cache/remote_file）にダウンロードして、２回目以降はキャッシュを使用するようにする。

```rb
remote_file "#{Chef::Config['file_cache_path']}/git-#{git['version']}.zip" do
  source git['url']
  mode   0644
  not_if "test -f #{Chef::Config['file_cache_path']}/git-#{git['version']}.zip"
end
```

## パッケージのアンインストール→インストール
　`yum`の`ruby`が古いので、アンインストール後に`checkinstall`で作成した`rpm`をインストールしようとした際に少しハマったのでメモ。

・失敗例：ruby-2.1.0のインストールに失敗

```rb
# ruby-1.8.7がインストールされており、アンインストールされる
yum_package "ruby < 1.9.2" do
  action :remove
end

# rubyがインストールされている場合はtrue、されていない場合はfalse
is_installed_ruby = ( /^ruby/ =~ `rpm -q ruby` ) ? true : false

if is_installed_ruby
  log "ruby is installed."

else
  cookbook_file "/tmp/ruby-2.1.0-1.x86_64.rpm" do
    source "ruby-2.1.0-1.x86_64.rpm"
  end

  rpm_package "ruby" do
    source "/tmp/ruby-2.1.0-1.x86_64.rpm"
    action :install
  end
end
```

・実行結果

```
（中略）
[2014-04-02T01:08:17+09:00] WARN: yum_package[ruby < 1.9.2] matched multiple Provides for ruby < 1.9.2 but we can only use the first match: ruby. Please use a more specific version.
[2014-04-02T01:08:18+09:00] INFO: yum_package[ruby < 1.9.2] removed
[2014-04-02T01:08:18+09:00] INFO: ruby is installed.
（中略）
```

　上記の通り、`ruby`はアンインストールされるが、`is_installed_ruby`は`true`となる。
　[このページ](http://www.engineyard.co.jp/blog/2013/chef-recipe-lifecycle/)にあるように、`ruby`スクリプトの処理が最初に行われ、次にリソースの処理が行われる。そのため、`is_installed_ruby`はレシピ開始時点での状態で評価され、`yum_package`の処理結果と関係なく`true`になる。

　本来の意図である、`ruby`をアンインストールしてからインストールする場合には、リソースの中に条件を記載する必要がある。

```rb
yum_package "ruby < 1.9.2" do
  action :remove
end

log "ruby is installed." do
  only_if { /^ruby/ =~ `rpm -q ruby` }
end

cookbook_file "/tmp/ruby-2.1.0-1.x86_64.rpm" do
  source "ruby-2.1.0-1.x86_64.rpm"
  not_if { /^ruby/ =~ `rpm -q ruby` }
end

rpm_package "ruby" do
  source  "/tmp/ruby-2.1.0-1.x86_64.rpm"
  action  :install
  not_if  { /^ruby/ =~ `rpm -q ruby` }
end
```

・実行結果

```
（中略）
[2014-04-02T01:33:31+09:00] WARN: yum_package[ruby < 1.9.2] matched multiple Provides for ruby < 1.9.2 but we can only use the first match: ruby. Please use a more specific version.
[2014-04-02T01:33:32+09:00] INFO: yum_package[ruby < 1.9.2] removed
[2014-04-02T01:33:32+09:00] INFO: cookbook_file[/tmp/ruby-2.1.0-1.x86_64.rpm] created file /tmp/ruby-2.1.0-1.x86_64.rpm
[2014-04-02T01:33:35+09:00] INFO: cookbook_file[/tmp/ruby-2.1.0-1.x86_64.rpm] updated file contents /tmp/ruby-2.1.0-1.x86_64.rpm
（中略）
```

複数のリソースに同じ条件を記載するのは面倒なので、何か上手い方法はないものか。。。

## ファイルのコピー、リネーム

レシピから転送するのは`cookbook_file`でいいとして、対象のサーバ上のファイルをコピー、リネームする。（バックアップ等の用途）

### ファイルのコピー
例）/etc/profileのコピー（/etc/profile.bak）

```rb
file "/etc/profile.bak" do
  content IO.read("/etc/profile")
end
```

### ファイルのリネーム
例）/etc/httpd/conf.d/welcome.confをwelcome.conf.bakにリネーム
コピーした後に元のファイルを消すしかない？それか`execute`で`mv`を実行するか。

```rb
file "/etc/httpd/conf.d/welcome.conf.bak" do
  content IO.read("/etc/httpd/conf.d/welcome.conf")
end

file "/etc/httpd/conf.d/welcome.conf" do
  action :delete
end
```

## 環境変数の設定
JBossを動作させるために`JAVA_HOME`を設定したり、`gem install pg`を実行するために、`/usr/pgsql-*.*/bin`を`PATH`に追加する。（pg_configのパスを通すため）
`/etc/profile.d`ディレクトリ内にシェルを作成する。

### 環境変数の値がレシピに依存しない場合

JAVA_HOMEの値は`readlink -m $(which java)`で取得できるので、cookbook_fileで静的ファイルを転送する。

・recipe

```rb
cookbook_file "profile-java.sh" do
  path "/etc/profile.d/java.sh"
end
```

・files/default/profile-java.sh

```bash
JAVA_PATH=`readlink -m $(which java)`
export JAVA_HOME=${JAVA_PATH%/*/*}
```

### 環境変数の値がattributes等に依存する場合
パスが`/usr/pgsql-*.*/bin`(*.*はバージョン)の場合、バージョンはパラメータによって変わるので`template`で転送する。
レシピの中でシェルコマンドでpg_configのパスを取得して、その値を`template`から参照したりできるんですかね・・・？


・recipe

```rb
# 9.2の場合、/etc/profile.d/postgresql92.shを作成
template "/etc/profile.d/postgresql#{short_version}.sh" do
  source "profile-postgresql.sh.erb"
end
```

・templates/default/profile-postgresql.sh.erb

```erb
# PATHに/usr/pgsql-9.2/binを追加
PGSQL_PATH=/usr/pgsql-<%= node['postgresql']['version'] %>/bin
export PATH=$PATH:${PGSQL_PATH}
```