суббота, 30 марта 2019 г.

Простые юнит-тесты на Python


Привет.

Короче, меня таки засосала подготовка к RHCE. Да, решил начать. Но об этом как-нибудь в следующий раз. Сейчас о том как автоматизированно проверять сложные и не очень конфигурации. Причем для этого не надо быть прям "разработчиком". Сами все увидите...
Началось все с того, что я пошел к коллегам из UNIX команды с целью разузнать что там и как на тему RHCE. Слово за слово и один из ребят (Серега, привет!) рассказал мне как он готовится к RHEL экзаменам. Не к RHCSA/RHCE, а к тем, что потом, что посложнее. Сегодня хочу рассказать про одну из таких вещей, а именно - авто-тестирование.

Разберем на примере как можно легко и просто протестировать настройки LDAP+Kerberos аутентификации на linux машине. 

Настройка LDAP+Kerberos

Итак, быстро опишу как именно производится настройка. Это не тема поста, поэтому почти за пару пунктов.

1. Ставим нужные пакеты
yum install sssd sssd-tools pam_krb5 krb5-workstation

2. Копируем сертификат для LDAP
scp root@srv1.rhcsalab.hi:/root/cacert.p12 /etc/openldap/certs/

3. Правим /etc/sysconfig/authconfig, для того чтобы убедиться в использовании sssd.
USESSSDAUTH=yes
USESSSD=yes
FORCELEGACY=no

4. Собственно сами настройки LDAP + Kerberos через утилиту authconfig, которая генерирует /etc/sssd/sssd.conf.
authconfig \
--enableldap \
--ldapserver=ldap://srv1.rhcsalab.hi \
--ldapbasedn="dc=rhcsalab,dc=hi" \
--enableldaptls \
--enablekrb5 \
--krb5realm RHCSALAB.HI \
--krb5kdc srv1.rhcsalab.hi \
--krb5adminserver srv1.rhcsalab.hi \
--update

5. Правим /etc/sssd/sssd.conf для того чтобы отключить проверку сертификата.
ldap_tls_reqcert = never

6. Рестартуем сервис
systemctl restart sssd

7. Далее проверки.
    Видим ли пользователя из LDAP
id boris

    Можем ли получить тикет от Kerberos
kinit boris

    Появился ли ключ
klist

8. Работает ли SSO, получится ли зайти на сам kerberos сервер без пароля по kerberos тикету.
klist ssh boris@srv1.rhcsalab.hi


Как видно выше, все работает. Не будем заострять внимание на Kerberos, это не тема сегодняшней заметки. Если интересна сама лабораторка и процесс ее создания, то это все я описывал в процессе подготовки к RHCSA.

Юнит-тестирование

Чтобы понять что такое юнит-тестирование, нам нужно немного влезть вообще в теорию тестирования. Экскурс будет коротким. Юнит-тест проверяет функционал какой-то отдельно взятой функции, кнопки, выпадающего меню и т.д. Это должно быть самым простым и элементарным действием во всей системе. Если множество юнит-тестов проходит успешно, то можно с большой долей вероятности предположить, что вся система находится в работоспособном состоянии. Далее начинаются другие тесты, но сейчас опять же не об этом. Если вдруг кто-то из профессиональных QA будет читать эту заметку, было бы интересно узнать ваше мнение.

Наши юнит-тесты я буду писать на Python. Но для начала надо знать что проверять.

Тесты

Я остановился на следующем списке проверок в случае LDAP+Kerberos аутентификации.
  1. Установлены ли нужные пакеты
  2. Есть ли нужные строки в authconfig
  3. Загружен ли сертификат
  4. Есть ли все нужные строки в sssd.conf касаемо LDAP
  5. Есть ли все нужные строки в sssd.conf касаемо Kerberos
  6. Отключена ли валидация сертификата
  7. Рестартуется ли сервис sssd
  8. Встает ли он после этого
  9. Добавлен ли sssd в автозагрузку
  10. Можно ли получить информацю о пользователе в LDAP
К сожалению, проверку самого логина на данный момент я решил не описывать. Во-первых, нужно подставлять пароль, когда его спрашивает Kerberos. Во-вторых, эту часть я могу проверить и руками.

Пробуем

Использовать будем testinfra. Позволяет тестировать состояние серверов в довольно удобном и легком для понимания формате (если не лезть в глубины глубин). Поддерживает много модулей, умеет дружить с Ansible, Salt, Jenkins. Короче, все прям хорошо.

Ставим все это хозяйство через PIP. Тестировать прям на локальной машине не очень интересно, поэтому поставим paramiko для подключения по SSH. Использовал этот модуль когда "брутфорсил" циски.

(main) [root@srv1 ~]# pip3.6 install testinfra paramiko

Создаем папку для тестов и пишем наш первый тест. Важно! Все тесты должны начинаться с test_

(main) [root@srv1 ~]# mkdir testing
(main) [root@srv1 ~]# vim testing/test_kerberos.py

Для начала проверим две простые вещи. Установлен ли SSSD, запущен ли он и добавлен ли в автозагрузку.

def test_sssd_is_installed(host):
    service = host.package("sssd")
    assert service.is_installed

def test_is_sssd_running_and_enabled(host):
    service = host.service("sssd")
    assert service.is_running
    assert service.is_enabled

Запускаем прям на локальной машине через py.test -v


Как видно, две проверки прошли успешно.

Натравливаем эти простые тесты на тестируемую машину по SSH через директиву --hosts. Важно! Доступ должен быть без пароля по ключам.


