Усвоив идею тестирования на Testinfra и переориентировав ее для проверки системы, можно добиться очень многого. Автоматизация запуска тестов в ходе разработки приложений или даже написания и выполнения тестов для существующей инфраструктуры - два прекрасных способа упростить рутинные операции, которые могут вызвать ошибки. Рytest и Testinfra - отличные проекты, начать использовать их очень просто, к тому же при необходимости они легко расширяются. Самое естественное начало разработки проверочных тестов - во время создания самой инфраструктуры. Любое усовершенствование требует нового теста.
Сегодня погоняем пару теcтов через Testinfra.
Вам понадобится установить web-сервер и запустить его на порту 80 для выдачи статической стартовой страницы. По мере достижения прогресса мы будем добавлять новые тесты. Неотъемлемой составляющей написания тестов является осознание сбоев, так что смоделируем несколько проблем, на примере которых будем разбираться, что нужно исправить.
На свежем сервере Ubuntu начните с установки пакета Nginx:
$apt install nginx
Создайте новый файл теста test_webserver.py, в который мы будем постепенно
добавлять тесты:
def test_nginx_is_installed(host):
assert host.package('nginx').is_installed
Сделаем вывод pytest лаконичнее с помощью флага -q, чтобы сосредоточить свое внимание на сбоях. Удаленный сервер называется node4, для подключения к нему используется SSH. Вот команда для запуска первого теста:
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
1 passed in 1.44 seconds
Отлично! Необходимо, чтобы веб-сервер был запущен и работал, так что добавляем новый тест для проверки этого поведения:
def test_nginx_is_running(host):
assert host.service('nginx').is_running
Повторный запуск теста, казалось бы, должен опять пройти успешно:
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
.F
= FAILURES ===================================
_____________________ test_nginx_is_running[ssh://node4] ______________________
host = <testinfra.host.Host object at 0x7f629bf1d668>
def test_nginx_is_running(host):
> assert host.service('nginx').is_running
E AssertionError: assert False
E + where False = <service nginx>.is_running
E + where <service nginx> = <class 'SystemdService'>('nginx')
test_webserver.py:7: AssertionError
1 failed, 1 passed in 2.45 seconds
Некоторые дистрибутивы Linux не разрешают пакетам запускать сервисы при установке. Более того, тест обнаружил, что сервис Nginx не запущен, как сообщает systemd (сервис по умолчанию для работы с юнитами). Если запустить Nginx вручную и выполнить тест еще раз, он снова должен пройти успешно:
(validate) $ systemctl start nginx
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
..
2 passed in 2.38 seconds
Как упоминалось в начале раздела, веб-сервер должен выдавать статическую стартовую страницу на порте 80. Следующий шаг — добавление еще одного теста (в тот же файл test_webserver.py) для проверки порта:
def test_nginx_listens_on_port_80(host):
assert host.socket("tcp://0.0.0.0:80").is_listening
Этот тест несколько сложнее, так что имеет смысл обратить внимание на некоторые его нюансы. По умолчанию он проверяет TCP-соединения на порте 80 для всех IP-адресов данного сервера. Хотя для текущего теста никакой разницы нет, но, если у сервера есть несколько интерфейсов и он настроен на привязку к конкретному адресу, значит, нужно добавить еще один тест. Добавление еще одного теста для проверки прослушивания на порте 80 на конкретном IP-адресе может показаться перебором, но если задуматься о выводимом тестом отчете, станет ясно, что из него будет понятнее, что происходит:
1) Тест, проверяющий, что nginx прослушивает на порте 80: ПРОЙДЕН.
2) Тест, проверяющий, что nginx прослушивает по адресу 192.168.0.2 на порте 80: не пройден.
Из этого видно, что Nginx привязывается к порту 80, просто не к тому интерфейсу, который нужен. Дополнительный тест в данном случае - прекрасный способ улучшить детализацию за счет увеличения объема выводимой информации.
Запустим только что добавленный тест:
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
..F
= FAILURES ===================================
_________________ test_nginx_listens_on_port_80[ssh://node4] __________________
host = <testinfra.host.Host object at 0x7fbaa64f26a0>
def test_nginx_listens_on_port_80(host):
> assert host.socket("tcp://0.0.0.0:80").is_listening
E AssertionError: assert False
E + where False = <socket tcp://0.0.0.0:80>.is_listening
E + where <socket tcp://0.0.0.0:80> = <class 'LinuxSocketSS'>
test_webserver.py:11: AssertionError
1 failed, 2 passed in 2.98 seconds
Ни на одном IP-адресе не производилось прослушивание на порте 80. Из конфигурации Nginx становится ясно, что он был настроен на прослушивание на порте 8080 с помощью инструкции настройки порта в сайте по умолчанию:
(validate) $ grep "listen 8080" /etc/nginx/sites-available/default
listen 8080 default_server;
После изменения на порт 80 и перезапуска сервиса nginx тест снова проходит успешно:
(validate) $ grep "listen 80" /etc/nginx/sites-available/default
listen 80 default_server;
(validate) $ systemctl restart nginx
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
...
3 passed in 2.92 seconds
А поскольку встроенной фикстуры для обработки HTTP-запросов к конкретному адресу не существует, последний тест извлекает контент запущенного вебсайта с помощью утилиты wget и выполняет операторы контроля результатов, чтобы убедиться в правильной визуализации статического сайта:
def test_get_content_from_site(host):
output = host.check_output('wget -qO- 0.0.0.0:80')
assert 'Welcome to nginx' in output
Выполняем test_webserver.py еще раз и убеждаемся, что все наши предположения верны:
(validate) $ pytest -q --hosts='ssh://node4' test_webserver.py
....
4 passed in 3.29 seconds
Кстати, также просто Testinfra интегрируется и с Unittest. Вот пример теста:
import unittest
import testinfra
class Test(unittest.TestCase):
def setUp(self):
self.host = testinfra.get_host("local://")
def test_nginx_service(self):
service = self.host.service("nginx")
self.assertTrue(service.is_running)
def test_nginx_config(self):
self.assertEqual(self.host.run("nginx -t").rc, 0)
def test_nginx_service(self):
service = self.host.service("php-fpm")
self.assertTrue(service.is_running)
if __name__ == "__main__":
unittest.main()
Подробнее про Testinfra можно узнать в книге Ной Гифта и др., "Python и DevOps: Ключ к автоматизации Linux." — СПб.: Питер, 2022, либо на сайте проекта Testinfra.
Успехов.
No comments:
Post a Comment
А что вы думаете по этому поводу?