Здравейте на всички!
Работя като DevOps инженер в услуги за резервация на хотели . В тази статия искам да споделя нашия опит в тестването на ansible роли.
В Ostrovok.ru като мениджър на конфигурации използваме ansible. Напоследък стигнахме до необходимостта от тестване на ролите, но, както се оказа, инструментите за това не са много — най-популярният е, вероятно, фреймворкът Molecule, затова решихме да го използваме. Но се оказа, че неговата документация не покрива много подводни камъни. Не успяхме да намерим достатъчно подробно ръководство на български, затова решихме да напишем тази статия.

Molecule
— фреймворк за помощ в тестването на ansible роли.
Определение: Molecule създава инстанция на избраната от вас платформа (облако, виртуален сървър, контейнер; повече информация вижте в раздела ), изпълнява вашата роля на нея, след което стартира тестовете и изтрива инстанцията. В случай на неуспех на една от стъпките, Molecule ще ви уведоми за това.
Сега по-подробно.
Няколко теории
Нека разгледаме две ключови единици на Molecule: Scenario и Driver.
Scenario
Сценарият съдържа описание на това какво, къде, как и в каква последователност ще бъде изпълнено. Една роля може да има няколко сценария, като всеки един от тях е директория в пътя /molecule/, съдържаща описания на необходимите действия за теста. Задължително трябва да присъства сценарий по подразбиране, който ще бъде автоматично създаден, ако инициализирате ролята с помощта на Molecule. Имената на останалите сценарии се избират по ваше усмотрение.
Последователността на действията в сценария за тестване се нарича matrix, а по подразбиране тя е следната:
(Стъпките, обозначени ?, по подразбиране се пропускат, ако не са описани от потребителя)
lint— изпълнение на линтъри. По подразбиране се използватyamllintиflake8,destroy— изтриване на инстанции от предишното изпълнение на Molecule (ако има останали),dependency? — установка ansible-зависимости тестируемой роли,syntax— проверка на синтаксиса на ролята с помощта наansible-playbook --syntax-check,create— създаване на инстанция,prepare? — подготовка инстанса; например, проверка / установка python2converge— стартиране на тествания плейбук,idempotence— повторно изпълнение на плейбука за тест на идемпотентност,side_effect? — действия, не относящиеся непосредственно к роли, но нужные для тестов,verify— стартиране на тестовете на получената конфигурация с помощта наtestinfra(по подразбиране) /goss/inspec,cleanup? — (в новых версиях) — грубо говоря, «очистка» внешней инфраструктуры, задетой Молекулой,destroy— изтриване на инстанцията.
Тази последователност покрива повечето случаи, но при необходимост може да бъде променена.
Всяка от гореописаните стъпки може да се стартира отделно с помощта на molecule. Но е важно да разберем, че за всяка такава cli-команда може да има своя последователност от действия, която може да се открие чрез изпълнение на molecule matrix. Например, при стартиране на командата converge (изпълнение на тестовия плейбук) ще се извършат следните действия:
$ molecule matrix converge
...
└── default # име на сценария
├── dependency # инсталиране на зависимости
├── create # създаване на инстанс
├── prepare # предварителна настройка на инстанса
└── converge # изпълнение на плейбукаПоследователността на тези действия може да се редактира. Ако нещо от списъка вече е изпълнено, то ще бъде пропуснато. Текущото състояние, както и конфигурацията на инстансите, Молекула съхранява в директорията $TMPDIR/molecule//.
Добавянето на стъпки с ? може да се извърши, описвайки желаните действия във формат ansible плейбук, а името на файла да бъде съответно на стъпката: prepare.yml/side_effect.yml. Очаквайте тези файлове Молекула да бъдат в папката на сценария.
Driver
Драйверът е същество, където се създават инстанси за тестове.
Списъкът със стандартни драйвери, за които Молекулата има готови шаблони, е следният: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
В повечето случаи шаблоните са файлове create.yml и destroy.yml в папката на сценария, които описват създаването и изтриването на инстанс съответно.
Изключения представляват Docker и Vagrant, тъй като взаимодействията с техните модули могат да се осъществяват без гореспоменатите файлове.
Стои да се открои драйверът Delegated, тъй като в случай на неговото използване в файловете за създаване и изтриване на инстанси е описана само работа с конфигурацията на инстансите, останалото трябва да опише инженер.
По подразбиране драйверът е Docker.
Сега да преминем към практиката и по-другите особености да разгледаме там.
Започване
Като 'hello world' ще тестваме проста роля за инсталиране на nginx. Като драйвер ще изберем Docker – мисля, той е инсталиран при повечето от вас (и помнете, че Docker е драйверът по подразбиране).
Подготвяме virtualenv и инсталираме в него molecule > pip install virtualenv > virtualenv -p `which python2` venv > source venv/bin/activate > pip install molecule docker # molecule ще инсталира ansible като зависимост; docker за драйвера:
Следващата стъпка е иницииране на нова роля.Инициирането на нова роля, както и на нов сценарий, се извършва с помощта на командата
molecule init молекула инициализиране:
> molecule init role -r nginx
--> Инициализиране на нова роля nginx...
Ролята е инициализирана успешно в /nginx.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars
6 директории, 1 файлПолучена типична ansible роля. Всички взаимодействия с CLI Molecule се извършват от корена на ролята.
Нека да видим какво има в директорията на ролята:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Jinja шаблон за Dockerfile
├── INSTALL.rst. # Няколко информации за инсталиране на зависимости
├── molecule.yml # Конфигурационен файл
├── playbook.yml # Плейбук за стартиране на ролята
└── tests # Директория с тестове от етапа verify
└── test_default.py
1 директория, 6 файлаНека разгледаме конфигурацията molecule/default/molecule.yml (ще сменим само docker image):
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
platforms:
- name: instance
image: centos:7
provisioner:
name: ansible
lint:
name: ansible-lint
scenario:
name: default
verifier:
name: testinfra
lint:
name: flake8dependency
Тази секция описва източника на зависимости.
Възможни варианти: , , shell.
Shell е просто команден интерфейс, който се използва, когато galaxy и gilt не покриват вашите нужди.
Няма да се задържам дълго тук, достатъчно е написано в .
driver
Име на драйвера. При нас това е docker.
lint
Като линтер се използва yamllint.
Полезни опции в тази част на конфигурацията са възможността да посочите конфигурационен файл за yamllint, да предадете променливи на средата или да деактивирате линтера:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: Falseplatforms
Описва конфигурацията на инстансите.
При използване на docker като драйвер, Molecule итерира през тази секция, и всеки елемент от списъка е достъпен в Dockerfile.j2 като променлива item.
При драйвер, който изисква create.yml и destroy.yml, секцията е достъпна в тях като molecule_yml.platforms, а итерациите по нея са описани в тези файлове.
Тъй като Molecule предоставя управление на инстансите на ansible модулите, списъкът с възможни настройки трябва да се търси там. Например, за docker се използва модул . Можете да намерите кои модули се използват в останалите драйвери в .
Също така примери за използване на различни драйвери могат да бъдат намерени .
Ще сменим тук centos:7 на ubuntu.
provisioner
«Provisioner» — субект, управляващ инстансите. В случая на Molecule това е ansible, поддръжката на други не е планирана, така че тази секция може да бъде наречена разширена конфигурация на ansible с уговорка.
Тук можете да посочите много неща, но ще изтъкна основните, на моето мнение, моменти:
- playbooks: можете да посочите кои плейбуци трябва да се използват на определени етапи.
provisioner:
name: ansible
playbooks:
create: create.yml
destroy: ../default/destroy.yml
converge: playbook.yml
side_effect: side_effect.yml
cleanup: cleanup.yml- config_options:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: True- connection_options: параметри
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"- опции: параметри Ansible и променливи на средата
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenario
Име и описание на последователностите на сценария.
Можете да промените матрицата на действия по подразбиране за определена команда, като добавите ключ <command>_sequence и зададете необходимия списък от стъпки като стойност за него.
Да предположим, че искаме да променим последователността на действията при изпълнение на командата за стартиране на плейбук: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeverifier
Настройка на рамката за тестове и линтер за нея. По подразбиране се използва линтер testinfra и flake8. Възможните опции са подобни на вече споменатите:
verifier:
name: testinfra
additional_files_or_dirs:
- ../path/to/test_1.py
- ../path/to/test_2.py
- ../path/to/directory/*
options:
n: 1
enabled: False
env:
FOO: bar
lint:
name: flake8
options:
benchmark: True
enabled: False
env:
FOO: barНека се върнем към нашата роля. Нека редактираме файла tasks/main.yml до следния вид:
---
- name: Install nginx
apt:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
И добавим тестове в molecule/default/tests/test_default.py
def test_nginx_is_installed(host):
nginx = host.package("nginx")
assert nginx.is_installed
def test_nginx_running_and_enabled(host):
nginx = host.service("nginx")
assert nginx.is_running
assert nginx.is_enabled
def test_nginx_config(host):
host.run("nginx -t")
Готово, остава само да изпълните (от корена на ролята, напомням):
> molecule testДълъг изход под спойлер:
--> Валидация на схемата /nginx/molecule/default/molecule.yml.
Валидацията завърши успешно.
--> Тестова матрица
└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy
--> Сценарий: 'default'
--> Действие: 'lint'
--> Изпълнение на Yamllint върху файловете в /nginx/...
Lintът завърши успешно.
--> Изпълнение на Flake8 върху файловете в /nginx/molecule/default/tests/...
Lintът завърши успешно.
--> Изпълнение на Ansible Lint върху /nginx/molecule/default/playbook.yml...
Lintът завърши успешно.
--> Сценарий: 'default'
--> Действие: 'destroy'
PLAY [Унищожаване] *************************************************************
ЗАДАЧА [Унищожаване на молекулни екземпляри] ********************************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Изчакване за завършване на изтриването на инстанцията(ите)] ************
добре: [localhost] => (item=None)
добре: [localhost]
ЗАДАЧА [Изтриване на docker мрежи] ********************************************
PLAY РЕЗЮМЕ ******************************************************************
localhost : добре=2 променено=1 недостъпен=0 неуспешен=0
--> Сценарий: 'default'
--> Действие: 'dependency'
Пропуснато, липсва файлът с изисквания.
--> Сценарий: 'default'
--> Действие: 'syntax'
playbook: /nginx/molecule/default/playbook.yml
--> Сценарий: 'default'
--> Действие: 'create'
PLAY [Създаване] ***************************************************************
ЗАДАЧА [Вход в Docker хранилище] **********************************************
пропуснато: [localhost] => (item=None)
ЗАДАЧА [Създаване на Dockerfiles от имена на изображения] *********************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Откриване на локални Docker изображения] ******************************
добре: [localhost] => (item=None)
добре: [localhost]
ЗАДАЧА [Създаване на образ, съвместим с Ansible] ***************************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Създаване на docker мрежи] ********************************************
ЗАДАЧА [Създаване на молекулни екземпляри] **********************************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Изчакване за завършване на създаването на инстанцията(ите)] ********
променено: [localhost] => (item=None)
променено: [localhost]
PLAY РЕЗЮМЕ ******************************************************************
localhost : добре=5 променено=4 недостъпен=0 неуспешен=0
--> Сценарий: 'default'
--> Действие: 'prepare'
Пропуснато, playbook за подготовка не е конфигуриран.
--> Сценарий: 'default'
--> Действие: 'converge'
PLAY [Конвергенция] ***********************************************************
ЗАДАЧА [Събиране на факти] ***************************************************
добре: [инстанция]
ЗАДАЧА [nginx : Инсталиране на nginx] ****************************************
променено: [инстанция]
ЗАДАЧА [nginx : Стартиране на nginx] ******************************************
променено: [инстанция]
PLAY РЕЗЮМЕ ******************************************************************
инстанция : добре=3 променено=2 недостъпен=0 неуспешен=0
--> Сценарий: 'default'
--> Действие: 'idempotence'
Idempotence завърши успешно.
--> Сценарий: 'default'
--> Действие: 'side_effect'
Пропуснато, playbook за странични ефекти не е конфигуриран.
--> Сценарий: 'default'
--> Действие: 'verify'
--> Изпълнение на Testinfra тестовете, намерени в /nginx/molecule/default/tests/...
============================= тест сесията започва ==============================
платформа darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
rootdir: /nginx/molecule/default, inifile:
плъгини: testinfra-1.16.0
събрани 4 елемента
tests/test_default.py .... [100%]
========================== 4 преминати за 27.23 секунди ===========================
Проверяващият завърши успешно.
--> Сценарий: 'default'
--> Действие: 'destroy'
PLAY [Унищожаване] *************************************************************
ЗАДАЧА [Унищожаване на молекулни екземпляри] ********************************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Изчакване за завършване на изтриването на инстанцията(ите)] ************
променено: [localhost] => (item=None)
променено: [localhost]
ЗАДАЧА [Изтриване на docker мрежи] ********************************************
PLAY РЕЗЮМЕ ******************************************************************
localhost : добре=2 променено=2 недостъпен=0 неуспешен=0
Нашата проста роля успешно премина тестовете.
Важно е да запомните, че ако се появят проблеми при работата molecule test, ако не сте променяли стандартната последователност, Молекула ще изтрие инстанса.
За дебъг полезни са следните команди:
> molecule --debug # информация за дебъг. При нормално стартиране Молекула скрива логовете.
> molecule converge # Оставя инстанса след изпълнението на тестуваната роля.
> molecule login # Вход в създадения инстанс.
> molecule --help # Пълен списък с команди.Съществуваща роля
Добавянето на нов сценарий към съществуваща роля става от директорията на роля следните команди:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>В случай, че това е първият сценарий в ролята, параметърът -s може да бъде пропуснат, тъй като ще бъде създаден сценарий. по подразбиране.
Заключение
Както виждате, Молекула не е много сложна, а при използване на собствени шаблони може да се сведе разгръщането на нов сценарий до редактиране на променливи в плейбуковете за създаване и изтриване на инстанси. Молекула безпроблемно се интегрира с CI системи, което позволява увеличаване на скоростта на разработване, като се намалява времето за ръчно тестване на плейбуковете.
Благодаря за вашето внимание. Ако имате опит с тестване на ansible роли и той не е свързан с Молекула — споделете го в коментарите!
Източник: habr.com
