Chefレシピ逆引きメモ


よく使うものを逆引きできるように。

## アンチパターンを知る

http://www.slideshare.net/JulianDunn/beginner-chef-antipatterns
http://www.creationline.com/lab/3080

1. すべてのChefデータを1つの巨大なGitレポジトリに入れてしまう
 * cookbooksにはバージョンがあるが、environmentsやrolesにはない
 * cookbooksとして分割すべきものはリポジトリを分けるべき
1. 会社名つきの巨大なCookbookを作ってしまう
 * 本来組み合わせるべきでないものが混ざる危険がある
 * プロジェクト別に分けるべき
1.  Environmentsを論理的な環境以上の目的で使ってしまう
 * developmentとかproductionとか論理的な環境で使い、クラスタとかデータセンターといった割り当てに使わない
1. Community Cookbookをフォークしてしまう
 * 開発元のバグ修正や機能追加といった恩恵を受け取れない、なるべくラップする方が良い
1. Role内でrun_listを管理してしまう
 * roleはバージョンが付かない、代わりに ``include_recipe`` を使いrecipeでまとめる
1. 無秩序なdata bagを作ってしまう
 * data bagとdata bag itemという2階層なので作る前に計画しておく
1. chef-shellを知らない、使わない
 * Cookbook開発、デバッグに適している
1. LWRPを怖がってしまう
 * 言うほどLWRPの実装は難しくない
1. 外部発祥だから利用しない症候群に陥ってしまう
 * まず使えるものがあるか探し、変更が必要ならコントリビュートする
1. 孤独なChef使いになってしまう
 * あるセットアップに自分より詳しいメンバーがいるなら任せ、チームで責任を持つ

## レシピをチェックする


http://acrmp.github.io/foodcritic/

```bash
gem i foodcritic
foodcritic -C .
```

