#ansible #jinja2
#ansible #jinja2
Вопрос:
У меня есть следующие переменные:
domain_names:
- app1.example.com
- app2.example.com
customers:
- name: customer1
- name: customer2
И я пытаюсь сгенерировать следующий список доменных имен:
- customer1.app1.example.com
- customer2.app1.example.com
- customer1.app2.example.com
- customer2.app2.example.com
Используя следующий код Ansible / Jinja2:
- name: check which certificates exist
stat:
path: '/etc/nginx/{{item}}.crt'
register: cert_file
loop: '{% for d in domain_names %}{{ d }} {% for customer in customers %}{{ customer.name }}.{{ d }} {% endfor %}{% endfor %}'
Однако я получаю следующую ошибку:
failed | msg: Invalid data passed to 'loop', it requires a list, got this instead: customer1.app1.example.com customer2.app1.example.com customer1.app2.example.com customer2.app2.example.com. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup.
Как я могу это исправить?
Ответ №1:
Просто используйте правильные инструменты :). В этом случае вашими друзьями являются:
map
фильтр для извлеченияname
атрибута в виде списка из вашейcustomers
переменнойproduct
фильтр для объединения двух ваших списков
например, test.yml
playbook:
---
- name: product and map filters demo
hosts: localhost
gather_facts: false
vars:
domain_names:
- app1.example.com
- app2.example.com
customers:
- name: customer1
- name: customer2
tasks:
- name: Demonstrate product and map filters use
debug:
msg: "{{ item.0 }}.{{ item.1 }}"
loop: "{{ customers | map(attribute='name') | product(domain_names) | list }}"
Что дает:
$ ansible-playbook test.yml
PLAY [product and map filters demo] *******************************************************************************************************************************************************************************
TASK [Demonstrate product and map filters use] ********************************************************************************************************************************************************************
ok: [localhost] => (item=['customer1', 'app1.example.com']) => {
"msg": "customer1.app1.example.com"
}
ok: [localhost] => (item=['customer1', 'app2.example.com']) => {
"msg": "customer1.app2.example.com"
}
ok: [localhost] => (item=['customer2', 'app1.example.com']) => {
"msg": "customer2.app1.example.com"
}
ok: [localhost] => (item=['customer2', 'app2.example.com']) => {
"msg": "customer2.app2.example.com"
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Применительно к вашей задаче это дает:
- name: Check which certificates exist
stat:
path: '/etc/nginx/{{ item.0 }}.{{ item.1 }}.crt'
register: cert_file
loop: "{{ customers | map(attribute='name') | product(domain_names) | list }}"
Если вы действительно хотите повторно использовать этот список, вы можете встроить его в другую переменную. Один из простых способов понять это — встроить ее в set_fact
задачу, например
- name: Create my application names list
vars:
current_name: "{{ item.0 }}.{{ item.1 }}"
set_fact:
application_names_list: "{{ application_names_list | default([]) [current_name] }}"
loop: "{{ customers | map(attribute='name') | product(domain_names) | list }}"
- name: Check which certificates exist
stat:
path: '/etc/nginx/{{ item }}.crt'
register: cert_file
loop: "{{ application_names_list }}"
Но вы также можете объявить ее «статически» в своих переменных с помощью немного более сложного выражения (см. map
Возможности фильтрации и join
фильтр)
---
- name: product and map filters demo
hosts: localhost
gather_facts: false
vars:
domain_names:
- app1.example.com
- app2.example.com
customers:
- name: customer1
- name: customer2
application_names_list: "{{ customers | map(attribute='name') | product(domain_names) | map('join', '.') | list }}"
tasks:
- name: Demonstrate product and map filters use
debug:
var: application_names_list
=>
PLAY [product and map filters demo] ****************************************************************************************************************************************************************************************************
TASK [Demonstrate product and map filters use] *****************************************************************************************************************************************************************************************
ok: [localhost] => {
"all_domains": [
"customer1.app1.example.com",
"customer1.app2.example.com",
"customer2.app1.example.com",
"customer2.app2.example.com"
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Комментарии:
1. Вы, сэр, гений. Вы не только ответили на мой вопрос, но и научили меня множеству полезных приемов, которые я обязательно буду использовать в будущем. Хотел бы я, чтобы у меня было больше одного голоса за!