Comments 12
Мне очень не нравится идея ориентироваться на размер кода в гите при решении "что нужно тестировать". Бывают архиважные проекты из сотни-другой строчек, а бывают вполне себе ленивые помоечки на тысячи строк (про которые известно, что это помоечки), которые едва-едва тестируемы и это ок.
В моём представлении линтеры не являются тестами. Они хороши для того, чтобы затащить в проект наличие CI/CD (когда не знаешь с чего начинать - начинай с линтеров), но первый, основной тест, который нужно написать - это smoke test всего.
Есть плейбука, которая "конфигурирует"? Сделай её на тестовый сервер с другой инвентори. (та же молекула, или даже прямо поверх второго сервера). Как бы уродливо оно не было, 90% проблем в плейбуках будут пойманы на этом этапе.
Если к ней приделать чуть-чуть инфра-тестов (testinfra) - получится уже вполне себе решение на котором можно долго жить. Тесты ролей имеют смысл только если роли большие и подразумевают переиспользование, по моим наблюдениям в большинстве случаев роли "per project" и спокойно тестируются в составе всего остального, без дополнительной бюрократии вида "отдельная репа для роли со своим CI".
И только если проект разрастается до больших размеров (больших == один человек всего уже не помнит), в этот момент можно думать про тесты для подсистем, просто для упрощения и ускорения процесса работы.
Т.е. приоритеты выглядят так (в порядке появления в проекте): staging/empty run, testinfra-тесты, e2e сценарии, тесты модулей (по мере появления модульности).
Мне очень не нравится идея ориентироваться на размер кода в гите при решении "что нужно тестировать". Бывают архиважные проекты из сотни-другой строчек, а бывают вполне себе ленивые помоечки на тысячи строк (про которые известно, что это помоечки), которые едва-едва тестируемы и это ок.
в целом валидно. поэтом и не возвожу это правило в абсолют, это на уровне рекомендаций. ведь ту же помоечку лучше пролинтовать и хотя бы прикрутить молекулу. пусть и с проблемами идемпотентности.
В моём представлении линтеры не являются тестами. Они хороши для того, чтобы затащить в проект наличие CI/CD (когда не знаешь с чего начинать - начинай с линтеров), но первый, основной тест, который нужно написать - это smoke test всего.
линтеры не тесты, но без них не получить быстрой обратной связи прям из в IDE своей не отходя от кассы.
Есть плейбука, которая "конфигурирует"? Сделай её на тестовый сервер с другой инвентори. (та же молекула, или даже прямо поверх второго сервера). Как бы уродливо оно не было, 90% проблем в плейбуках будут пойманы на этом этапе.
обычно молекулу прикручиваем: получаем smoke test, плюс можно не зависеть от тестовых серверов. единственное что в контейнерах есть порой дискомфорт, особенно если настраиваешь контейнеры, трогаешь ядро. приходится какие-то корявые заглушки ставить. вот были бы dependecy injection... зажили бы тогда
Если к ней приделать чуть-чуть инфра-тестов (testinfra) - получится уже вполне себе решение на котором можно долго жить. Тесты ролей имеют смысл только если роли большие и подразумевают переиспользование, по моим наблюдениям в большинстве случаев роли "per project" и спокойно тестируются в составе всего остального, без дополнительной бюрократии вида "отдельная репа для роли со своим CI".
И только если проект разрастается до больших размеров (больших == один человек всего уже не помнит), в этот момент можно думать про тесты для подсистем, просто для упрощения и ускорения процесса работы.
у меня так исторически сложилось, что на большинстве проектов монорепозитории и большинство ролей в одной репе. на каждую роль накидываем молекулу с ansible verifier И рефакторинг/расширение функционала/внесение изменений происходят плавно.
Работает ли молекула как smoke test зависит от того, что тестируется. Если плейбука, то да. Если роль - то нет, увы. Слишком много двигающихся деталей (начиная с опечаток в самой плейбуке).
Насчёт контейнеров... У нас молекула использует delegated (поскольку все драйвера в молекуле - странноватые), и у нас написаны плейбуки для openstack под молекулу. Свои, с учётом нашего openstack'а. Это позволяет тестировать настолько близко к продакшену, насколько можно. У нас часто даже group_vars линкуются из продакшена, и только хосты в инвентори - свои. (Разумеется, это работает, если нет внешних stateful зависимостей). Основная причина, почему мы полностью ушли от контейнеров для тестов - ребут не проверить, /etc/hosts не проверить, настройку сети не проверить, systemd козлит, ntp козлит, machine id козлит... Да что из инфры не козлит в контейнерах?
... У нас тоже монорепы. Репа на роль - это утопия, IMHO. Единственный вид модулярности, который мы себе сделали - это facility (доморощенный термин) - завендоренные (git vendor) репозитории, предоставляющие большой функционал (авторизация, мониторинг и т.д.), где модули, роли и плейбуки. galaxy грозился добавить в коллекции плейбуки, тогда можно на них перейти. До этого момента модульность получается на уровне import_playbook из завендоренного.
А вот тесты отдельных ролей я всё-таки считаю слегка избыточными. Писать приятно, но пользы мало. Если есть тест проекта, то роли в рамках своего применения всё равно тестируются.
с group_vars тоже финт ушами пробовали, но как-то не прижился. через delegated подход позволяет стать ближе к проду, но тогда это очень жестко приколачивает к существющим решениям. мы пока так не готовы и сидим на контейнерах.
вывод у меня напрашивается один с этого - в инфраструктуре не хватате(и не факт что будет) dependency injection, что обрекает тесты быть условными и/или черечур сложными и/или долгими
На самом деле сами молекульщики говорят - пишите на delegated. Реальный размер плейбук - пара сотен строк (с учётом всех нюансов и идемпотентности), а потом их можно переиспользовать снова и снова. А использование виртуалок для тестов офигенно в первую очередь потому, что оно делает процесс тестирования предельно близким к продакшену без спецусилий. Сами виртуалки - что используете, для того и пишите. Мы используем openstack, хотя какой-нибудь gcp или aws туда встраивается тривиально. Для hyper-v, вроде бы, модули тоже есть.
А вот тезис про DI, вы бы не могли развернуть пошире? Например, на псевдокоде/псевдоямле.
А вот тезис про DI, вы бы не могли развернуть пошире? Например, на псевдокоде/псевдоямле.
например есть задача расширить диск, поправить LVM и создать swap. я бы хотел вместо диска подсунуть образ пустышку и заменить модуль создания lvm
---
- name: create swap
hosts: test
inject:
disk:
name: /dev/sda
src: some_image
module:
name: lvol
cmd: return true
tasks:
- name: Read device information
parted:
device: /dev/sda
unit: MiB
register: sda_info
- name: Extend last partition at /dev/sda
parted:
device: "{{ sda_info.partitions | length }}"
label: gpt
number: 2
part_end: "100%"
resize: true
state: present
- name: Create swap logical volume
lvol:
vg: VGsystem
lv: LVswap
size: 8g
- name: format swap partition
command: mkswap /dev/mapper/VGsystem-LVswap
- name: Run swapon on the swap partition
command: swapon /dev/mapper/VGsystem-LVswap
Ну, подмена модулей - это харкдор, потому что модули не совсем "ансибл", и их описать силами ансибла очень сложно (потому что мы хотим не просто "запустить мок", но и получить от него разумный ответ для других модулей, а ,может, и правильный сайд-эффект в setup).
Конкретно эту проблему можно попробовать решить двумя способами:
check_mode - если он поддерживается модулем.
Дать ему /dev/sd-что-то.
Последний раз, когда мне нужно было иметь настоящее scsi (sas) устройство в виртуалке, я монтировал файл через iscsi. Экспортировал диск, потом на localhost обратно цеплял. Настоящее sas-устройство, никаких намёков на loop.
... Хотя это хороший пример, почему контейнеры плохо для тестов. Интеграция такого уровня (сделал lv - получил диск, на диске можно partitions) моками почти не возможна. А вот с помощью "карманного" диска - вполне. Но карманный диск требует рутового доступа к ядру, а докер всё это ломает.
Ок, не модули подменяем, а роли. Но здесь тогда привет: правис некий «глобал стейт» и нет возвращаемого значения.
У меня был в практике один проект где был оркестратор инфрой на ruby. Там под капотом дергался shell, и к этому был прикручен самописный тест фржймворк: мы в нем подменяли класс который дергал шел и вставляли моки.
Погодите, если вы подменяете роли, то это уже ...м.. что-то странное и я не совсем понимаю, что вы тестируете.
Но если вы очень хотите это сделать, то есть role_path в ansible.cfg. Молекула позволяет задавать куски ansible.cfg.
Вообще, в ansible глобальное состояние обязательно, и оно не ограничивается переменными. У вас каждый модуль меняет глобальное состояние.
В целом, мок для роли/плейбуки, если и нужен, должен возвращать похожие сайд-эффекты. iscsi в качестве диска - вполне себе мок.
А вот если вы хотите тестировать ансибл без сайд-эффектов, то это практически не возможно по дизайну, потому что ансибл - это система управления сайд-эффектами. Если вы в плейбуку или роль вместо управления сайд-эффектами кодируете принятие решений, то это ошибка. (Поговорка "ансибл - не язык программирования").
Хорошо написанные роли и плейбуки в ансибле имеют минимум (или полное отсутствие решений) и линейное/декларативное исполнение сайд-эффектов. Соответственно, все тесты для такого должны проверять сайд-эффекты, а не джинджу и when
'ы.
Роли это пример подняться по уровням абстракциям выше. Готового кейса не придумаю. В голову мне с натягом приходит вариант есть роль создания вм подключаемая через include_role куда скармливаются переменные, но это странно
Можно раскрыть мысль про «полное отсутсвие решений»?
Ну, собственно, тезис простой: в ансибле нет адекватных средств программирования. У переменных модель наследования, shadowing'а, приоритетов и т.д. ориентируется на нужды инвентори и контроллера (`-e`), а не ролей. Jinja не очень выразительна. Конвертация данных из jinja в ансибл имеет кучу глюков и не всегда приводит к ожидаемому (https://github.com/amarao/ansible-bingo). Отсутствуют нормальные инструменты для реализации базовых паттернов (даже block не умеет loop).
Другими словами, в Ансибле нет ничего, с помощью чего было бы легко программировать. Наоборот, все наличествующие инструменты целятся в удобство выполнения сайд-эффектов, часто в ущерб программируемости.
Вывод - не надо программировать. Сделали when? Уже подозрительно. Если when по динамической перменной (register/set_fact), то это, практически, code smell. Использовали loop по чему-то, кроме явно переданного из инвентори/групповых переменных/локальному списку - code smell. Совместили loop с when? ой-ой-ой.
Сделали include_role в цикле по результату uri в предыдущей таске с when и развесистой клюквой из jmeth_path/jinja в переменных? Закапывайте и никому не показывайте.
Ансибл - исполнитель сайд-эффектов. Там есть чуть-чуть фэнсервиса, но в целом, вся логика должна быть вне yaml'а. Пишите генераторы инвентори, динамические инвентори, lookup-plugin'ы, модули, динамические факты, пожалуйста. Но не пишите тяжёлого и сложного в jinja, особенно в перемешку с тасками.
Инфраструктура не статична, он как живой организм изменяется во времени. Но, как правило, она стремится к хаосу.
После прочтения этих предложений задёргался глаз... Совпадение?
IaC Development Life Cycle