Как предотвратить ошибку пустого списка подэлементов в цикле Ansible?

Вопрос или проблема

Когда я пытаюсь выполнить цикл (с помощью цикла 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. Существуют несколько подходов, чтобы корректно обходить такие ситуации.

  1. Использование default и допустимых значений. Модифицировать ваш плейбук, чтобы подстановить пустой список в случае отсутствия ssh_pub_keys:

    loop: "{{ users | subelements('ssh_pub_keys') | default([]) }}"

    Однако, эта конструкция изначально использовалась и не решила ваше затруднение.

  2. Параметр skip_missing. Использование этого параметра помогает избежать ошибок, связанных с отсутствующими атрибутами:

    loop: "{{ lookup('subelements', users, 'ssh_pub_keys', {'skip_missing': True}) }}"

    Вам нужно убедиться, что обработка элементов выполняется правильно, и при выводе данных указывается индекс для получения конкретных атрибутов:

    label: "{{ item.0.username }}"
  3. Фильтрация списка пользователей. Вы можете применить фильтрацию списка, чтобы предварительно исключить пользователей, у которых отсутствует список ключей:

    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, чтобы избежать ошибок. Это обеспечит стабильность автоматизации и исключит вынужденные простои или ошибки, связанные с конфигурацией пользователей и их доступов.

Оцените материал
Добавить комментарий

Капча загружается...