LoginSignup
1
2

More than 1 year has passed since last update.

Ansible: 不特定数のvarsファイルの内容をマージして使用する例3つとChefの場合

Last updated at Posted at 2018-12-14

課題

いくつあるか分からないvarsファイルの内容を、ディレクトリー(Hash)に関してマージして使用したい。
例えば、いくつあるか分からないvars*.ymlの内容をマージして使用する。

vars/vars1.yml
---
foo:
  bar1: baz1
  bar2: baz2
vars/vars2.yml
---
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

tasks/merge1.yml
---
- 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 }}"
tasks/merge2.yml
---
- 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

tasks/merge1.yml
---
- 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

attributes/var1.rb
default['foo']['bar1'] = 'baz1'
default['foo']['bar2'] = 'baz2'
attributes/var2.rb
default['foo']['bar3'] = 'baz3'
default['foo']['bar4'] = 'baz4'

recipe

recipes/default.rb
log node['foo']

実行結果

Recipe: merge_chef::default
  * log[{"bar1"=>"baz1", "bar2"=>"baz2", "bar3"=>"baz3", "bar4"=>"baz4"}] action write

参考

1
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2