Вопрос или проблема
Когда я пытаюсь выполнить цикл (с помощью цикла Ansible 2.6) с подэлементами через все публичные ключи из списка пользователей и встречаю пользователя, у которого не определены публичные ключи:
- authorized_key:
user: "{{ item.0.username }}"
state: present
key: "{{ item.1.pub_key }}"
loop: "{{ users | subelements('ssh_pub_keys') | default ([]) }}"
loop_control:
label: "{{ item.username }}"
Я получаю следующую ошибку: the key 'ssh_pub_keys' should point to a list, got None
Когда я пытаюсь использовать skip_missing так:
- authorized_key:
user: "{{ item.0.username }}"
state: present
key: "{{ item.1.pub_key }}"
loop: "{{ lookup('subelements', users, 'ssh_pub_keys', {'skip_missing': True})}}"
loop_control:
label: "{{ item.username }}"
Я получаю эту ошибку: 'list object' has no attribute 'username'
Это может быть список пользователей:
users:
- username: usera
ssh_pub_keys:
- from: home
pub_key: kdzadizajdiazjd
- from: work
pub_key: dzadadazdzadzad
- username: userb
ssh_pub_keys:
- from: home
pub_key: kdzadizajdiazjd
- from: work
pub_key: dzadadazdzadzad
- username: userc
- username: userd
ssh_pub_keys:
- from: home
pub_key: kdzadizajdiazjd
- from: work
pub_key: dzadadazdzadzad
Как я могу сделать так, чтобы цикл с подэлементами переходит к следующему пользователю без выброса ошибки, когда пользователь встречается без списка ssh_pub_keys
?
Q: “Ошибка: ключ ‘ssh_pub_keys’ должен указывать на список, получено None.“
A: Возможно, вы захотите использовать lookup и установить ‘skip_missing’: True.
loop: "{{ lookup('subelements',
users,
'ssh_pub_keys',
{'skip_missing': True}) }}"
При желании, вы можете использовать subelements в качестве фильтра. Следующий цикл дает те же результаты
loop: "{{ users | subelements('ssh_pub_keys', skip_missing=true) }}"
Q: “Ошибка: ‘list object’ не имеет атрибута ‘username’“
A: Индекс отсутствует. Пожалуйста, исправьте это.
label: "{{ item.0.username }}"
Пример полного плейбука для тестирования
- hosts: localhost
vars:
users:
- username: foo
ssh_pub_keys:
- pub_key: k1
- pub_key: k2
- username: bar
- username: baz
ssh_pub_keys:
- pub_key: k1
- pub_key: k2
tasks:
- debug:
msg: "{{ item.0.username }} {{ item.1.pub_key }}"
loop: "{{ users | subelements('ssh_pub_keys', skip_missing=true) }}"
loop_control:
label: "{{ item.0.username }}"
дает (сокращенно)
msg: foo k1
msg: foo k2
msg: baz k1
msg: baz k2
Примечание: Параметр ключ позволяет использовать несколько ключей. Вам не нужно итерировать ssh_pub_keys. Например,
- debug:
msg: |
user: "{{ item.username }}"
key: |
{{ item.ssh_pub_keys | map(attribute="pub_key") | join('\n') }}
loop: "{{ users | selectattr('ssh_pub_keys', 'defined') }}"
дает (сокращенно)
user: "foo"
key: |
k1\nk2
user: "baz"
key: |
k1\nk2
Ответ или решение
В Ansible часто встречается задача управления пользователями и их SSH ключами, особенно в окружениях, где необходимо поддерживать актуальные списки ключей на различных хостах. Тем не менее, при использовании Ansible нередко возникают ошибки, когда часть данных, таких как списки публичных ключей у некоторых пользователей, может отсутствовать. Это создает необходимость в управлении исключениями и корректной обработке отсутствующих данных в плейбуках Ansible.
Теория
Основная проблема, с которой вы столкнулись в Ansible, заключается в том, что модуль, занимающийся обработкой ключей, не может корректно интерпретировать ситуации, когда у некоторых пользователей отсутствует список публичных ключей SSH. Обращение к несуществующим элементам в списке неизбежно вызывает ошибку. В вашем случае, когда пытаетесь выполнить итерацию через подэлементы (подключения subelements), возникает ошибка, указывающая, что ключ ‘ssh_pub_keys’ должен ссылаться на список, но возвращается None. Это случается, когда нет публичных ключей для определенного пользователя.
Также возникает новая проблема при попытке использования опции skip_missing: true
, которая теоретически должна пропускать отсутствующие элементы, но на практике это приводит к возникновению ошибок с ссылками на атрибуты, т.к. Ansible интерпретирует данные неправильно при форматировании этикеток.
Пример решения
Чтобы избежать ошибки «ключ ‘ssh_pub_keys’ должен ссылаться на список, но было получено None», важно понимать концепцию управления отсутствующими значениями в Ansible. Существуют несколько подходов, чтобы корректно обходить такие ситуации.
-
Использование default и допустимых значений. Модифицировать ваш плейбук, чтобы подстановить пустой список в случае отсутствия ssh_pub_keys:
loop: "{{ users | subelements('ssh_pub_keys') | default([]) }}"
Однако, эта конструкция изначально использовалась и не решила ваше затруднение.
-
Параметр skip_missing. Использование этого параметра помогает избежать ошибок, связанных с отсутствующими атрибутами:
loop: "{{ lookup('subelements', users, 'ssh_pub_keys', {'skip_missing': True}) }}"
Вам нужно убедиться, что обработка элементов выполняется правильно, и при выводе данных указывается индекс для получения конкретных атрибутов:
label: "{{ item.0.username }}"
-
Фильтрация списка пользователей. Вы можете применить фильтрацию списка, чтобы предварительно исключить пользователей, у которых отсутствует список ключей:
loop: "{{ users | selectattr('ssh_pub_keys', 'defined') | subelements('ssh_pub_keys', skip_missing=true) }}"
Применение
В реальности, когда вы управляете конфигурацией многих машин, важно не только чтобы плейбук работал, но и чтобы он учитывал все возможные сценарии отсутствия данных. Представляем пример плейбука, который показывал бы, как применить вышеописанные решения:
- hosts: localhost
vars:
users:
- username: usera
ssh_pub_keys:
- pub_key: kdzadizajdiazjd
- pub_key: dzadadazdzadzad
- username: userb
- username: userc
ssh_pub_keys:
- pub_key: ldaljdlasjdlasjd
- username: userd
tasks:
- name: Управление публичными ключами пользователей
authorized_key:
user: "{{ item.0.username }}"
state: present
key: "{{ item.1.pub_key }}"
loop: "{{ users | selectattr('ssh_pub_keys', 'defined') | subelements('ssh_pub_keys', skip_missing=true) }}"
loop_control:
label: "{{ item.0.username }}"
Этот плейбук исключает из обработки пользователей, у которых отсутствуют публичные ключи, и занимается только теми, у кого они есть. Такой подход улучшает надежность и отказоустойчивость плейбука, что критично в управлении конфигурацией IT-систем.
Заключение
Управление отсутствующими данными в Ansible может вызывать затруднения, особенно когда вы работаете с иерархическими структурами данных. Важно правильно использовать фильтры, функции по умолчанию и опции, такие как skip_missing
, чтобы избежать ошибок. Это обеспечит стабильность автоматизации и исключит вынужденные простои или ошибки, связанные с конфигурацией пользователей и их доступов.