
Molecule — отличный инструмент для тестирования ролей Ansible, он выполняет надежный и гибкий процесс проверки для обеспечения хорошего уровня качества ролей. Почти вся документация Molecule сосредоточена на драйвере докера, где тесты выполняются с контейнерным сервером, но, несмотря на то, что это хороший выбор в большинстве случаев использования, могут быть случаи, в которых полезно переключиться на внешний облачный бэкэнд с использованием делегированного драйвера.
К сожалению, документация по делегированному драйверу в основном состоит всего из нескольких строк в официальном документе, тогда как более четкое объяснение и несколько примеров могут оказать огромную помощь тем разработчикам, которые хотят использовать Molecule таким образом.
Этот пост основан на моем опыте разработки простой роли Ansible от 0 до galaxy и фокусирует внимание на использовании делегированного драйвера, интегрированного с Google Cloud Platform. В качестве отправной точки я взял следующие полезные ссылки для своего проекта:
- Официальный документ Molecule (https://molecule.readthedocs.io/en/latest/#)
- Хорошее введение в Molecule от Джеффа Герлинга (https://www.jeffgeerling.com/blog/2018/testing-your-ansible-roles-molecule)
- Редкий пример делегированного драйвера из проблемы, открытой для сообщества Molecule (https://github.com/ansible-community/molecule/issues/1292)
- Полное руководство по использованию ansible вместе с Google Cloud Platform от Гэри А. Стаффорда (https://itnext.io/getting-started-with-red-hat-ansible-for-google-cloud-platform-fa666c42a00c)
Делегированный драйвер: что говорит документация Molecule?
Одна из причин, которая заставила меня написать это руководство, — это заявление в официальной документации Molecule:
Разработчик должен придерживаться instance-config API. Пособие по созданию разработчика должно содержать следующие данные instance-config, а руководство по уничтожению разработчика должно сбрасывать instance-config.
Возникает вопрос: что такое instance-config и какие данные должен предоставить разработчик?
Instance-config — это факт Ansible, хранящийся в файле YAML в кэше Molecule ( $HOME/.cache/molecule/<role-name>/<scenario-name>/instance_config.yml), который имеет следующую структуру:
- address: 10.10.15.17 identity_file: /home/fabio/.ssh/id_rsa # mutually exclusive with # password instance: millennium_falcon port: 22 user: hansolo # password: ssh_password # mutually exclusive with identity_file become_method: sudo # optional # become_pass: password_if_required # optional
Для тех, кому нужно иметь дело с узлами Windows, документация также предоставляет эквивалентную структуру для WinRM.
Файл create.yml
После того, как мы прояснили, что такое instance-config, мы можем переходить к следующему шагу. К счастью, Molecule также помогает нам сделать дополнительный шаг вперед, предоставляя файлы шаблонов сценариев с помощью команды molecule init, например:
molecule init scenario -driver-name=delegated
который создает следующую структуру каталогов:
. ├── INSTALL.rst ├── converge.yml ├── create.yml ├── destroy.yml ├── molecule.yml └── verify.yml
molecule.yml— это файл конфигурации Molecule, который определяет переменные, устанавливает фазовую последовательность и конфигурацию для каждой из них.create.yml— код Ansible для создания экземпляров на облачной платформе и хранения данных в instance-config.destroy.ymlкод Ansible для уничтожения экземпляров на облачной платформе и удаления их из instance-configconverge.ymlисполнение ролиverify.ymlнабор проверочных тестовINSTALL.rstинструкции по установке необходимых зависимостей для запуска тестов Molecule
Теперь давайте сосредоточимся на файле create.yml, созданном Molecule:
--- - name: Create hosts: localhost connection: local gather_facts: false no_log: "{{ molecule_no_log }}" tasks: # Developer must implement. # Developer must map instance config. # Mandatory configuration for Molecule to function. — name: Populate instance config dict set_fact: instance_conf_dict: { 'instance': "{{ }}", 'address': "{{ }}", 'user': "{{ }}", 'port': "{{ }}", 'identity_file': "{{ }}", } with_items: "{{ server.results }}" register: instance_config_dict when: server.changed | bool — name: Convert instance config dict to a list set_fact: instance_conf: {{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" when: server.changed | bool — name: Dump instance config copy: content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" dest: "{{ molecule_instance_config }}" when: server.changed | bool
Три задачи: заполнение, преобразование и дамп, создание в конце файла instance-config.yml. Прокомментированный раздел является заполнителем для кода Ansible, который должен создавать облачные ресурсы и возвращать массив серверов (содержащий детали экземпляра) в качестве зарегистрированной переменной или факта. Следующий фрагмент кода, взятый из этой проблемы с github, предоставляет пример того, что указано выше для контекста VMWare:
… 7 - name: Create molecule instance(s) 8 vmware_guest: 9 hostname: "{{ molecule_yml.driver.hostname }}" 10 esxi_hostname: "{{ molecule_yml.driver.esxi_hostname }}" 11 username: "{{ molecule_yml.driver.username }}" 12 password: "{{ molecule_yml.driver.password }}" 13 datacenter: "{{ molecule_yml.driver.datacenter }}" 14 validate_certs: "{{ molecule_yml.driver.validate_certs }}" 15 resource_pool: "{{ molecule_yml.driver.resource_pool }}" 16 folder: "{{ molecule_yml.driver.folder }}" 17 name: "{{ item.name }}" 18 template: "{{ item.template }}" 19 hardware: 20 memory_mb: "{{ item.memory | default(omit) }}" 21 num_cpus: "{{ item.cpu | default(omit) }}" 22 wait_for_ip_address: "yes" 23 state: poweredon 24 register: server 25 with_items: "{{ molecule_yml.platforms }}" 26 27 - name: Populate instance config dict 28 set_fact: 29 instance_conf_dict: { 30 'instance': "{{ item.instance.hw_name }}", 31 'address': "{{ item.instance.ipv4 }}", 32 'user': "vagrant", 33 'port': "22", 34 'identity_file': 'identity_file': "{{ molecule_yml.driver.ssh_identity_file }}" 35 } 36 with_items: "{{ server.results }}" 37 register: instance_config_dict 38 when: server is changed …
Код вызывает модуль vmware_guest (строки 7–23) для создания виртуальной машины на сервере VMWare. Это делается для каждого элемента массива платформ, определенного в файле molecule.yml (строка 25). Как видите, переменные, определенные в файле molecule.yml, доступны через факт molecule_yml.
Значения, возвращаемые каждым вызовом vmware_guest, регистрируются как элементы массива сервера (строка 24), который, в свою очередь, используется для заполнения конфигурации экземпляра (instance-config) (строки 27 и далее). Обратите внимание, что обновление факта конфигурации экземпляра пропускается, если переменная сервера не изменяется.
Работа с Google Cloud Platform (GCP)
Теперь, когда я разъяснил, что и как должен делать разработчик при работе с делегированным драйвером, я собираюсь поделиться работой, проделанной для моей роли docker-secured Ansible. Для этой роли я решил использовать GCP в качестве облачного сервера для делегированного драйвера. Ansible предоставляет семейство модулей GCP для работы с таким облачным провайдером, и я надеюсь, что вы легко сможете адаптировать мой код, если вам нужно сменить семейство модулей и облачного провайдера.
Для этого проекта я использовал следующие версии инструментов:
- python 2.7
- ansible 2.9.6
- molecule 3.0.2
- ansible-lint 4.2.0
- yamllint 1.20.0
- flake8 3.7.9 (mccabe: 0.6.1, pycodestyle: 2.5.0, pyflakes: 2.1.1) CPython 2.7.17 на Linux
где yamllint, ansible-lint и flake8 — инструменты для проверки кода, которые включены в фазы молекулы.
Роль docker-secured
Роль для тестирования устанавливает докер на узел, предоставляет API-интерфейсы докеров и защищает их с помощью ssl. Процедура, которой я следовал, описана в этих двух ссылках из документации Docker:
-
https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd
-
https://docs.docker.com/engine/security/https/
Репозиторий содержит готовые к использованию файлы сертификатов ssl для целей тестирования, но есть также возможность предоставить свои собственные, если они у вас есть.
Вы можете посмотреть мой проект, клонировав репозиторий GitHub:
git clone https://github.com/fabiomarinetti/fmarinetti.docker-secured.git
Предварительные шаги для GCP
Прежде всего, мне нужно было создать проект GCP, учетную запись службы и загрузить связанный ключ. Эти шаги выходят за рамки этого руководства, и вы можете обратиться к официальному документу GCP для получения общей процедуры. В качестве хорошей ссылки я также нашел эту ссылку полезной для всего, что связано с совместной работой Ansible и GCP.
Для этой роли я создал проект ansible-272015 и службу учетной записи service, ее ключ хранится в файле secret.json.
Файл molecule.yml
В этом разделе я покажу и прокомментирую соответствующий раздел моего файла molecule.yml.
Проект, тип аутентификации и секретный ключ вставляются в файл molecule.yml в разделе driver. Кроме того, я также добавил в тот же раздел все другие параметры, которые остаются постоянными на этапах создания и уничтожения, например, регион и зона GCP, пользователь ssh и файл идентификатора, а также параметры сети, поскольку предполагается, что виртуальные машины находятся в той же сети, в которой создан ad-hoc на время теста. Ко всем этим значениям можно получить доступ из сценария через molecule_yml (например, molecule_yml.driver.region для доступа к региону).
20 driver: 21 name: delegated 22 gcp_service_account_key: ${GOOGLE_APPLICATION_CREDENTIALS} 23 gcp_project_id: ansible-272015 24 region: us-east1 25 zone: us-east1-c 26 ssh_user: ${SSH_USER} 27 ssh_pub_key_file: "${SSH_ID_FILE}.pub" 28 ssh_key_file: "${SSH_ID_FILE}" 29 network_name: ansible-network 30 subnet_name: ansible-subnet 31 firewall_name: ansible-firewall 32 ip_cidr_range: 172.16.0.0/28
Раздел платформы в файле molecule.yml содержит массив, содержащий параметры (имя, изображение, тип, размер…) для экземпляров, которые я хочу проверить. Мое тестовое покрытие включает CentOS 7, Ubuntu Xenial 16.04 и Ubuntu Bionic 18.04. Эти машины сгруппированы по типу ОС (например, CentOS или Ubuntu), чтобы использовать группы инвентаря при выполнении Ansible.
41 platforms: 42 - name: "ds-centos7-${TRAVIS_BUILD_ID}" 43 image_family: projects/centos-cloud/global/images/family /centos-7 44 machine_type: n1-standard-1 45 size_gb: 200 46 groups: 47 - centos 48 - name: "ds-ubuntu-bionic-${TRAVIS_BUILD_ID}" 49 image_family: projects/ubuntu-os-cloud/global/images/family /ubuntu-1804-lts 50 machine_type: n1-standard-1 51 size_gb: 200 52 groups: 53 - ubuntu 54 - name: "ds-ubuntu-xenial-${TRAVIS_BUILD_ID}" 55 image_family: projects/ubuntu-os-cloud/global/images/family /ubuntu-1604-lts 56 machine_type: n1-standard-1 57 size_gb: 200 58 groups: 59 - ubuntu
В других разделах файла molecule.yml определяется тестовая последовательность и конфигурации для каждой фазы, хотя они не заданы по умолчанию.
Фаза создания и файл create.yml
Как уже говорилось ранее, create.yml — это сценарий, который управляет этапом создания. Здесь я широко использовал модули семейства gcp для управления ресурсами облачного провайдера (GCP). Модули GCP нуждаются в каком-то фиксированном параметре, таком как идентификатор проекта, тип аутентификации и путь к секретному ключу, и, чтобы избежать повторения этих значений в коде при каждом вызове модуля, я установил их как module_defaults для всего семейства gcp.
7 module_defaults: 8 group/gcp: 9 project: "{{ molecule_yml.driver.gcp_project_id }}" 10 auth_kind: serviceaccount 11 service_account_file: "{{ molecule_yml.driver.gcp_service_account_key }}"
В отличие от того, что мы видели ранее в случае VMWare, создание экземпляра в GCP — это не просто вопрос использования одного модуля, а процесс, состоящий из нескольких шагов: создание загрузочного диска, назначение IP-адреса и создание самого экземпляра. Это означает, что для цикла по платформам мне нужно было поместить задачи создания в отдельный файл и включить его в цикл:
16 — name: create instances 17 include_tasks: tasks/create_instance.yml 18 loop: "{{ molecule_yml.platforms }}"
Файл create_instance.yml содержит задачи по резервированию IP-адреса, созданию загрузочного диска и созданию экземпляра. То, как я вызвал связанные модули, довольно стандартно и их можно менять, если вы хотите переключиться на другого облачного провайдера, поэтому я не буду их обсуждать дальше, а лишь хочу сказать несколько слов о том, как возвращать данные экземпляра для подачи задач заполнения instance-config.
7 - name: initialize instance facts 8 set_fact: 9 instance_created: 10 instances: [] 11 when: instance_created is not defined ... create the instance and return instance variable ... 56 - name: update instance facts 57 set_fact: 58 instance_created: 59 changed: instance.changed | bool 60 instances: "{{ instance_created.instances + [ instance ]}}"
Затем после цикла платформы для заполнения isntance-config используется факт instance_create:
20 - name: Populate instance config dict 21 set_fact: 22 instance_conf_dict: { 23 'instance': "{{ item.name }}", 24 'address': "{{ item.networkInterfaces[0].accessConfigs[0].natIP }}", 25 'user': "{{ molecule_yml.driver.ssh_user }}", 26 'port': "22", 27 'identity_file': "{{ molecule_yml.driver.ssh_key_file }}", } 28 with_items: "{{ instance_created.instances }}" 29 register: instance_config_dict 30 when: instance_created.changed
Здесь эта задача выполняется, только если один из экземпляров был изменен, как это произошло в случае VMWare, когда было указано предложение servers is changed
Наконец, я протестировал этап создания, введя команду:
molecule create --scenario-name=gcp
Убедившись, что результаты созданы правильно, я перешел к конвейеру и выполнил / протестировал фазы:
- lint, которая выполняет проверку кода
- prepare, которая подготавливает экземпляр для применения роли. В данном случае это просто обновление исходных кодов пакетов для группы ubuntu.
- converge, которая просто применяет роль
- idempotence, которая применяет роль во второй раз для обеспечения ее идемпотентности
- verify, которая подтверждает, что результаты применения роли соответствуют ожиданиям
molecule <phase> --scenario-name=gcp
В этом случае, учитывая простоту роли и ограниченные требования, мне не пришлось так сильно менять Молекулу, генерируемую при инициализации сценария.
На последнем шаге я написал destroy.yml для удаления созданных ресурсов из проекта (а также моего счета ). Код для уничтожения ресурсов следует той же философии, что и тот, который их создает. Очевидно, проверка проводилась путем выдачи:
molecule destroy --scenario-name=gcp
Как только все этапы были правильными и не выдали ошибок, я мог протестировать весь процесс с помощью команды:
molecule test --scenario-test=gcp
Выводы
В этом посте я объяснил, как использовать делегированный драйвер Molecule, и показал, как я реализовал его с помощью GCP. Этот же код легко адаптировать к другому облачному провайдеру: AWS, Azure, Digital Ocean… и я надеюсь, что вы наверняка получите выгоду от использования Molecule. Пожалуйста, оставьте отзыв.
ссылка на оригинал статьи https://habr.com/ru/post/536892/
Добавить комментарий