Упутства: како тестирати ансибле улоге и сазнати о проблемима пре продукције

Поздрав свима!

Радим као ДевОпс инжењер за услугу резервације хотела. Островок.ру. У овом чланку желим да причам о нашем искуству у тестирању Ансибле улога.

На Островок.ру користимо ансибле као менаџер конфигурације. Недавно смо дошли до потребе да тестирамо улоге, али, како се испоставило, нема много алата за то - најпопуларнији је, можда, оквир Молецуле, па смо одлучили да га користимо. Али испоставило се да његова документација не говори о многим замкама. Нисмо могли да пронађемо довољно детаљан водич на руском, па смо одлучили да напишемо овај чланак.

Упутства: како тестирати ансибле улоге и сазнати о проблемима пре продукције

молекул

Молецуле — оквир који помаже у тестирању Ансибле улога.

Поједностављени опис: Молецуле креира инстанцу на платформи коју наведете (облак, виртуелна машина, контејнер; за више детаља погледајте одељак возач), покреће вашу улогу на њему, затим покреће тестове и брише инстанцу. Ако дође до квара на једном од корака, Молекул ће вас обавестити о томе.

Сада више.

Мало теорије

Хајде да размотримо два кључна ентитета Молекула: сценарио и покретач.

сценарио

Скрипта садржи опис шта, где, како и којим редоследом ће се изводити. Једна улога може имати неколико скрипти, а свака је директоријум дуж путање <role>/molecule/<scenario>, који садржи описе радњи потребних за тест. Мора да постоји сценарио default, који ће се аутоматски креирати ако иницијализујете улогу користећи Молецуле. Називи следећих скрипти су по вашем нахођењу.

Позива се редослед радњи тестирања у скрипти матрица, а подразумевано је овако:

(Означени кораци ?, се подразумевано прескачу ако их корисник не одреди)

  • lint - трчање линтера. Подразумевано yamllint и flake8,
  • destroy — брисање инстанци из последњег лансирања Молецуле-а (ако их преостане),
  • dependency? — инсталирање ансибле зависности тестиране улоге,
  • syntax - провера синтаксе улога коришћењем ansible-playbook --syntax-check,
  • create - стварање инстанце,
  • prepare? — припрема инстанце; на пример провера/инсталација питхон2
  • converge — покретање тестираног приручника,
  • idempotence — поново покрените приручник за тест идемпотенције,
  • side_effect? — радње које нису директно повезане са улогом, али су неопходне за тестирање,
  • verify — покретање тестова резултујуће конфигурације користећи testinfra(Уобичајено) /goss/inspec,
  • cleanup? - (у новим верзијама) - грубо речено, "чишћење" спољне инфраструктуре на коју утиче Молекул,
  • destroy — брисање инстанце.

Ова секвенца покрива већину случајева, али се може изменити ако је потребно.

Сваки од горе наведених корака може се покренути засебно користећи molecule <command>. Али треба да схватите да за сваку такву цли команду може постојати сопствени редослед радњи, што можете сазнати покретањем molecule matrix <command>. На пример, приликом покретања команде converge (покретање тестираног плаибоок-а) ће се извршити следеће радње:

$ molecule matrix converge
...
└── default         # название сценария
    ├── dependency  # установка зависимостей
    ├── create      # создание инстанса
    ├── prepare     # преднастройка инстанса
    └── converge    # прогон плейбука

Редослед ових радњи се може мењати. Ако је нешто са листе већ завршено, биће прескочено. Тренутно стање, као и конфигурација инстанце, се чувају у директоријуму Молецуле $TMPDIR/molecule/<role>/<scenario>.

Додајте кораке са ? Можете описати жељене радње у Ансибле плаибоок формату и направити назив датотеке према кораку: prepare.yml/side_effect.yml. Очекујте да ове датотеке Молецуле буду у фасцикли скрипте.

возач

Драјвер је ентитет у коме се креирају инстанце за тестове.
Листа стандардних драјвера за које Молецуле има готове шаблоне је: Азуре, Доцкер, ЕЦ2, ГЦЕ, ЛКСЦ, ЛКСД, ОпенСтацк, Вагрант, Делегатед.

У већини случајева, шаблони су датотеке create.yml и destroy.yml у фасцикли скрипте, који описују креирање и брисање инстанце, респективно.
Изузеци су Доцкер и Вагрант, пошто се интеракције са њиховим модулима могу десити без горе наведених датотека.

Вреди истаћи Делегирани драјвер, јер ако се користи, само рад са конфигурацијом инстанце је описан у датотекама за креирање и брисање инстанце, остало треба да опише инжењер.

Подразумевани драјвер је Доцкер.

Сада пређимо на праксу и размотримо даље карактеристике.

Први кораци

Као „здраво свет“ ми ћемо тестирати једноставну нгинк инсталациону улогу. Хајде да изаберемо доцкер као драјвер – мислим да га је већина вас инсталирала (и запамтите да је доцкер подразумевани драјвер).

Припремити virtualenv и инсталирајте га у њега molecule:

> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker  # molecule установит ansible как зависимость; docker для драйвера

Следећи корак је иницијализација нове улоге.
Иницијализација нове улоге, као и нове скрипте, врши се помоћу команде molecule init <params>:

> molecule init role -r nginx
--> Initializing new role nginx...
Initialized role in <path>/nginx successfully.
> cd nginx
> tree -L 1
.
├── README.md
├── defaults
├── handlers
├── meta
├── molecule
├── tasks
└── vars

6 directories, 1 file

Резултат је типична ансибле улога. Даље, све интеракције са Молецулес ЦЛИ су направљене од корена улоге.

Хајде да видимо шта се налази у директоријуму улога:

> tree molecule/default/
molecule/default/
├── Dockerfile.j2  # Jinja-шаблон для Dockerfile
├── INSTALL.rst.   # Немного информации об установке зависимостей сценария
├── molecule.yml   # Файл конфигурации
├── playbook.yml   # Плейбук запуска роли
└── tests          # Директория с тестами стадии verify
    └── test_default.py

1 directory, 6 files

Хајде да погледамо конфигурацију molecule/default/molecule.yml (заменићемо само доцкер слику):

---
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: flake8

зависност

Овај одељак описује извор зависности.

Могуће опције: галаксија, примењује,шкољка.

Схелл је једноставно командна шкољка коју можете користити ако галаксија и позлата не покривају ваше потребе.

Нећу дуго остати овде, довољно је описано у документација.

возач

Име возача. За нас је ово доцкер.

облога

Иамллинт се користи као линтер.

Корисне опције у овом делу конфигурације су могућност да наведете конфигурациону датотеку за иамллинт, проследите променљиве окружења или онемогућите линтер:

lint:
  name: yamllint
  options:
    config-file: foo/bar
  env:
    FOO: bar
  enabled: False

платформе

Описује конфигурацију инстанци.
У случају докера као драјвера, Молецуле понавља овај одељак, а сваки елемент листе је доступан у Dockerfile.j2 као променљива item.

У случају возача у коме create.yml и destroy.yml, одељак је доступан у њима као molecule_yml.platforms, а итерације на њему су већ описане у овим датотекама.

Пошто Молецуле обезбеђује управљање инстанцом за Ансибле модуле, требало би да тамо потражите листу могућих подешавања. За Доцкер, на пример, користи се модул доцкер_цонтаинер_модуле. Који се модули користе у другим драјверима можете пронаћи у документација.

Такође можете пронаћи примере коришћења различитих драјвера у тестовима самог Молекула.

Заменимо овде центос:7 на Убунту.

снабдевач

„Добављач“ је ентитет који управља инстанцама. У случају Молецуле-а, ово је ансибле; подршка за друге није планирана, тако да се овај одељак може, уз резерву, назвати проширеном ансибле конфигурацијом.
Овде можете да наведете много тога, али ја ћу истаћи главне тачке, по мом мишљењу:

  • плаибоокс: Можете одредити које свеске за игру треба користити у одређеним фазама.

provisioner:
  name: ansible
  playbooks:
    create: create.yml
    destroy: ../default/destroy.yml
    converge: playbook.yml
    side_effect: side_effect.yml
    cleanup: cleanup.yml

provisioner:
  name: ansible
  config_options:
    defaults:
      fact_caching: jsonfile
    ssh_connection:
      scp_if_ssh: True

provisioner:
  name: ansible  
  connection_options:
    ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"

  • Опције: Ансибле параметри и променљиве окружења

provisioner:
  name: ansible  
  options:
    vvv: true
    diff: true
  env:
    FOO: BAR

сценарио

Наслов и опис секвенци сценарија.
Можете променити подразумевану матрицу радњи команде додавањем кључа <command>_sequence и као вредност за то, дефинисање листе корака који су нам потребни.
Рецимо да желимо да променимо редослед радњи када покрећемо команду за покретање плаибоок-а: molecule converge

# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
  name: default
  converge_sequence:
    - create
    - converge

верификатор

Постављање оквира за тестове и линтер за њега. Подразумевано се користи линтер 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

Дуги ауспух испод спојлера:

--> Validating schema <path>/nginx/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in <path>/nginx/...
Lint completed successfully.
--> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml...
Lint completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.
--> Scenario: 'default'
--> Action: 'syntax'

    playbook: <path>/nginx/molecule/default/playbook.yml

--> Scenario: 'default'
--> Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None)

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)
    ok: [localhost]

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Create docker network(s)] ************************************************

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured.
--> Scenario: 'default'
--> Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [instance]

    TASK [nginx : Install nginx] ***************************************************
    changed: [instance]

    TASK [nginx : Start nginx] *****************************************************
    changed: [instance]

    PLAY RECAP *********************************************************************
    instance                   : ok=3    changed=2    unreachable=0    failed=0

--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/...
    ============================= test session starts ==============================
    platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0
    rootdir: <path>/nginx/molecule/default, inifile:
    plugins: testinfra-1.16.0
collected 4 items

    tests/test_default.py ....                                               [100%]

    ========================== 4 passed in 27.23 seconds ===========================
Verifier completed successfully.
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item=None)
    changed: [localhost]

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0

Наша једноставна улога тестирана је без проблема.
Вриједно је запамтити да ако се појаве проблеми током рада molecule test, онда ако нисте променили стандардну секвенцу, Молецуле ће избрисати инстанцу.

Следеће команде су корисне за отклањање грешака:

> molecule --debug <command> # debug info. При обычном запуске Молекула скрывает логи.
> molecule converge          # Оставляет инстанс после прогона тестируемой роли.
> molecule login             # Зайти в созданный инстанс.
> molecule --help            # Полный список команд.

Постојећа улога

Долази до додавања нове скрипте постојећој улози из директоријума улога са следећим командама:

# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>

Ако је ово прва скрипта у улози, онда параметар -s може бити изостављен јер ће се креирати скрипта default.

Закључак

Као што видите, Молецуле није много компликован, а када користите сопствене шаблоне, можете смањити примену нове скрипте на уређивање променљивих у приручницима за креирање и брисање инстанци. Молекул се неприметно интегрише са ЦИ системима, што вам омогућава да повећате брзину развоја смањењем времена за ручно тестирање књига.

Хвала на пажњи. Ако имате искуства са тестирањем ансибле улога, а то није везано за Молецуле, реците нам о томе у коментарима!

Извор: ввв.хабр.цом

Додај коментар