[FC001](http://acrmp.github.io/foodcritic/#FC001)など警告の内容についてはサイトに載っている。

### FC001以外をチェックする

``FC001``はdeprecatedになった。``FC001``を除外して検証するには以下のようにする。

```bash
foodcritic -t ~FC001 site-cookbooks
```


## ログを出す

[log](http://docs.opscode.com/resource_log.html)リソースを使う。でもレシピの一時的なデバッグならrubyの``puts``や``p``でもいいかもしれない。

```ruby
log "a debug string" do
  level :debug
end

log "message" do
  message "This is the message that will be added to the log."
  level :info
end
```

## Vagrantのプロビジョニングでログレベルを変更する

``log_level``属性を使う。
http://docs.vagrantup.com/v2/provisioning/chef_common.html

```ruby
config.vm.provision :chef_solo do |chef|
  chef.log_level = :debug
```

## nodeの属性を出力する

とりあえずjsonで

```ruby
puts JSON.pretty_generate(node)
```

またはレシピ内で使いそうなものだけ確認。

```ruby
[:platform, :platform_version, :ipaddress, :fqdn, :hostname, :domain].each do |key|
  puts "node['#{key}'] = #{node[key]}"
end
```

## 置き換わったファイルのバックアップを見る

``template``リソースなどでファイルを置き換えた場合に、元のファイルはリモートサーバの ``/var/chef/backup/`` に保存されている。



## パスワードのハッシュを生成する

Unixではパスワードを ``$＜ハッシュ方式＞$＜salt＞$＜ハッシュ後のパスワード＞`` で表現している。以下のコマンドでそれに対応したMD5のhashを出力できる。

```bash
openssl passwd -1 'password'
```

## Environmentで分岐する

``node.chef_environment`` を見る

```ruby
if node.chef_environment == 'development'
end
```

## OSで分岐する

### OS

```ruby
case node["platform"]
when "debian", "ubuntu"
  # do debian/ubuntu things
when "redhat", "centos", "fedora"
  # do redhat/centos/fedora things
end
```

### OSカテゴリ

xx系で分岐する場合。yumかaptでpackage名が変わったりパスが変わったりするときなどで使う。

```ruby
case node["platform_family"]
when "debian"
  # do things on debian-ish platforms (debian, ubuntu, linuxmint)
when "rhel"
  # do things on RHEL platforms (redhat, centos, scientific, etc)
end
```

## テンポラリファイルのパスを組み立てる

foodcritic の [FC013](http://acrmp.github.io/foodcritic/#FC013)で ``/tmp`` ではなく ``Chef::Config[:file_cache_path]`` を使えとある。

```ruby
remote_file "#{Chef::Config[:file_cache_path]}/large-file.tar.gz" do
  source "http://www.example.org/large-file.tar.gz"
end
```

## 事前に用意した静的ファイルを配置する

[cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースを使う。

デフォルトではファイルは上書きになる。上書きが困る場合は ``action: create_if_missing`` や ``not_if {File.exist?}`` などを使う。


```ruby
# files/default/testfile
cookbook_file "/#{Chef::Config[:file_cache_path]}/testfile" do
  source "testfile"
  mode 00644
  owner 'root'
  group 'root'
end
```


配置する元ファイルは以下の順番で ``files`` のサブディレクトリから検索される。

1. ホスト名 ``host-#{node[:fqdn]}``
1. OS-フルバージョン ``#{node[:platform]}-#{node[:platform_version]}``
1. OS-メジャーバージョン ``#{node[:platform]}-#{version_components}``
1. OS ``node[:platform]``
1. ``default``

```
files/
   host-foo.example.com
   ubuntu-10.04
   ubuntu-10
   ubuntu
   default
```

## テンプレートを使ってファイルを配置する

[template](http://docs.opscode.com/resource_template.html)リソースを使う。

[cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースとの違いは用意できるファイルがerbテンプレートであり動的に出力を変えられるところ。テンプレートのデフォルトのコンテキストは``node``オブジェクトで、``variables``属性で任意のhashをテンプレートに渡すことも可能。

```ruby
template "/etc/apache2/ports.conf" do
  source 'apache/ports.conf.erb'
  mode '0644'
end
```

ソースのテンプレートファイルは ``files`` ディレクトリではなく、``templates`` ディレクトリに配置する。

```
templates/
   host-foo.example.com
   ubuntu-10.04
   ubuntu-10
   ubuntu
   default
```

## 書き換えてしまったファイルの前のバージョンを見る

[cookbook_file](http://docs.opscode.com/resource_cookbook_file.html)リソースや[template](http://docs.opscode.com/resource_template.html)リソースで既存のファイルを上書きしてしまった場合に世代バックアップが``/var/chef/backup`` にある。

## 空のファイルを作成する

[file](http://docs.opscode.com/resource_file.html)リソースの``: create_if_missing``アクションを使う。(ファイルが無い時限定)

```ruby
file "/tmp/something" do
  action :create_if_missing
end
```

## ファイルを削除する

[file](http://docs.opscode.com/resource_file.html)リソースを使う。

```ruby
file "/tmp/something" do
  action :delete
end
```

削除対象がシンボリックリンクの場合は警告がでるので、[link](http://docs.opscode.com/resource_link.html)リソースを使うのがエレガント。

```ruby
link "/tmp/mylink" do
  action :delete
  only_if "test -L /tmp/mylink"
end
```

## ディレクトリを作成する

[directory](http://docs.opscode.com/resource_directory.html)リソースを使う。

```ruby
directory "/var/lib/foo" do
  owner "root"
  group "root"
  mode 00755
  action :create
end
```

親ディレクトリが存在しないと失敗する。``recursive true``をつけると親ディレクトリを再帰的に作成する。

## シンボリックリンクを作成する

[link](http://docs.opscode.com/resource_link.html)リソースを使う。

```ruby
link "/tmp/passwd" do
  to "/etc/passwd"
end

link "/tmp/passwd" do
  to "/etc/passwd"
  link_type :hard
end

link "/tmp/mylink" do
  action :delete
  only_if "test -L /tmp/mylink"
end
```

## ファイルがなければリソースを実行する

``not_if``でファイルをチェックする。

```ruby
execute "create file hoge" do
  command "touch /tmp/hoge"
  not_if {File.exist?("/tmp/hoge")}
end
```

``Dir.exist?`` も使える(rubyの世界)


## コマンドを実行する

[execute](http://docs.getchef.com/resource_execute.html)リソースを使う。

```ruby
execute 'bundle install' do
  cwd '/myapp'
  not_if 'bundle check' # This is not run inside /myapp
end
```

## シェルファイルを実行する

正確には以下の例は[bash](http://docs.opscode.com/resource_bash.html)リソースからコマンドを実行。

```ruby
bash "install_ruby_build" do
  cwd "#{Chef::Config[:file_cache_path]}/ruby-build"
  user "rbenv"
  group "rbenv"
  code <<-EOH
    ./install.sh
  EOH
  environment 'PREFIX' => "/usr/local"
end
```

## Gitリポジトリからファイルを取得する

[git](http://docs.opscode.com/resource_git.html)リソースを使う。``revision``属性にタグなどを指定できる。

```ruby
git "/home/#{node[:user]}/redmine"  do
  repository "git://github.com/edavis10/redmine.git"
  revision '2.5.1'
  user node[:user]
  group node[:user]
end
```

## ウェブにあるアーカイブファイルを展開する

[remote_file](http://docs.opscode.com/resource_remote_file.html)リソースでダウンロード。[bash](http://docs.opscode.com/resource_bash.html)リソースで展開。

```ruby
#  the following code sample is similar to the ``upload_progress_module`` recipe in the ``nginx`` cookbook: https://github.com/opscode-cookbooks/nginx

src_filename = "foo123-nginx-module-v#{node['nginx']['foo123']['version']}.tar.gz"
src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}"
extract_path = "#{Chef::Config['file_cache_path']}/nginx_foo123_module/#{node['nginx']['foo123']['checksum']}"

remote_file src_filepath do
  source node['nginx']['foo123']['url']
  checksum node['nginx']['foo123']['checksum']
  owner 'root'
  group 'root'
  mode 00644
end

bash 'extract_module' do
  cwd ::File.dirname(src_filepath)
  code <<-EOH
    mkdir -p #{extract_path}
    tar xzf #{src_filename} -C #{extract_path}
    mv #{extract_path}/*/* #{extract_path}/
    EOH
  not_if { ::File.exist?(extract_path) }
end
```
## hostsファイルにエントリを追加する

``hostsfile`` cookbook を追加し、[hostsfile_entry](https://github.com/customink-webops/hostsfile) リソースを使う。

```ruby
hostsfile_entry '1.2.3.4' do
  hostname  'www2.example.com'
  aliases   ['foo.com', 'foobar.com']
  comment   'Append by Recipe X'
  action    :append
end
```

## ファイルを置き換えたのでサービスを再起動する

[Notifications](http://docs.getchef.com/resource_common.html#notifications)を使う。

```
template '/etc/httpd/conf/httpd.conf' do
  source 'httpd/httpd.conf.erb'
  notifies :restart, 'service[httpd]'
end
service 'httpd' do
  action [ :enable ]
end
```

``notifies``で指定するリソース名が存在しないといけないので [service](http://docs.getchef.com/resource_service.html)リソースで ``:enable`` や ``:nothing``などのActionで記述しておく。


## Ohaiで取得して使える属性

|属性|説明|
|:--|:--|
|node['platform']|nodeのプラットフォーム|
|node['platform_version']|プラットフォームのバージョン|
|node['ipaddress']|IPアドレス。nodeがデフォルトルートを持っている場合はIPv4アドレス、もっていない場合はnil|
|node['macaddress']|MACアドレス|
|node['fqdn']|完全修飾ドメイン名|
|node['hostname']|ホスト名|
|node['domain']|ドメイン|
|node['recipes']|関連付けられたレシピのリスト|
|node['roles']|関連付けられたroleのリスト|
|node['ohai_time']|Ohaiが最後に実行された時刻。knife statusサブコマンドでサーバに保存し参照できる|