_Автор выбрал Mozilla Foundation для получения пожертвования в рамках программы Write for DOnations.
Вступление
Модульное тестирование в Ansible является ключом к тому, чтобы убедиться, что роли работают должным образом. Molecule упрощает этот процесс, позволяя указывать сценарии, тестирующие роли в различных средах. Используя Ansible изнутри, Molecule выгружает роли поставщику, который развертывает роль в сконфигурированной среде и вызывает верификатор (например, Testinfra) для проверки дрейфа конфигурации. Это гарантирует, что ваша роль внесла все ожидаемые изменения в среду в этом конкретном сценарии.
В этом руководстве вы создадите роль Ansible, которая развертывает Apache на хост и настраивает Firewalld в CentOS 7. Чтобы проверить, что эта роль работает должным образом, вы создадите тест в Molecule, используя Docker в качестве драйвера, и Testinfra, библиотеку Python для тестирования состояния серверов. Molecule предоставит контейнеры Docker для проверки роли, а Testinfra проверит, что сервер настроен так, как задумано. Когда вы закончите, вы сможете создавать несколько тестовых случаев для сборок в разных средах и запускать эти тесты, используя Molecule.
Предпосылки
Прежде чем начать это руководство, вам понадобится следующее:
-
Один сервер Ubuntu 16.04. Следуйте инструкциям в руководстве Initial Server Setup с Ubuntu 16.04, чтобы создать пользователя sudo без полномочий root, и убедитесь, что вы можете подключиться к серверу без пароля.
-
Docker установлен на вашем сервере. Выполните шаги 1 и 2 в Как установить и использовать Docker в Ubuntu 16.04 и обязательно добавьте своего пользователя без полномочий root в группу
+ docker +
. -
Знакомство с Ansible playbooks. Для просмотра см. Https://www.digitalocean.com/community/tutorials/configuration-management-101-writing-ansible-playbooks[Configuration Management 101: Написание Ansible Playbooks].
Шаг 1 - Подготовка среды
Чтобы создать нашу роль и тесты, давайте сначала создадим виртуальную среду и установим Molecule. Установка Molecule также установит Ansible, что позволит использовать книги для создания ролей и запуска тестов.
Начните с входа в систему как пользователь без полномочий root и убедитесь, что ваши репозитории обновлены:
sudo apt-get update
Это обеспечит включение в ваш репозиторий последней версии пакета + python-pip +
, который установит + pip +
и Python 2.7. Мы будем использовать + pip +
для создания виртуальной среды и установки дополнительных пакетов. Чтобы установить + pip +
, запустите:
sudo apt-get install -y python-pip
Используйте + pip +
для установки модуля Python `+ virtualenv +:
python -m pip install virtualenv
Далее давайте создадим и активируем виртуальную среду:
python -m virtualenv
Активируйте его, чтобы убедиться, что ваши действия ограничены этой средой:
source my_env/bin/activate
Установите + молекулу
и` + docker` с помощью + pip
:
python -m pip install molecule docker
Вот что будет делать каждый из этих пакетов:
-
+ молекуле +
: это основной пакет Molecule, который вы будете использовать для проверки ролей. Установка+ молекулы +
автоматически устанавливает Ansible вместе с другими зависимостями и позволяет использовать пьесы Ansible для выполнения ролей и тестов. -
+ docker +
: эта библиотека Python используется Molecule для взаимодействия с Docker. Это понадобится вам, поскольку вы используете Docker в качестве драйвера.
Далее, давайте создадим роль в Молекуле.
Шаг 2 - Создание роли в молекуле
Установив свою среду, вы можете использовать Molecule для создания основной роли для проверки установки Apache. Эта роль создаст структуру каталогов и некоторые начальные тесты и определит Docker в качестве драйвера, чтобы Molecule использовал Docker для выполнения своих тестов.
Создайте новую роль с именем + ansible-apache
:
molecule init role -r ansible-apache -d docker
Флаг + -r +
указывает имя роли, в то время как + -d +
указывает драйвер, который предоставляет хосты для Molecule для использования в тестировании.
Перейдите в каталог вновь созданной роли:
cd ansible-apache
Проверьте роль по умолчанию, чтобы проверить, правильно ли настроена Молекула:
molecule test
Вы увидите вывод, в котором перечислены все тестовые действия по умолчанию:
Output--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
...
Перед началом теста Molecule проверяет файл конфигурации + молекуле.имл +
, чтобы убедиться, что все в порядке. Он также печатает эту тестовую матрицу, которая определяет порядок тестовых действий.
Мы подробно обсудим каждое действие теста после того, как вы создали свою роль и настроили свои тесты. А пока обратите внимание на + PLAY_RECAP +
для каждого теста и убедитесь, что ни одно из действий по умолчанию не возвращает статус + failed +
. Например, + PLAY_RECAP +
для действия по умолчанию + 'create' +
должно выглядеть так:
Output...
PLAY RECAP *********************************************************************
localhost : ok=5 changed=4 unreachable=0 failed=0
Давайте перейдем к изменению роли для настройки Apache и Firewalld.
Шаг 3 - Настройка Apache и Firewalld
Чтобы настроить Apache и Firewalld, вы создадите файл задач для роли с указанием пакетов для установки и служб, которые нужно включить. Эти данные будут извлечены из файла переменных и шаблона, которые вы будете использовать для замены индексной страницы Apache по умолчанию.
Создайте файл задач для роли, используя + nano +
или ваш любимый текстовый редактор:
nano tasks/main.yml
Вы увидите, что файл уже существует. Удалите то, что там, и вставьте следующий код, чтобы установить необходимые пакеты и включить правильные службы, параметры HTML по умолчанию и параметры брандмауэра:
~ / Анзибль-Apache / задачи / main.yml
---
- name: "Ensure required packages are present"
yum:
name: "{{ pkg_list }}"
state: present
- name: "Ensure latest index.html is present"
template:
src: index.html.j2
dest: /var/www/html/index.html
- name: "Ensure httpd service is started and enabled"
service:
name: "{{ item }}"
state: started
enabled: true
with_items: "{{ svc_list }}"
- name: "Whitelist http in firewalld"
firewalld:
service: http
state: enabled
permanent: true
immediate: true
Этот сборник включает в себя 4 задания:
-
" Убедитесь, что необходимые пакеты присутствуют "
: Эта задача установит пакеты, перечисленные в файле переменных в+ pkg_list +
. Файл переменных будет расположен в+ ~ / ansible-apache / vars / main.yml +
, и вы создадите его в конце этого шага. -
" Убедитесь, что последний index.html присутствует "
: эта задача скопирует страницу шаблона+ index.html.j2 +
и вставит ее в файл индекса по умолчанию, `+ / var / www / html / index.html + `, сгенерированный Apache. Вы также создадите шаблон на этом шаге. -
" Убедитесь, что служба httpd запущена и включена "
: эта задача запустится и включит службы, перечисленные в+ svc_list +
в файле переменных. -
" Белый список http в firewalld "
: эта задача внесет белый список в службу+ http +
в+ firewalld +
. Firewalld - это законченное решение брандмауэра, присутствующее по умолчанию на серверах CentOS. Чтобы служба+ http +
работала, вам нужно предоставить необходимые порты. Указание+ firewalld +
для внесения в белый список службы гарантирует, что она занесет в белый список все порты, которые требуются службе.
Сохраните и закройте файл, когда вы закончите.
Далее, давайте создадим каталог + templates +
для страницы шаблона + index.html.j2 +
:
mkdir templates
Создайте саму страницу:
nano templates/index.html.j2
Вставьте следующий шаблонный код:
~ / Анзибль-апаш / шаблоны / index.html.j2
<div style="text-align: center">
<h2>Managed by Ansible</h2>
</div>
Сохраните и закройте файл.
Последний шаг в завершении роли - это запись файла переменных, в котором указаны имена пакетов и сервисов для вашей главной книги роли:
nano vars/main.yml
Вставьте содержимое по умолчанию с помощью следующего кода, который задает + pkg_list +
и + svc_list +
:
~ / Анзибль-апаш / вары / main.yml
---
pkg_list:
- httpd
- firewalld
svc_list:
- httpd
- firewalld
Эти списки содержат следующую информацию:
-
+ pkg_list +
: содержит имена пакетов, которые будет установлена ролью:+ httpd +
и+ firewalld +
. -
+ svc_list +
: содержит имена служб, которые роль запускает и включает:+ httpd +
и+ firewalld +
.
Теперь, когда вы закончили создание роли, давайте настроим Molecule, чтобы проверить, работает ли она как задумано.
Шаг 4 - Изменение роли для запуска тестов
В нашем случае настройка Molecule включает в себя изменение файла конфигурации Molecule + молекуле.yml +
для добавления спецификаций платформы. Поскольку вы тестируете роль, которая настраивает и запускает службу + httpd +
systemd, вам нужно будет использовать образ с настроенным systemd и включенным привилегированным режимом. В этом руководстве вы будете использовать изображение + milcom / centos7-systemd +
available на Docker Hub. Привилегированный режим позволяет контейнерам работать практически со всеми возможностями своего хост-компьютера.
Давайте отредактируем + молекулу.имл +
, чтобы отразить эти изменения:
nano molecule/default/molecule.yml
Добавьте выделенную информацию о платформе:
~ / Анзибль-апаш / молекула / по умолчанию / molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8
Сохраните и закройте файл, когда вы закончите.
Теперь, когда вы успешно сконфигурировали тестовую среду, давайте перейдем к написанию тестовых примеров, которые Molecule будет запускать для вашего контейнера после выполнения роли.
Шаг 5 - Написание тестовых случаев
В тесте на эту роль вы будете проверять следующие условия:
-
Установлены пакеты
+ httpd +
и+ firewalld +
. -
Службы
+ httpd +
и+ firewalld +
работают и включены. -
Сервис
+ http +
включен в настройках вашего брандмауэра. -
Этот
+ index.html +
содержит те же данные, которые указаны в вашем файле шаблона.
Если все эти тесты пройдены, то роль работает так, как задумано.
Чтобы написать тестовые случаи для этих условий, давайте отредактируем тесты по умолчанию в + ~ / ansible-apache / молекуле / default / tests / test_default.py +
. Используя Testinfra, вы будете писать контрольные примеры в виде функций Python, которые используют классы Molecule.
Откройте + test_default.py +
:
nano molecule/default/tests/test_default.py
Удалите содержимое файла, чтобы вы могли писать тесты с нуля.
Начните с импорта необходимых модулей Python:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
import os
import pytest
import testinfra.utils.ansible_runner
Эти модули включают в себя:
-
+ os +
: этот встроенный модуль Python обеспечивает функциональные возможности, зависящие от операционной системы, что позволяет Python взаимодействовать с базовой операционной системой. -
+ pytest +
: модуль https://docs.pytest.org/en/latest/ [+ pytest +
] позволяет писать тесты. -
+ testinfra.utils.ansible_runner +
: этот модуль Testinfra использует Ansible в качестве серверной части для выполнения команды.
Под импортом модуля вставьте следующий код, который использует бэкэнд Ansible для возврата текущего экземпляра хоста:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
...
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
С вашим тестовым файлом, настроенным для использования бэкэнда Ansible, давайте напишем модульные тесты для проверки состояния хоста.
Первый тест гарантирует, что + httpd +
и + firewalld +
установлены:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
...
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
Тест начинается с https://docs.pytest.org/en/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions [+ pytest.mark.parametrize +
decorator], который позволяет нам параметризировать аргументы для теста. Этот первый тест примет + test_pkg +
в качестве параметра для проверки наличия пакетов + httpd +
и + firewalld +
.
Следующий тест проверяет, работают ли + httpd +
и + firewalld +
и включены ли они. Он принимает + test_svc +
в качестве параметра:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
...
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
Последний тест проверяет, существуют ли файлы и содержимое, переданные в + parametertrize () +
. Если файл не создан вашей ролью, а содержимое не настроено должным образом, + assert +
вернет + False +
:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
...
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
В каждом тесте + assert
возвращает` + True` или + False
в зависимости от результата теста.
Готовый файл выглядит так:
~ / Анзибль-апаш / молек / по умолчанию / тесты / test_default.py
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
@pytest.mark.parametrize('pkg', [
'httpd',
'firewalld'
])
def test_pkg(host, pkg):
package = host.package(pkg)
assert package.is_installed
@pytest.mark.parametrize('svc', [
'httpd',
'firewalld'
])
def test_svc(host, svc):
service = host.service(svc)
assert service.is_running
assert service.is_enabled
@pytest.mark.parametrize('file, content', [
("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
file = host.file(file)
assert file.exists
assert file.contains(content)
Теперь, когда вы указали свои контрольные примеры, давайте проверим роль.
Шаг 6 - Проверка роли с молекулой
После запуска теста Molecule выполнит действия, которые вы определили в своем сценарии. Теперь давайте снова запустим сценарий «+ молекула +» по умолчанию, выполняя действия в последовательности тестов по умолчанию, при этом более внимательно изучая каждый из них.
Запустите тест для сценария по умолчанию еще раз:
molecule test
Это запустит тестовый прогон. Первоначальный вывод выводит тестовую матрицу по умолчанию:
Output--> Validating schema /home/sammy/ansible-apache/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
Давайте рассмотрим каждое тестовое действие и ожидаемый результат, начиная с linting.
Действие linting выполняет + yamllint +
, + flake8 +
и + ansible-lint +
:
-
+ yamllint +
: этот линтер выполняется для всех файлов YAML, присутствующих в каталоге ролей. -
+ flake8 +
: эта строка кода Python проверяет тесты, созданные для Testinfra. -
+ ansible-lint +
: этот linter для Ansible playbooks выполняется во всех сценариях.
Output...
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /home/sammy/ansible-apache/...
Lint completed successfully.
--> Executing Flake8 on files found in /home/sammy/ansible-apache/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on /home/sammy/ansible-apache/molecule/default/playbook.yml...
Lint completed successfully.
Следующее действие, destroy, выполняется с использованием файла + destroy.yml +
. Это сделано для проверки вашей роли во вновь созданном контейнере.
По умолчанию destroy вызывается дважды: в начале тестового прогона, чтобы удалить все ранее существующие контейнеры, и в конце, чтобы удалить вновь созданный контейнер:
Output...
--> Scenario: 'default'
--> Action: 'destroy'
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) deletion to complete] *******************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Delete docker network(s)] ************************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
После завершения действия уничтожения тест перейдет к dependency. Это действие позволяет вам извлекать зависимости из https://galaxy.ansible.com/ [+ ansible-galaxy +
], если ваша роль требует их. В этом случае роль не:
Output...
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
Следующим тестовым действием является проверка syntax, которая выполняется в playbook по умолчанию + playbook.yml +
. Он работает аналогично флагу + - syntax-check +
в команде + ansible-playbook --syntax-check playbook.yml +
:
Output...
--> Scenario: 'default'
--> Action: 'syntax'
playbook: /home/sammy/ansible-apache/molecule/default/playbook.yml
Затем тест переходит к действию create. Он использует файл + create.yml +
в каталоге Molecule вашей роли для создания контейнера Docker с вашими спецификациями:
Output...
--> Scenario: 'default'
--> Action: 'create'
PLAY [Create] ******************************************************************
TASK [Log into a Docker registry] **********************************************
skipping: [localhost] => (item=None)
skipping: [localhost]
TASK [Create Dockerfiles from image names] *************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Discover local Docker images] ********************************************
ok: [localhost] => (item=None)
ok: [localhost]
TASK [Build an Ansible compatible image] ***************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Create docker network(s)] ************************************************
skipping: [localhost]
TASK [Create molecule instance(s)] *********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) creation to complete] *******************************
changed: [localhost] => (item=None)
changed: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=5 changed=4 unreachable=0 failed=0
После создания тест переходит к действию prepare. Это действие выполняет подготовительную книгу воспроизведения, которая переводит хост в определенное состояние перед запуском конвергенции. Это полезно, если ваша роль требует предварительной настройки системы перед ее выполнением. Опять же, это не относится к нашей роли:
Output...
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
После подготовки действие converge выполняет вашу роль в контейнере, запуская playbook + playbook.yml +
. Если в файле + молекуле.имл +
настроено несколько платформ, Molecule будет сходиться во всех этих:
Output...
--> Scenario: 'default'
--> Action: 'converge'
PLAY [Converge] ****************************************************************
TASK [Gathering Facts] *********************************************************
ok: [centos7]
TASK [ansible-apache : Ensure required packages are present] *******************
changed: [centos7]
TASK [ansible-apache : Ensure latest index.html is present] ********************
changed: [centos7]
TASK [ansible-apache : Ensure httpd service is started and enabled] ************
changed: [centos7] => (item=httpd)
changed: [centos7] => (item=firewalld)
TASK [ansible-apache : Whitelist http in firewalld] ****************************
changed: [centos7]
PLAY RECAP *********************************************************************
centos7 : ok=5 changed=4 unreachable=0 failed=0
После укрытия тест переходит к idempotence. Это действие проверяет книгу на предмет идемпотентности, чтобы убедиться, что в нескольких прогонах не было внесено неожиданных изменений:
Output...
--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
Следующим тестовым действием является действие side-effect. Это позволяет создавать ситуации, в которых вы сможете тестировать больше вещей, например, отработка отказа HA. По умолчанию Molecule не конфигурирует playbook с побочными эффектами, и задача пропускается:
Output...
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
Затем Molecule запустит действие verifier, используя верификатор по умолчанию Testinfra. Это действие выполняет тесты, которые вы написали ранее в + test_default.py +
. Если все тесты пройдены успешно, вы увидите сообщение об успехе, и Molecule перейдет к следующему шагу:
Output...
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/...
============================= test session starts ==============================
platform linux2 -- Python 2.7.12, pytest-3.8.0, py-1.6.0, pluggy-0.7.1
rootdir: /home/sammy/ansible-apache/molecule/default, inifile:
plugins: testinfra-1.14.1
collected 6 items
tests/test_default.py ...... [100%]
========================== 6 passed in 56.73 seconds ===========================
Наконец, Molecule destroys экземпляры завершены во время теста и удаляет сеть, назначенную этим экземплярам:
Output...
--> Scenario: 'default'
--> Action: 'destroy'
PLAY [Destroy] *****************************************************************
TASK [Destroy molecule instance(s)] ********************************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Wait for instance(s) deletion to complete] *******************************
changed: [localhost] => (item=None)
changed: [localhost]
TASK [Delete docker network(s)] ************************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=2 unreachable=0 failed=0
Тестовые действия завершены, и вы убедились, что ваша роль работает так, как задумано.
Заключение
В этой статье вы создали роль Ansible для установки и настройки Apache и Firewalld. Затем вы написали модульные тесты для Testinfra, которые Молекула использовала, чтобы утверждать, что роль успешно выполнена.
Вы можете использовать один и тот же базовый метод для очень сложных ролей, а также автоматизировать тестирование с использованием конвейера CI. Molecule - это настраиваемый инструмент, который можно использовать для тестирования ролей с любыми поставщиками, которые поддерживает Ansible, а не только с Docker. Также возможно автоматизировать тестирование в соответствии с собственной инфраструктурой, следя за тем, чтобы ваши роли всегда были современными и функциональными. Официальная Molecule документация - лучший ресурс для изучения, как использовать Molecule.