個人的Chefのtips、豆知識

# 初期知識
* コーディング規約は [Ruby コーディング規準](https://github.com/cookpad/styleguide/blob/master/ruby.ja.md) に準拠する
* デフォルトのactionはcreateです。それ以外の操作がしたい場合はactionを記述しましょう
* コメントアウトでどういう動作をしてるか、resource毎に日本語で書きましょう。
* fileやtemplate、user、group resourceなどは、resourceブロックの名前に書く事で、<br>
path（実際に配置するpath）や、group_name部分を省略してます。<br><br>
（例）file '/tmp/hogehoge.txt' do ・・・/tmp/hogehoge.txtを配置
* 定数、分岐はなるべく少なくしましょう。
* environments, roleを有効活用しましょう

# ①bash : スクリプトを打ちたいとき、git cloneしたいとき
* bash, execute, scriptなど色々有りますが、どれもやってることはほぼ同じなのでbashに統一します。
* scriptだと、スクリプトがtypoしてたり間違ってたら止まっちゃいます。くらい

```rb
### /tmp/hogeにhogehogeと書き込む
bash 'write to /tmp/hoge' do
  code <<-EOS
    echo "hogehoge" >> /tmp/hoge
  EOS
end
```

# ②bash or package : yum操作をするとき
* package resourceを使う方法もありますが、色々とエラーが出てしまう場合が多いので、bashでやるのがおすすめです。（一応packageの方法も乗せます）
* yumコマンドのinstallはupdateもするが、<br>
package resourceのupgradeは、既にパッケージがインストールされてないとupdateはしない。<br><br>
しかもupgradeで読みにいったファイルが、インストールされてる物より古かった場合、下記のようなエラーを吐いて処理が止まる<br>
「Installed package prelink-0.4.6-3.1.el6_4 is newer than candidate package prelink-0.4.6-3.el6」
* package resourceのinstall➡installのみ。upgradeはしない
* package resourceのupgrade➡installもupgradeもする
* packageが多い場合はattributeディレクトリで管理するのがおすすめです。

```rb
### symlinksとprelink packageのインストール + アップデート
%w(symlinks prelink).each.do |package|
  bash 'install #{package}' do
      code <<-EOS
          yum -y install #{package}
      EOS
  end
end
```

```rb
### symlinksとprelink packageのインストール + アップデート
### パッケージ名の部分は12.1以降の記述方法です。それ以前のバージョンであれば、パッケージはeach doで回しましょう。
package 'install package' do
  package_name [
    'symlinks',
    'prelink'
  ]
  options "--disablerepo=* --enablerepo=base,epel"
  action :upgrade
end
```

# ③gem_package : gemパッケージをインストールしたいとき

```rb
### ruby2.2.1のrakeとbundlerをインストール
%w(rake bundler).each do |package|
  gem_package "#{package}" do
    gem_binary '/usr/local/bin/gem'
    version '2.2.1'
    action :install
  end
end
# %w・・・,（コンマ）や' "（クォート）を使わずに配列を作るrubyの記法
```

# ④file, directory : 空ファイル、空ディレクトリを作成、パーミッション変更したいとき
* file : 空ファイルを作成、パーミッション変更
* directory : 空ディレクトリ作成、パーミッション変更。recursiveで再帰的に作成も出来る。<br>が、再帰的にパーミッションは変更出来ないので注意。対象がファイルでもパーミッション変更されます。

```rb
### /tmp/hoge.txtを作成
file '/tmp/hoge.txt' do
 owner 'root'
 group 'root'
 mode '0644'
end 
# action :deleteを使うと、ファイルが削除出来るが、
# 削除対象がシンボリックリンクの場合は警告が出るので、link resourceを使うのがベスト。
```

```rb
### /tmp/hoge/fugaを作成
directory '/tmp/hoge/fuga' do
  owner 'root'
  group 'root'
  mode '0644'
  recursive true
end
```

# ⑤template, cookbook_file, remote_directory : <br>cookbook内のファイル、ディレクトリを配置したいとき
* template、cookbook_file、remote_directoryを使い分けましょう。<br>
<b>①template</b> : ファイル内に変数を置いたり、配置する際に書き込みできる。<br>なので、配置する際に中身を読み込んでしまう。<br>圧縮ファイルやバイナリ、ディレクトリ以外は基本ココに置きましょう。<br>erb記述のファイルも扱えます。<br><br>
<b>②cookbook_file</b> : 配置する際に中身は読み込まない。圧縮ファイルとか、rpmファイルとか、バイナリファイルなど置くと良い。<br><br>
<b>③remote_directory</b> : files/default/のディレクトリとその中のファイルを配置。またファイルに対して必要なパーミッションを付与出来る
* 上書きが困る場合はaction: create_if_missing や not_if {File.exist?} などを使いましょう

```rb
### templateディレクトリのhogehoge_producrtion.txtを/tmp/hogehoge.txtとして配置する
template '/tmp/hogehoge.txt' do
  source 'hogehoge_producrtion.txt'
  owner  'root'
  group  'root'
  mode   '0644'
end
```

```rb
### filesからrpmを配置する
### files/default/hogehoge.el6.x86_64.rpmを配置しておく。
cookbook_file '/tmp/hogehoge.el6.x86_64.rpm' do
  source 'hogehoge.el6.x86_64.rpm'
  owner 'root'
  group 'root'
  mode '0644'
end
```

```rb
### filesから/etc/httpd/conf.dディレクトリを配置する。またその中のファイルのパーミッションを変更する
### files/default/plugins/....を配置しておく。
remote_directory '/etc/httpd/conf.d' do
  source 'conf.d'
  files_owner 'apache'
  files_group 'apache'
  files_mode '0755'
  owner 'apache'
  group 'apache'
  mode '0755'
  recursive true
  action :create
end
# files_ownerでファイルのパーミッション変更、ownerでディレクトリの変更
```

# ⑥remote_file : 外部からファイルをダウンロードしたいとき

```rb
### http://hoge.example.com/fugafuga.rpmをダウンロードし、/tmp/fugafuga.rpmに配置
remote_file '/tmp/fugafuga.rpm' do
  source 'http://hoge.example.com/fugafuga.rpm'
  owner 'root'
  group 'root'
  mode '0644'
end
```

# ⑦data_bags : ユーザを作成 

```rb
### ユーザを定義
$ data_bags/users/alice.json
// alice
{
  "id": "alice",
  "uid": "1234",
  "gid": "alice"
  "shell": "/bin/bash",
  "password": "password"
} 

### ⑧ユーザ作成
$ vim site-cookbooks/hoge/recipes/default.rb
data_bag('users').each do |id|
  u = data_bag_item('users', id)
  user u['id'] do
    shell    u['shell']
    uid       u['uid']
    gid       u['gid']
    password u['password']
    supports :manage_home => true, :non_unique => false
    action   [:create]
  end
end
```

# ⑧user : ユーザの作成、変更をしたいとき
* ユーザ管理は出来る限りdata_bagsで管理しましょう〜。

```rb
### hogeユーザ作成
user 'hoge' do
  uid 1234
  gid 'hoge'
  home '/home/hoge'
  supports :manage_home => true
  password 'passworddayo'
end
# supports :manage_home => trueは、ホームディレクトリを作成するオプション
```

# ⑨group : グループの作成、変更をしたいとき
```rb
### apache groupを作成して、"hoge"と"fuga"ユーザを所属させる
group 'apache' do
   gid 48
   members ['hoge', 'fuga']
   append true
   action :modify
end
# append trueにすると、追加するユーザだけ記述すればいい。
# append falseにすれば、apacheに属しているユーザを全て書かなければならない。
# 新しくgroupを作りたい場合はデフォルトのcreateです。
```

# ⑩service : サービスの起動、停止、再起動をしたいとき
```rb
### httpdの起動 + chkconfig on を実施
service 'start httpd add chkconfig on' do
  service_name 'httpd'
  action [:enable, :start]
end			
# actionは複数じゃなければ、[]はいらないが、文言統一のために毎回[]でくくった方が良い
# supports status: true, restart: true, reload: true を指定すると、httpdではこのコマンドが使えるよ！と教えて上げれる。使う場合は記述しましょう。
# restart => trueを指定しない場合、stop + start で代用される
# ignore_failure true を指定すると、エラーは無視します。
 
### Action  説明
# :enable ブート時の起動を有効化
# :disable  ブート時の起動を無効化
# :nothing  何もしない（default）
# :start  サービスを起動
# :stop サービスを停止
# :restart  サービスを再起動
# :reload
```

```rb
### virtualhosts.confを作成した後、httpdを再起動する。service resourceは先に書いても、後に書いてもどちらでも動く
template '/etc/httpd/conf.d/virtualhosts.conf' do
  source 'virtualhosts.conf'
  owner  'root'
  group  'root'
  mode   '0644'
  notifies :restart, 'service[httpd]', :immediately
end 
 
service 'httpd' do
  action [ :nothing  ]
end
# notifies：該当のResourceが実行された際、他のResourceに通知してそのResourceの実行を促す。複数行記述することができる。
# subscriibes：notifiesと逆で、他のResourceの実行終了後に該当ResourceのActionを指定して実行する。
```

# ⑪link : シンボリックリンク、ハードリンクを貼る
```rb
### /tmp/passwdに/etc/passwdを参照するシンボリックリンクを作成
link '/tmp/passwd' do
  to '/etc/passwd'
end
# link_type :hard　を指定するとハードリンクが貼られる。:symbolicだとシンボリックリンク。
# ハードリンクってあまり使わないので、基本シンボリックリンクだと思います。
# なので、link_typeは明示的に書かなくても良いでしょう。
```

# ⑫文字列置換したいとき
```rb
### /tmp/hoge.txtに書かれてるeth0をeth1に変更
file '/tmp/hoge.txt' do
  f = Chef::Util::FileEdit.new(path)
  f.searchfile_replace_line('eth0', 'eth10')
  content f.send(:editor).lines.join
end
```

# ⑬only_ifとnot_ifの違い
* not_if : 既にファイルが存在している場合コマンドを実行しない
* only_if : 既にファイルが存在している場合コマンドを実行する
* デフォルトはファイル上書き

```rb
### only_ifの例
# file が存在し、シンボリックリンクであれば、削除を実行する。
link "/tmp/mylink" do
  action :delete
  only_if "test -L /tmp/mylink"
end

# /var/lib/mysqlが存在した場合、/tmp/hoge.txtを作成する
file '/tmp/hoge.txt' do
  owner 'root'
  group 'root'
  mode '0644'
  only_if {Dir.exist?("/var/lib/mysql")}
end
```

```rb 
### not_ifの例
# /tmp/hogeが存在した場合、書き込まない
bash "fugaaa" do
    code <<-EOS
      echo "fugaaaaa" >> /tmp/hoge
    EOS
    not_if { File.exist?('/tmp/hoge') }
end
```

# ⑭attributeについて
* attributeに値を定義することができます。こんな感じに。（それぞれの書き方は公式のexampleを見ると良いでしょう。https://docs.chef.io/attributes.html）

```rb
### 定義
$ vim site-cookbooks/hogehoge/attributes/default.rb
# インストールしたいrpmパッケージを定義
default['rpm_package'] = %w(
  httpd
  httpd-devel
)
 
### environmentなどはjson形式で定義
$ vim environments/development.json
# hoge = development dayoと定義
  "default_attributes": {
    "hoge": "development dayo"
  }
 
### recipeで実際に使う
$ vim site-cookbooks/hogehoge/recipes/default.rb
# rpmをインストール
node['rpm_package'].each do |package|
  bash "install #{package}" do
    code <<-EOS
      yum -y install #{package}
    EOS
  end
end
 
# ⑮ohaiで取得した、対象サーバのipアドレス。その名前をファイル名として、/tmp/配下にファイル作成
file "/tmp/#{node['ipaddress']}" do
  owner 'root'
  group 'root'
  mode  '0644'
end
 
### erb形式など
$ vim site-cookbooks/hogehoge/templates/default/test.txt
# development dayoと書く
<%= node['hoge'] %>
```

* 優先順位は弱い順に下記。どんどん上書きされて行きます。<br>
①recipes/attribute/<br>
②recipes/default/<br>
③environments/<br>
④roles/<br>
⑤nodes/<br><br>
* Attributeの強さは弱い順に下記。同じく上書きされて行きます。<br>
①default<br>
②force_default<br>
③normal<br>
④override<br>
⑤force_override<br>
⑥automatic<br><br>
* ルールを作って運用するのが一番。自分は下記でやってます。
    * なるべく変数、定数を使わないように構成する。
    * defaultとoverrideのみ使います。<br>
     <b>①recipes/attribute/</b>：インストールするパッケージ一覧や、rubyのバージョンなどのような変動が多い部分。<br>また③④のjson形式で解消出来ない部分。<br>
<b>②recipes/default/</b>：使わない<br>
<b>③environments/</b>：development(開発）、production（本番）でのサーバ収容先、またはネットワーク差異の分岐。<br>
<b>④roles/</b>：基本構成、AP（apache用）、AP（nginx用）、DBサーバの分岐。<br>
<b>⑤nodes/</b>：使わない<br>

# ⑮environmentsの情報を取ってくる

```rb
### もしenvironmentsがproductionだったら・・・
### もしenvironmentsがdevelopmentだったら・・・
if node.chef_environment == "production"
   ・・・・
elsif node.chef_environment == "development"
   ・・・・
end 
```

# ⑯ohai便利
* ohaiはchefが実行環境の情報を取得するためのライブラリです。
* ohai | lessなどで、取って来れる情報がJSON形式で出力されます。
* #{node['ipaddress']}で、IPが取ってこれたり、
* #{node['virtualization']['system']}で、kvmかopenstackかを取ってこれたりします。

```rb
### CPUが1コアの場合、1coreと記述、1コアじゃない場合は、not 1coreと記述。
bash "hoge" do
    if node['cpu']['total'] == 1
        code <<-EOS
            echo "1core" >> /tmp/hoge
        EOS
    else
        code <<-EOS
            echo "not 1core" >> /tmp/hoge
        EOS
    end
end
```


# chefエラー対応
## ERROR: Cookbook repository not found.とエラーが出たら
* キッチン（リポジトリ）のトップディレクトリにいって、$ knife solo init .を実行する

## ERROR: Net::SSH::HostKeyMismatch: fingerprint 0c:82:e9:23:22:6b:b0:73:f6:06:cb:e6:55:49:7a:0a does not match for "10.26.2.19"
の場合は、known_hostsで対象行を消しましょう
* エラー見たら分かるかと思います。

# chef豆知識編
## cookbook名に　.（ドット）は使えません

## 下記は同じ動き
* %w{test test2 test3}
* ['test', 'test2', 'test3']

## chef 12から cookbookのmetadata.rb内のname attributeの記述が必須になった
* metadata.rbのnameを、cookbook名にすればOK

## rubocopで整形しましょう

```rb
$ gem install rubocop --no-ri --no-rdocで、インストールして、$ rubocop site-cookbooksで
```

* 整形すると綺麗になります
* warningやcriticalが出た部分は適宜修正しましょう

## erbを使うときでもファイル名をerbにしなくても通ります。

## resourceの使い回しは出来ます

```rb
### 当然と言えば当然ですが。こんな感じでもいけます。
service "httpd" do
 action [:start]
end

service "httpd" do
 action [:stop]
end
```

## MySQL5.6を使ってる方は/usr/my.cnfを事前に作りましょう
* rpmでmysql5.6をインストールすると、その時点でmysql_install_dbが走ります。
* パスワードを設定する処理などが走りますので、/usr/my.cnfを事前に作っておきましょう

## gitリソースを使って、cloneするときはenable_checkout falseをオプションに入れてあげましょう
* gitリソースを使うと、下記のように新しくdeployがbranchとして切られます。
* enable_checkout falseをオプションにつけるとdeployブランチが切られません。