課題
いくつあるか分からないvarsファイルの内容を、ディレクトリー(Hash)に関してマージして使用したい。
例えば、いくつあるか分からないvars*.ymlの内容をマージして使用する。
---
foo:
bar1: baz1
bar2: baz2
---
foo:
bar3: baz3
bar4: baz4
この様な場合、以下の様にマージして使用したい。
foo:
bar1: baz1
bar2: baz2
bar3: baz3
bar4: baz4
なお、自動的に読み込まれる host_vars/<対象host>/, group_vars/all/, group_vars/<対象group名>/ 等の下におけば、不特定数のvarsファイルは読み込まれるが、同じキーの場合には次々と上書きしてしまう。
実装例1
---
- hosts: all
tasks:
- name: set_fact merged_vars = {}
set_fact:
merged_vars: {} # 空のHashに読み込んだ内容を追加していく
- include: merge2.yml # ループ内容は別ファイルで
with_fileglob: # 指定されたローカルファイルに対してループ
- ../vars/vars*.yml # 変数ファイルを正規表現指定
- debug: msg="{{ merged_vars }}"
---
- name: include_vars {{ item }} to tmp_vars
include_vars:
file: "{{ item }}" # 読み込むファイル
name: tmp_vars # tmp_vars下に読み込み
- name: merge tmp_vars into merged_vars
set_fact:
merged_vars: "{{ merged_vars | combine(tmp_vars, recursive=True) }}" # マージ本体。merged_varsに追加マージ
実行例
TASK [set_fact merged_vars = {}] *********************************************************
ok: [ansiblepush-centos-7]
TASK [include] ***************************************************************************
included: /xxx/tasks/merge2.yml for ansiblepush-centos-7
included: /xxx/tasks/merge2.yml for ansiblepush-centos-7
TASK [include_vars /xxx/tasks/../vars/vars2.yml to tmp_vars] ***
ok: [ansiblepush-centos-7]
TASK [merge tmp_vars into merged_vars] ***************************************************
ok: [ansiblepush-centos-7]
TASK [include_vars /xxx/tasks/../vars/vars1.yml to tmp_vars] ***
ok: [ansiblepush-centos-7]
TASK [merge tmp_vars into merged_vars] ***************************************************
ok: [ansiblepush-centos-7]
TASK [debug] *****************************************************************************
ok: [ansiblepush-centos-7] => {
"msg": {
"foo": {
"bar1": "baz1",
"bar2": "baz2",
"bar3": "baz3",
"bar4": "baz4"
}
}
}
この例ではvarsX.ymlの指定を../vars/vars*.ymlとしているが、これはfilesもしくはtasksからの相対パス。指定をvars*.ymlとして、ファイルをfiles/(やtasks/)に配置しても良い。
キーが同じ場合には後から読まれたほうが上書きする。
どちらから読むかは不明。1ステップ追加して取得したvarsファイル名をsortして使用すれば順位は一意に決められそうだが。
ブロックをループできないという理解なので2ファイルに分けている。(Ansibleでは少し制御をしようとすると途端に醜くなる。)
もっと良い方法があれば教えてください。
追記1: hash_behaviour = merge による対応(実装例2)
.ansible.cfgにhash_behaviour = mergeを指定する対応をコメントいただきました。影響を管理できるなら最も容易な方法になります。
その場合、varsファイルは、自動的に読み込まれるhost_vars/<対象host>/やgroup_vars/all/等におけば良いでしょう。
追記2: 実装例3(playbookを分割しない)
以下の手法を借り、include_vars + with_fileglob の各回の取り込み内容を保存し、それらをマージする。
Ansibleのplaybookからコピペをなくすset_factモジュールの使い方: おまけループの中のset_fact
ただ、register:の結果を使用するので、見通しは悪い。
playbook
---
- hosts: all
tasks:
- name: set_fact merged_vars = {}
set_fact:
merged_vars: {}
- name: include_vars to result
include_vars:
file: "{{ item }}"
name: tmp_vars # tmp_vars下に取り込むが各ループで上書きされるので直接は使用しない
with_fileglob:
- ../vars/vars*.yml
register: include_vars_result # 各ループで取り込んだ内容を含む結果を保存
- name: merge each result.results.ansible_facts into merged_vars
set_fact:
merged_vars: "{{ merged_vars | combine( item.ansible_facts.tmp_vars, recursive=True) }}" # マージ本体
with_items: "{{ include_vars_result.results }}"
loop_control:
label: "{{ item.ansible_facts }}" # 実行時ログに不要な情報が表示されない様にする
- debug: msg="{{ merged_vars }}"
実行結果
TASK [set_fact merged_vars = {}] ***********************************************************************************************
ok: [ansiblepush-centos-7]
TASK [include_vars to result] **************************************************************************************************
ok: [ansiblepush-centos-7] => (item=/xxx/tasks/../vars/vars2.yml)
ok: [ansiblepush-centos-7] => (item=/xxx/tasks/../vars/vars1.yml)
TASK [merge each result.results.ansible_facts into merged_vars] ****************************************************************
ok: [ansiblepush-centos-7] => (item={u'tmp_vars': {u'foo': {u'bar3': u'baz3', u'bar4': u'baz4'}}})
ok: [ansiblepush-centos-7] => (item={u'tmp_vars': {u'foo': {u'bar1': u'baz1', u'bar2': u'baz2'}}})
TASK [debug] *******************************************************************************************************************
ok: [ansiblepush-centos-7] => {
"msg": {
"foo": {
"bar1": "baz1",
"bar2": "baz2",
"bar3": "baz3",
"bar4": "baz4"
}
}
}
蛇足: Chefなら
Ruby DSL定義で、キーを分けて代入すればマージされる。
attributes
default['foo']['bar1'] = 'baz1'
default['foo']['bar2'] = 'baz2'
default['foo']['bar3'] = 'baz3'
default['foo']['bar4'] = 'baz4'
recipe
log node['foo']
実行結果
Recipe: merge_chef::default
* log[{"bar1"=>"baz1", "bar2"=>"baz2", "bar3"=>"baz3", "bar4"=>"baz4"}] action write