chefでテンプレートResourceを利用する

# 概要
Chefでのテンプレート操作をキャッチアップした時の備忘録です。
この記事では、下記を整理しています。

- テンプレートResourceの使い方
- `Role`で変数を設定する方法
- `Enviroment`で変数を設定する方法

テンプレートとは、静的テキストファイルを動的に生成するために利用します。
テンプレートファイルはERBが利用されます。

https://docs.chef.io/resources/template/

```Bash:テンプレートファイル(ERB)
Hello <%= @name %>.
```

↓ 置換イメージ

```Bash:最終的に生成されるファイルイメージ
Hello XXX.
```

また、`Role`や`Enviroment`は下記を参考にさせていただきました。
https://qiita.com/kkojima0127/items/a96fe991b6630d4d277d

## 01. 事前準備
- `chefdk`をinstall済み
- ローカル環境を汚したくない場合、docker等のVM内で実行すること


```Bash:centos/7
$ chef -v
ChefDK version: 4.13.3
Chef Infra Client version: 15.14.0
Chef InSpec version: 4.24.8
Test Kitchen version: 2.8.0
Foodcritic version: 16.3.0
Cookstyle version: 5.23.0
```


## 02. テンプレートのレシピを試す
作業用リポジトリを作成する。

```Bash:~/
$ chef generate repo chef-repo
Generating Chef Infra repo chef-repo
- Ensuring correct Chef Infra repo file content

Your new Chef Infra repo is ready! Type `cd chef-repo` to enter it.

$ tree chef-repo/
chef-repo/
|-- LICENSE
|-- README.md
|-- chefignore
|-- cookbooks
|   |-- README.md
|   `-- example
|       |-- README.md
|       |-- attributes
|       |   `-- default.rb
|       |-- metadata.rb
|       `-- recipes
|           `-- default.rb
|-- data_bags
|   |-- README.md
|   `-- example
|       `-- example_item.json
`-- policyfiles
    `-- README.md

```

まずは`/templates`ディレクトリを作成し、テンプレートを追加する。
`chef generate template`コマンドを利用すれば、対象のcookbooksに簡単に追加できる。

```Bash:~/
$ chef generate template ./chef-repo/cookbooks/example hello
Recipe: code_generator::template
  * directory[./chef-repo/cookbooks/example/templates] action create
    - create new directory ./chef-repo/cookbooks/example/templates
  * template[./chef-repo/cookbooks/example/templates/hello.erb] action create
    - create new file ./chef-repo/cookbooks/example/templates/hello.erb
    - update content in file ./chef-repo/cookbooks/example/templates/hello.erb from none to e3b0c4
    (diff output suppressed by config)

# templateファイルが作成される
$ tree chef-repo/cookbooks/example/
chef-repo/cookbooks/example/
|-- README.md
|-- attributes
|   `-- default.rb
|-- metadata.rb
|-- recipes
|   `-- default.rb
`-- templates
    `-- hello.erb
```

テンプレートファイルの中身を編集する。
今回は、`name`変数を置換してファイル配置する感じになります。

```Bash:chef-repo/cookbooks/example/templates/hello.erb
Hello <%= @name %>.
```

テンプレート作成が完了したら、レシピに作ったテンプレートを適用するように記述を追加します。

```Bash:./chef-repo/cookbooks/example/recipes/default.rb
template '/tmp/hello.txt' do
  source 'hello.erb'
  variables(
    :name => 'UserA'
  )
end
```

テンプレートレシピをローカルにデプロイする。
`--local-mode`オプションを指定することで、`Chef Infra Server`を介さず直接レシピをローカルに適応できます。
`-o`オプションを指定することで`run-list`を上書きできる。

```Bash:~/chef-repo
$ chef-client --local-mode -o "recipe[example::default]"
[2022-02-13T04:57:35+00:00] WARN: Original Run List: []
[2022-02-13T04:57:35+00:00] WARN: Overridden Run List: [recipe[example::default]]
resolving cookbooks for run list: ["example::default"]
Synchronizing Cookbooks:
  - example (1.0.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 1 resources
Recipe: example::default
  * template[/tmp/hello.txt] action create
    - create new file /tmp/hello.txt
    - update content in file /tmp/hello.txt from none to 157fff
    --- /tmp/hello.txt  2022-02-13 04:57:35.649783300 +0000
    +++ /tmp/.chef-hello20220213-3298-1tkrpnp.txt       2022-02-13 04:57:35.649783300 +0000
    @@ -1 +1,2 @@
    +Hello UserA.
[2022-02-13T04:57:35+00:00] WARN: Skipping final node save because override_runlist was given
```

ファイルが作成されているか`more`コマンドで確認します。
テンプレート中の`<%= @name %>`が`UserA`に置換されてファイル作成できていることを確認できました！！

```Bash:~/chef-repo
$ more /tmp/hello.txt
Hello UserA.
```

## 03. テンプレート変数をRoleで設定する
https://docs.chef.io/roles/

`02`でテンプレートレシピを扱いましたが、変数の値をレシピに直書きしていました。
これでは、環境毎に設定値を変えて利用することが難しいです。
今回は、`Role`毎に値を変更できるようにしてみます。

まず、`Role`を作るために`knife`をローカルで動く設定を行います。

```Bash:~/chef-repo
$ mkdir .chef
$ echo 'local_mode true' > .chef/knife.rb
```

次に、`Role`を作成します。
今回は`role1`、`role2`と作成し、`attributes`の`name`をRole別に変更しています。

```Bash:~/chef-repo
$ EDITOR=vi knife role create role1
Created role[role1]

$ EDITOR=vi knife role create role2
Created role[role2]
```

