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: poweredon24 register: server25 with_items: "{{ molecule_yml.platforms }}"26 27 - name: Populate instance config dict28 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_dict38 when: server is changed
Код вызывает модуль vmware_guest
(строки 723) для
создания виртуальной машины на сервере 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: delegated22 gcp_service_account_key: ${GOOGLE_APPLICATION_CREDENTIALS}23 gcp_project_id: ansible-27201524 region: us-east125 zone: us-east1-c26 ssh_user: ${SSH_USER}27 ssh_pub_key_file: "${SSH_ID_FILE}.pub"28 ssh_key_file: "${SSH_ID_FILE}"29 network_name: ansible-network30 subnet_name: ansible-subnet31 firewall_name: ansible-firewall32 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-744 machine_type: n1-standard-145 size_gb: 20046 groups:47 - centos48 - name: "ds-ubuntu-bionic-${TRAVIS_BUILD_ID}"49 image_family: projects/ubuntu-os-cloud/global/images/family /ubuntu-1804-lts50 machine_type: n1-standard-151 size_gb: 20052 groups:53 - ubuntu54 - name: "ds-ubuntu-xenial-${TRAVIS_BUILD_ID}"55 image_family: projects/ubuntu-os-cloud/global/images/family /ubuntu-1604-lts56 machine_type: n1-standard-157 size_gb: 20058 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: serviceaccount11 service_account_file: "{{ molecule_yml.driver.gcp_service_account_key }}"
В отличие от того, что мы видели ранее в случае VMWare, создание экземпляра в GCP это не просто вопрос использования одного модуля, а процесс, состоящий из нескольких шагов: создание загрузочного диска, назначение IP-адреса и создание самого экземпляра. Это означает, что для цикла по платформам мне нужно было поместить задачи создания в отдельный файл и включить его в цикл:
16 name: create instances17 include_tasks: tasks/create_instance.yml18 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 facts57 set_fact:58 instance_created:59 changed: instance.changed | bool60 instances: "{{ instance_created.instances + [ instance ]}}"
Затем после цикла платформы для заполнения isntance-config
используется факт instance_create
:
20 - name: Populate instance config dict21 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_dict30 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. Пожалуйста, оставьте отзыв.