Witam wszystkich!
Pracuję jako inżynier DevOps w usłudze rezerwacji hoteli. . W tym artykule chcę opowiedzieć o naszych doświadczeniach w testowaniu ról Ansible.
W Ostrovok.ru używamy ansible jako menedżera konfiguracji. Ostatnio doszliśmy do konieczności testowania ról, ale jak się okazało, narzędzi do tego nie ma zbyt wiele - najpopularniejszy chyba jest framework Molecule, więc postanowiliśmy go wykorzystać. Okazało się jednak, że jego dokumentacja milczy na temat wielu pułapek. Nie mogliśmy znaleźć wystarczająco szczegółowego przewodnika w języku rosyjskim, dlatego postanowiliśmy napisać ten artykuł.

Cząsteczka
— framework pomagający testować role Ansible.
Uproszczony opis: Molecule tworzy instancję na wskazanej przez Ciebie platformie (chmura, maszyna wirtualna, kontener; więcej szczegółów znajdziesz w dziale ), uruchamia na nim Twoją rolę, następnie uruchamia testy i usuwa instancję. Jeśli na którymś etapie wystąpi awaria, Molecule powiadomi Cię o tym.
Teraz więcej.
Trochę teorii
Rozważmy dwie kluczowe jednostki cząsteczki: scenariusz i sterownik.
Scenariusz
Skrypt zawiera opis co, gdzie, jak i w jakiej kolejności będzie wykonywane. Jedna rola może mieć kilka skryptów, a każdy z nich jest katalogiem na ścieżce <role>/molecule/<scenario>, zawierający opisy działań wymaganych do wykonania testu. Musi być scenariusz default, który zostanie utworzony automatycznie, jeśli zainicjujesz rolę za pomocą Molecule. Nazwy poniższych skryptów zależą od Twojego uznania.
Nazywa się sekwencja działań testowych w skrypcie matrycai domyślnie wygląda to tak:
(Kroki zaznaczone ?, są domyślnie pomijane, jeśli nie zostały określone przez użytkownika)
lint- bieganie lintersów. Domyślnieyamllintиflake8,destroy— usuwanie instancji z ostatniego uruchomienia Molecule (jeśli jakieś pozostały),dependency? — zainstalowanie zależności anible testowanej roli,syntax- sprawdzanie składni roli przy użyciuansible-playbook --syntax-check,create- utworzenie instancji,prepare? — przygotowanie instancji; na przykład sprawdzanie/instalowanie Pythona2converge— uruchomienie testowanego playbooka,idempotence— uruchom ponownie playbook dla testu idempotencji,side_effect? — czynności niezwiązane bezpośrednio z rolą, ale niezbędne do testów,verify— przeprowadzenie testów powstałej konfiguracji przy użyciutestinfra(domyślny) /goss/inspec,cleanup? - (w nowych wersjach) - z grubsza mówiąc, „czyszczenie” infrastruktury zewnętrznej dotkniętej Molekułą,destroy— usunięcie instancji.
Ta sekwencja obejmuje większość przypadków, ale w razie potrzeby można ją zmodyfikować.
Każdy z powyższych kroków można uruchomić osobno za pomocą molecule <command>. Ale powinieneś zrozumieć, że dla każdego takiego polecenia cli może istnieć osobna sekwencja działań, którą możesz sprawdzić, uruchamiając molecule matrix <command>. Na przykład podczas uruchamiania polecenia converge (uruchamiając testowany podręcznik) zostaną wykonane następujące czynności:
$ molecule matrix converge
...
└── default # название сценария
├── dependency # установка зависимостей
├── create # создание инстанса
├── prepare # преднастройка инстанса
└── converge # прогон плейбукаKolejność tych działań można edytować. Jeśli coś z listy zostało już ukończone, zostanie pominięte. Bieżący stan oraz konfiguracja instancji są przechowywane w katalogu Molecule $TMPDIR/molecule/<role>/<scenario>.
Dodaj kroki za pomocą ? Możesz opisać żądane działania w formacie podręcznika Ansible i nadać nazwę plikowi zgodnie z krokiem: prepare.yml/side_effect.yml. Oczekuj, że te pliki Molecule będą znajdować się w folderze skryptów.
Kierowca
Sterownik to obiekt, w którym tworzone są instancje do testów.
Lista standardowych sterowników dla których Molecule posiada gotowe szablony to: Azure, Docker, EC2, GCE, LXC, LXD, OpenStack, Vagrant, Delegated.
W większości przypadków szablony są plikami create.yml и destroy.yml w folderze skryptów, które opisują odpowiednio utworzenie i usunięcie instancji.
Wyjątkami są Docker i Vagrant, ponieważ interakcje z ich modułami mogą odbywać się bez powyższych plików.
Warto wyróżnić sterownik Delegowany, gdyż w przypadku jego wykorzystania w plikach tworzenia i usuwania instancji opisana jest jedynie praca z konfiguracją instancji, resztę powinien opisać inżynier.
Domyślnym sterownikiem jest Docker.
Przejdźmy teraz do praktyki i rozważmy tam dalsze funkcje.
Pierwsze kroki
W ramach „witaj świecie” przetestujemy prostą rolę instalacyjną Nginx. Wybierzmy dokera jako sterownik - myślę, że większość z Was go ma zainstalowaną (i pamiętajcie, że doker jest sterownikiem domyślnym).
Przygotujmy się virtualenv i zainstaluj w nim molecule:
> pip install virtualenv
> virtualenv -p `which python2` venv
> source venv/bin/activate
> pip install molecule docker # molecule установит ansible как зависимость; docker для драйвераNastępnym krokiem jest zainicjowanie nowej roli.
Inicjalizacja nowej roli, a także nowego skryptu odbywa się za pomocą polecenia 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 fileRezultatem jest typowa rola ansible. Co więcej, wszystkie interakcje z interfejsem CLI Molecules są wykonywane z poziomu głównego roli.
Zobaczmy, co znajduje się w katalogu ról:
> tree molecule/default/
molecule/default/
├── Dockerfile.j2 # Jinja-шаблон для Dockerfile
├── INSTALL.rst. # Немного информации об установке зависимостей сценария
├── molecule.yml # Файл конфигурации
├── playbook.yml # Плейбук запуска роли
└── tests # Директория с тестами стадии verify
└── test_default.py
1 directory, 6 filesSpójrzmy na konfigurację molecule/default/molecule.yml (zastąpimy tylko obraz okna dokowanego):
---
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: flake8zależność
W tej sekcji opisano źródło zależności.
Możliwe opcje: , , powłoka.
Shell to po prostu powłoka poleceń, której można używać, jeśli galaktyka i pozłacanie nie zaspokajają twoich potrzeb.
Nie zabawię tu długo, jest to wystarczająco opisane w .
kierowca
Imię kierowcy. Dla nas jest to doker.
szarpie
Yamllint jest używany jako linter.
Przydatnymi opcjami w tej części konfiguracji jest możliwość określenia pliku konfiguracyjnego dla yamllint, przekazywania zmiennych środowiskowych lub wyłączania lintera:
lint:
name: yamllint
options:
config-file: foo/bar
env:
FOO: bar
enabled: FalsePlatformy
Opisuje konfigurację instancji.
W przypadku okna dokowanego jako sterownika Molecule wykonuje iterację po tej sekcji, a każdy element listy jest dostępny w Dockerfile.j2 jako zmienna item.
W przypadku kierowcy w którym create.yml и destroy.yml, sekcja jest w nich dostępna jako molecule_yml.platforms, a jego iteracje są już opisane w tych plikach.
Ponieważ Molecule zapewnia zarządzanie instancjami modułów Ansible, powinieneś poszukać tam listy możliwych ustawień. Na przykład w przypadku Dockera używany jest moduł . Jakie moduły są używane w innych sterownikach, można dowiedzieć się w .
Można też znaleźć przykłady użycia różnych sterowników .
Zamieńmy tutaj centos:7 na ubuntu.
zaopatrzeniowiec
„Dostawca” to podmiot zarządzający instancjami. W przypadku Molecule jest to ansible, nie jest planowana obsługa innych, więc tę sekcję można z zastrzeżeniem nazwać rozszerzoną konfiguracją ansibla.
Można tu wiele wymienić, ale moim zdaniem skupię się na najważniejszych kwestiach:
- playbooks: Możesz określić, które podręczniki mają być używane na określonych etapach.
provisioner:
name: ansible
playbooks:
create: create.yml
destroy: ../default/destroy.yml
converge: playbook.yml
side_effect: side_effect.yml
cleanup: cleanup.yml- opcje_konfiguracji:
provisioner:
name: ansible
config_options:
defaults:
fact_caching: jsonfile
ssh_connection:
scp_if_ssh: True- opcje_połączenia: opcje
provisioner:
name: ansible
connection_options:
ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"- Opcje: Parametry Ansible i zmienne środowiskowe
provisioner:
name: ansible
options:
vvv: true
diff: true
env:
FOO: BARscenariusz
Tytuł i opis sekwencji skryptów.
Możesz zmienić domyślną macierz akcji polecenia, dodając klucz <command>_sequence i jako wartość, określając listę kroków, których potrzebujemy.
Załóżmy, że chcemy zmienić kolejność działań podczas uruchamiania polecenia uruchamiania podręcznika: molecule converge
# изначально:
# - dependency
# - create
# - prepare
# - converge
scenario:
name: default
converge_sequence:
- create
- convergeweryfikator
Stworzenie frameworka do testów i lintera do niego. Domyślnie używany jest linter testinfra и flake8. Możliwe opcje są podobne do powyższych:
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: barWróćmy do naszej roli. Edytujmy plik tasks/main.yml do tego wyglądu:
---
- name: Install nginx
apt:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
I dodaj testy do 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")
Gotowe, pozostaje tylko uruchomić (od źródła roli, przypomnę):
> molecule testDługi wydech pod spojlerem:
--> 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
Nasza prosta rola została przetestowana bez problemów.
Warto o tym pamiętać, jeśli podczas pracy pojawią się problemy molecule test, to jeśli nie zmieniłeś standardowej sekwencji, Molecule usunie instancję.
Do debugowania przydatne są następujące polecenia:
> molecule --debug <command> # debug info. При обычном запуске Молекула скрывает логи.
> molecule converge # Оставляет инстанс после прогона тестируемой роли.
> molecule login # Зайти в созданный инстанс.
> molecule --help # Полный список команд.Istniejąca rola
Następuje dodanie nowego skryptu do istniejącej roli z katalogu ról za pomocą następujących poleceń:
# полный список доступных параметров
> molecule init scenarion --help
# создание нового сценария
> molecule init scenario -r <role_name> -s <scenario_name>Jeżeli jest to pierwszy skrypt w roli to parametr -s można pominąć, ponieważ zostanie utworzony skrypt default.
wniosek
Jak widać Molecule nie jest bardzo skomplikowane, a korzystając z własnych szablonów, można zredukować wdrażanie nowego skryptu do edycji zmiennych w playbookach w celu tworzenia i usuwania instancji. Cząsteczka płynnie integruje się z systemami CI, co pozwala zwiększyć szybkość rozwoju poprzez skrócenie czasu ręcznego testowania playbooków.
Dziękuję za uwagę. Jeśli masz doświadczenie w testowaniu ról ansible i nie jest ono związane z Molecule, powiedz nam o tym w komentarzach!
Źródło: www.habr.com