```Bash:role{1|2}
# 変更点は★の箇所のみ
{
  "name": "role{1|2}", // ★
  "description": "",
  "json_class": "Chef::Role",
  "default_attributes": {
    "example": {
      "name": "role{1|2}" // ★
    }
  },
  "override_attributes": {

  },
  "chef_type": "role",
  "run_list": [
    "example::default"
  ],
  "env_run_lists": {

  }
}
```

次に、`02`で作ったレシピを変更します。
`variables`に設定していた固定値を、`attributes`から取得するようにします。

```Bash:./chef-repo/cookbooks/example/recipes/default.rb
template '/tmp/hello.txt' do
  source 'hello.erb'
  variables(
    :name => node["example"]["name"]
  )
end
```

最後に、`Role`の設定を元にテンプレートが変更できるか確認してみます。
`role1`では、ファイルに`Hello role1.`が設定されていることが確認できます。

```Bash:./chef-repo
# node(ローカル端末)のrun_listにrole1を追加
$ knife node run_list add 0612a2a79715 "role[role1]"
0612a2a79715:
  run_list: role[role1]

# 実行
$ chef-client --local-mode
Recipe: example::default
  * template[/tmp/hello.txt] action create
    - create new file /tmp/hello.txt
    - update content in file /tmp/hello.txt from none to a5d764
    --- /tmp/hello.txt  2022-02-13 06:48:48.729783300 +0000
    +++ /tmp/.chef-hello20220213-3708-1trtj7q.txt       2022-02-13 06:48:48.729783300 +0000
    @@ -1 +1,2 @@
    +Hello role1.

# 確認
$ more /tmp/hello.txt
Hello role1.
```

続いて、`Role2`も確認してみます。
`role2`では、ファイルに`Hello role2.`が設定されていることが確認できます。

```Bash:./chef-repo
# role1を削除
$ knife node run_list remove 0612a2a79715 "role[role1]"
0612a2a79715:
  run_list:

# role2を追加
$ knife node run_list add 0612a2a79715 "role[role2]"
0612a2a79715:
  run_list: role[role2]

# 実行
$ chef-client --local-mode
Recipe: example::default
  * template[/tmp/hello.txt] action create
    - update content in file /tmp/hello.txt from a5d764 to 3b949b
    --- /tmp/hello.txt  2022-02-13 06:48:48.729783300 +0000
    +++ /tmp/.chef-hello20220213-3774-uzt0b8.txt        2022-02-13 06:52:23.899783300 +0000
    @@ -1,2 +1,2 @@
    -Hello role1.
    +Hello role2.

# 確認
$ more /tmp/hello.txt
Hello role2.
```

## 04. テンプレート変数をEnvironmentsで設定する

https://docs.chef.io/environments/

`03`では`Role`の`attributes`を元にテンプレート変数の値を変更しました。
今回は、`Environment`毎に値を変更できるようにしてみます。

まずは`Environment`を作成します。
今回は`dev`、`stg`を作成し、`attributes`の`name`を環境毎に変更しています。

```Bash:~/chef-repo
$ EDITOR=vi knife environment create dev
Created dev

$ EDITOR=vi knife environment create stg
Created stg
```

```Bash:dev|stg
# 変更点は★の箇所のみ
{
  "name": "{dev|stg}", // ★
  "description": "",
  "cookbook_versions": {

  },
  "json_class": "Chef::Environment",
  "chef_type": "environment",
  "default_attributes": {
    "example": {
      "name": "{dev|stg}" // ★
    }
  },
  "override_attributes": {

  }
}
```

次に、先ほど設定した`Role`を削除し、`Environment`をnodeに設定します。

```Bash:./chef-repo
$ knife node run_list remove 0612a2a79715 "role[role2]"
0612a2a79715:
  run_list:

$ knife node environment set 0612a2a79715 "dev"
chef_environment: dev
```

最後に、`Environment`の設定を元にテンプレートが変更できるか確認してみます。
`dev`では、ファイルに`Hello dev.`が設定されていることが確認できます。

```Bash:./chef-repo
#  実行
$ chef-client --local-mode -o "recipe[example::default]"
Recipe: example::default
  * template[/tmp/hello.txt] action create
    - update content in file /tmp/hello.txt from 3b949b to 78e244
    --- /tmp/hello.txt  2022-02-13 06:52:23.899783300 +0000
    +++ /tmp/.chef-hello20220213-3860-hxp62w.txt        2022-02-13 07:23:26.119783300 +0000
    @@ -1,2 +1,2 @@
    -Hello role2.
    +Hello dev.

# 確認
$ more /tmp/hello.txt
Hello dev.
```

続いて、`stg`も確認してみます。
`stg`では、ファイルに`Hello stg.`が設定されていることが確認できます。

```Bash:./chef-repo
# environmentをstgに変更
knife node environment set 0612a2a79715 "stg"
chef_environment: dev

# 実行
$ knife node environment set 0612a2a79715 "stg"
chef_environment: stg

$ chef-client --local-mode -o "recipe[example::default]"
Recipe: example::default
  * template[/tmp/hello.txt] action create
    - update content in file /tmp/hello.txt from 78e244 to e51b89
    --- /tmp/hello.txt  2022-02-13 07:23:26.119783300 +0000
    +++ /tmp/.chef-hello20220213-3926-1hkb2i7.txt       2022-02-13 07:25:50.449783300 +0000
    @@ -1,2 +1,2 @@
    -Hello dev.
    +Hello stg.

$ more /tmp/hello.txt
Hello stg.
```

## まとめ
`chef`の`template`リソースを利用したファイル操作方法を学びました。
静的ファイルを動的に変更できるので、環境毎に設定値を変更する等で活用することができそうですね！