Мы разработали и продаем программно-аппаратный комплекс для транскодирования (Flussonic Coder). От термина ПАК сводит зубы, но ничего лучше я не встречал и не видел даже в английском языке.
Смысл этой аббревиатуры в том, что мы подобрали всё железо, весь софт, их отладили вместе как единый продукт. Это отличается от варианта, когда наш клиент сам собирает себе сервер, ставит в него какое-то железо и админит убунту на нём.
Поскольку мы никого не пускаем туда и продаем это всё вместе, то и тестировать надо наш софт вместе с линуксом. Разберем несколько аспектов этой задачи.
Взаимодействие с линуксом
На железке стоит флюссоник и в админке у него есть возможность поменять айпишник, дефолтный шлюз и т.п. Мы показываем какой сейчас адрес получен через dhcp. Ещё флюссоник может скачать новую прошивку, положить её на диск, активировать и ребутнуть весь линукс.
Все эти действия должны быть жестко протестированы и тут я опробовал два подхода: плохой и толковый.
Плохой подход был следующим: внутри софта есть интерфейс (в смысле java интерфейс) ко всему перечню действий, которые нам приходится делать с линуксом и есть два модуля с реализацией интерфейса. Один дергает за реальные программы, второй без вызова внешнего дает заранее записанные ответы. Этот подход был выброшен на помойку по причине того, что он полностью исключает тестирование парсинга и формирования вызова команд.
Хороший подход примитивнее и стократ лучше: в тестовый каталог я поместил срез файловой системы со всеми /proc/..., /usr/sbin/..., /etc/systemd файлами, которые нужны для тестов и по сути меняю PATH на запуске внешних команд. В итоге куски флюссоника ничего не знают о том, что работают с подделкой, это позволило ощутимо сократить не только объём кода (что само по себе всегда правильно), но и найти пачку багов в парсинге и разборе внешних данных.
На этом фоне хочется особенно отметить положительную движуху в линуксе по добавлению json в базовые stdout важных программ. То, что развивается, у того уже есть json output, у того, что стагнирует, всё ещё только текст, который надежно распарсить нельзя.
Детали взаимодействия
Во время написания всей этой подсистемы стало хорошо видно, какие куски линукса развиваются, какие не очень. Одним из маркеров стала возможность управления через другой софт. Человеку без разницы: создать новый файл или отредактировать старый, нам вообще слишком легко чуточку подправить файлик, чтобы всё заработало (с этим борется весь девопс).
Systemd предлагает довольно удобную конструкцию по работе с настройкой сети: кладешь файл /etc/systemd/network/... и включаешь принудительную настройку сети, удаляешь его - выключаешь свой оверрайд и позволяешь работать файлу /lib/systemd/network/... Т.е. в /lib лежит то, то мы подготовили исходя из знания о том, какая это железка, а в /etc лежит то, что нужно клиенту здесь и сейчас и оно легко стирается.
Интеграционное тестирование
Подергать за какие-то файлики хорошо, но это юнит тестирование, которое основано на том, что мы очень сильно уверены в качестве интерфейса между двумя компонентами (флюссоник и линукс). Как-то надо тестировать и вторую сторону интерфейса и тут я выбрал интеграционные тесты на всю прошивку. Само транскодирование на спец-железке мы тут не тестируем, это задача флюссоника и тестируется в нём. Тут же у нас продукт и репозиторий под названием «прошивка кодера» и тестируем мы именно те фичи, которые нужны для прошивки, причем достаточно универсальные.
Выход простой, это qemu. Fabrice Bellard один из талантливейших людей, оказавших огромное и очень незаметное влияние на компьютерную индустрию (уж явно побольше чем какая-нибудь перхоть типа гейтса) создал эту штуку и обогнал время на пару десятилетий.
Что мы делаем с qemu? Полностью запускаем цикл прошивки голого кодера с USB флешки, проверяем что он грузится, отвечает на все API запросы, обновляет прошивку, откатывает её, меняет настройки сети.
Всё это происходит в тестах в CI на самом обычном сервере.
Запуск выглядит примерно так:
qemu-system-x86_64 \
-bios chassis/firmware/uefi/OVMF-pure-efi.fd \
-machine q35 \
-m 512 \
-enable-kvm \
--cpu max \
-nographic \
-fw_cfg name=opt/com.flussonic.chassis_conf,file=run/chassis.conf \
-netdev tap,id=net1,script=qemu-network/qemu-ifup.sh,downscript=qemu-network/qemu-ifdown.sh \
-device e1000,netdev=net1,id=net1,mac=52:54:00:12:34:46,bus=pcie.0,addr=0x4 \
-netdev user,id=net0,hostfwd=tcp::10022-:22,hostfwd=tcp::5555-:80 \
-device e1000,netdev=net0,id=net0,mac=52:54:00:c9:18:27,bus=pcie.0,addr=0x6 \
-device ide-hd,bus=ide.1,drive=HDD \
-drive id=HDD,if=none,format=raw,file=output/qemu-rootfs-${CHASSIS_FIRMWARE_VERSION}.img
bus=pcie... нужен для того, чтобы эти виртуальные сетевухи встали на те же места, что и на нашей железке. Поменяется расположение PCIe устройств и всё, нужно будет тут править и добавлять какую-то динамику.
Отдельная хитрость это fw_cfg, который позволяет передать аргументы в линукс внутри, фактически командная строка для операционной системы. Внутри в линуксе это вычитывается через файл /sys/firmware/qemu_fw_cfg/by_name/opt/com.flussonic.autoinstall/raw
Дальше у нас есть свой dhcp сервер, tftp клиент и сервер, позволяющие протестировать всю нужную функциональность, которую мы ожидаем от всей этой фигни.
На что не хватило сил (а очень хотелось) так это научиться цепляться непосредственно к tap2tcp-интерфейсу, торчащему из qemu чтобы из тестов получать полный доступ к внутренней сети qemu и проверять, чего там творится. Это требовало слишком больших усилий, так что в тестах немного шаманства с линуксовым ip перед их запуском.
Спецжелезо так проверить сложно, но штатный линукс можно и нужно.