Тесты прошли успешно, но Paramiko выводит кучу предупреждений про deprecated варианты шифрования.

Добавляем -W ignore.


Теперь все красиво.

Пишем тесты

Итак, прям по пунктам:
1. Установлены ли sssd, sssd-tools, pam_rkb5, krb5-workstation
2. Есть ли нужные строки в authconfig. Заметьте в строках стоит ^, что проверяет начинается ли строка прям сначала. Позволяет понять, когда строка есть, но закомментирована.
3. Есть ли сертификат в нужной директории
4. Есть ли нужные настройки LDAP
5. Отключена ли проверка серитфиката
6. Есть ли строки нужные для Kerberos
7. Рестартуется ли SSSD
8. Немного ждем
9. Проверяем поднят ли SSSD и включен ли он в автозапуск
10. Проверяем, есть ли boris в ldap'е.


import time

def test_is_packages_installed(host):
    assert host.package("sssd")
    assert host.package("sssd-tools")
    assert host.package("pam_krb5")
    assert host.package("krb5-workstation")

def test_authconfig_config(host):
    authconfig = host.file("/etc/sysconfig/authconfig")
    assert authconfig.contains("^USESSSDAUTH=yes")
    assert authconfig.contains("^USESSSD=yes")
    assert authconfig.contains("^FORCELEGACY=no")

def test_is_cert_exist(host):
    cacert = host.file("/etc/openldap/certs/cacert.p12")
    assert cacert.exists

def test_sssd_config_ldap(host):
    sssd_config = host.file("/etc/sssd/sssd.conf")
    assert sssd_config.contains("^id_provider = ldap")
    assert sssd_config.contains("^ldap_search_base = dc=rhcsalab,dc=hi")
    assert sssd_config.contains("^ldap_uri = ldap://srv1.rhcsalab.hi")
    assert sssd_config.contains("^ldap_tls_cacertdir = /etc/openldap/cacerts")

def test_sssd_tls_never(host):
    sssd_config = host.file("/etc/sssd/sssd.conf")
    assert sssd_config.contains("^ldap_tls_reqcert = never")

def test_sssd_config_krbrs(host):
    sssd_config = host.file("/etc/sssd/sssd.conf")
    assert sssd_config.contains("^auth_provider = krb5")
    assert sssd_config.contains("^krb5_realm = RHCSALAB.HI")
    assert sssd_config.contains("^krb5_kpasswd = srv1.rhcsalab.hi")
    assert sssd_config.contains("^krb5_server = srv1.rhcsalab.hi")
    assert sssd_config.contains("^chpass_provider = krb5")

def test_sssd_restart(host):
    cmd =  host.run('systemctl restart sssd')
    assert cmd.rc == 0

def test_just_sleep(host):
    time.sleep(5)

def test_is_sssd_running_and_enabled(host):
    service = host.service("sssd")
    assert service.is_running
    assert service.is_enabled

def test_ldap_query(host):
    username = host.user('boris')
    assert username.uid

Запускаем!


Как видно, все отработало успешно за почти 8 секунд! Теперь я знаю, что как минимум настройки я сделал верные. Все вроде как должно работать, можно идти и проверять сам функционал.

Тестируем тесты

Все нужно тестировать. И тесты тоже!

Попробуем закомментировать ldap_tls_reqcert = never в /etc/sssd/sssd.conf.


Маскируем sssd. Теперь его не получится рестартануть.


Не можем найти пользователя в LDAP.


Заключение

Казалось бы, зачем оно нужно. Все просто. Во-первых, немного качаешься в программировании, хотя согласитесь, все ну ооочень просто. Во-вторых, начинаешь думать со стороны тестирования того, что ты настроил. Это, как мне кажется, развивает аналитические скилы. В-третьих, да, ldap+kerberos не особо сложная часть для настройки и проверки. Но представьте, скажем, настройку чего-нибудь связанного с файловой системой, правами или apache virtual-host. Там уже довольно много винтиков, чтобы забыть их подкрутить. Плюс, не так уж и много занимает написание самих юнит-тестов. Короче, рекомендую! 

Да-да, скоро вероятно пойдут посты про RHCE. )

2 комментария:

  1. Читаю и не совсем понимаю зачем нужны тесты при использовании систем конфигурирования (таких, как Ansible). Ведь мы уже написали роль, плейбук и знаем что и в каком порядке будет выполняться.

    Зачем тогда нужны все эти TestInfra, Molecula и подобные?

    ОтветитьУдалить
    Ответы
    1. Хороший вопрос.

      В статье немного про другое, на самом деле. Тут я рассказывал я я хочу тестировать правильность выполнения задач для RHCE.

      А на тему зачем тесты при использовании систем конфигурирования. На мой взгляд, одно другому не мешает.
      Во-первых, любая система может ошибаться и это неплохо бы проверять.
      Во-вторых, как известно Ansible, например, не хранит у себя состояние управляемых устройств, а это значит, что плейбук может отработать так как ему сказали. Но вот будет ли результат совпадать с ожидаемым не всегда ясно. Скажем, прошлись плейбуком и остались какие-то ненужные артефакты в виде VLANов или что-то наподобие.
      В-третьих, можно же тестировать уже end-to-end сервисы. Скажем, создали везде VLAN, прокинули его куда надо, навесили там еще всего на свете. Самое время проверить а работает ли новый канал.

      Таково мое мнение.

      Удалить