Разработка Sparrow плагинов

Приветствую!

В предыдущей статье я писал о SparrowHub — репозиторий готовых утилит для системного администрирования. Что же, время прошло, и теперь хочется рассказать о том КАК разрабатывать эти самые утилиты и загружать их на SparrowHub для повторного использования кем-либо.

Прежде чем показать конкретный пример написания плагина, хочу немного раскрыть идеологию фреймворка Sparrow, на котором собственно и разрабатываются скрипты.

Во-первых, Sparrow не навязывает какого-то жесткого DSL для разработки скриптов. У вас есть на выбор три языка, поддерживаемых Sparrow:


  • Perl
  • Ruby
  • Bash

Таким образом, вы просто пишите обычный скрипт, который делает нужную вам работу и «оборачиваете» его в плагин, и, вуаля — он готов к загрузке на SparrowHub и для повторного использования. Ну, я немножко упростил, не совсем конечно так. Разрабатывая скрипт, вы все же придерживаетесь некоторых соглашений (например на именования скриптовых файлов и т.д.) для того что бы ваша утилита могла быть упакована в плагин в «sparrow» формате.

Другой особенностью Sparrow плагинов является встроенная система тестирования работы скриптов. Встроенная, потому что из коробки у вас есть возможность совершить дополнительные проверки работы скриптов, кроме банального кода завершения. Данная система проверок в ряде случаев может оказаться очень полезной. Детали станут ясны на конкретном примере.

Ну и третья, связанная с предыдущей, особенность Sparrow плагинов, это то, что результат их работы выводится в TAP формате. В двух словах TAP — специальный протокол (плюс формат вывода), разработанный для тестирования программных модулей, он является стандартным для написания unit тестов в Perl, но не привязан к конкретному языку программирования и имеет поддержку во многих системах и языках.

Таким образом, в Sparrow предпринята попытка (насколько удачная покажет практика ;)) совместить написание скриптов системного администрирования с системой тестирования работы самих скриптов. Похожие идеи можно увидеть в различных системах управления конфигурациями, например в chef — это minitest chef handler и новая разработка chef inspec.

Итак, напишем простейший Sparrow плагин.

Допустим, требуется установить пакет nginx и настроить запускаемый nginx сервер. Разобьем задачу на два сценария — собственно установка самого пакета и настройка сервера. Под настройкой будем понимать добавление сервера в автозагрузку, старт сервера и простейший тест на то, что сервер доступен по порту 80. Операционная система — Ubuntu. Сразу же оговорюсь, что пример достаточно умозрительный и тривиальный, любой системный администратор или программист способен выполнить подобную задачу, но хорошо подходит для объяснения того как пишутся плагины Sparrow.

Итак, у нас есть два сценария:


  • установка пакета
  • настойка сервера


Сценарий установки

В качестве языка, на котором будем писать плагин, выберем Bash т.к. в данной постановке задачи (очень простые действия на уровне настройки операционные системы) он больше всего подходит к нашей предметной области. В принципе, все тоже самое можно реализовать на Perl или Ruby.

В терминах Sparrow запускаемые сценарий — это история, в том смысле что есть некий скрипт, который можно запустить и который выводит что-то в stdout (оставляет след, образно выражаясь). После выполнения скрипта Sparrow позволяет сделать два типа проверок:


  • проверить, что код завершения успешный (== 0)
  • проверить, что в текстовом выводе скрипта содержатся определенные данные (проверка на соответствие строкам и регулярным выражениям)

Правила на именование скрипта простое — базовое имя должно быть story с соответствующим языку сценария расширением:


  • Bash — story.bash
  • Perl — story.pl
  • Ruby — story.rb

Итак, в случае с Bash имеем такую историю:

$ cat story.bash

sudo DEBIAN_FRONTEND=noninteractive apt-get -y install nginx >/dev/null
dpkg -s nginx | grep Status:

Запустив, данный скрипт руками посмотрим его вывод:

$ bash story.bash 
Status: install ok installed

Теперь несложно написать проверку для корректности работы данного скрипта. В Sparrow это делается с помощью так называемых проверочных файлов. У каждой истории должен быть свой проверочный файл, он должен лежать в той же директории что и запускаемый сценарий и называться story.check. Содержимое проверочного файла должно быть создано на языке проверочных правил Outthentic: DSL, который был разработан специально для анализа текстового вывода произвольных программ. DSL предлагает большой набор возможностей, из которых на практике может потребоваться только проверка на построчное включение заданных строк и соответствие регулярному выражению. Итак, story.check будет таким:

$ cat story.check

install ok installed

Отлично, теперь можно запустить нашу истории посредством Sparrow. Это будет прототипом нашего плагина. Для этого установим из CPAN модуль Sparrow, который предоставляет средства разработки и запуска Sparrow плагинов.

$ cpanm Sparrow

Отлично, перейдя в директорию, где лежит наш сценарий запустим его посредством утилиты strun — это клиент для запуска сценариев в формате Sparrow:

 $ strun 

/tmp/.outthentic/30382/home/melezhik/projects/nginx-example/story.t .. 
# Status: install ok installed
ok 1 - output match 'Status: install ok installed'
1..1
ok
All tests successful.
Files=1, Tests=1,  1 wallclock secs ( 0.01 usr  0.00 sys +  0.51 cusr  0.03 csys =  0.55 CPU)
Result: PASS

Как уже говорилось, мы получили результат работы нашего сценария в виде TAP отчета, по которому видно что скрипт отработал корректно. Что здесь произошло? strun запустил сценарий и проверил что:


  • код завершения успешный
  • сценарий вывел в stdout строчку 'Status: install ok installed'

Далее, аналогичным образом создадим сценарий настройки nginx сервера вместе с проверочный файлом. Так как каждая история должна находится в своей директории создадим для сценария настройки отдельную директорию:

$ mkdir nginx-setup
$ cd nginx-setup 

Сценарий настройки будет очень простым:

$ cat nginx-setup/story.bash

 sudo update-rc.d nginx defaults
 sudo service nginx start    
 sudo service nginx status
 curl -sf -o /dev/null 127.0.0.1

Для начала можно просто запустить сценарий руками и посмотреть вывод

System start/stop links for /etc/init.d/nginx already exist.
* nginx is running

Теперь можно создать проверочный файл:

$ cat nginx-setup/story.bash
nginx is running

Отлично, снова запустим наша сценарии уже через stun клиент:

$ strun

/tmp/.outthentic/32332/home/melezhik/projects/nginx-example/nginx-setup/story.t .. 
#  System start/stop links for /etc/init.d/nginx already exist.
#  * nginx is running
ok 1 - output match 'nginx is running'
1..1
ok
/tmp/.outthentic/32332/home/melezhik/projects/nginx-example/story.t .............. 
# Status: install ok installed
ok 1 - output match 'Status: install ok installed'
1..1
ok
All tests successful.
Files=2, Tests=2,  1 wallclock secs ( 0.01 usr  0.00 sys +  0.58 cusr  0.08 csys =  0.67 CPU)
Result: PASS

Убеждаемся, что мы достигли заданной цели. Nginx установлен, запущен, добавлен в автозагрузку и доступен по 80 порту.

Внимательный читатель увидит однако одну особенность в написанном нами наборе скриптов, а именно — сценарий установки nginx запускается после сценария настройки. Все дело в том, что по умолчанию все истории запускаемые клиентом strun считаются независимыми и никак не связанными, порядок их запуска, вообще говоря, не гарантирован и даже больше — разные истории могут быть запущены в параллельном режиме (читайте документацию по параметрам утилиты prove, которой strun диспетчеризирует запуск тестов).

Очевидно, что при таком поведении, запуск плагина в первый раз, когда пакет nginx еще не установлен в системе, даст нам ошибку.
Что же нам делать? Как обеспечить запуск сценариев в заданной последовательности. Нам нужно сначала установить пакет nginx, а затем уже произвести настройку сервера. Для этого в Sparrow существует понятие модулей или второстепенных (downstream) историй.


Основные и второстепенные сценарии

Второстепенные истории — это сценарии, которые вы можете вызвать ПЕРЕД выполнением какого-то основного сценария (основная (upstream) истории). В нашем случае — основной сценарий будет сценарий настройки, а вторичным — сценарием сценарий установки пакета. Вторичные сценарии никогда не запускаются клиентом strun напрямую, вы должны сделать их вызов явно в основном сценарии. Вот как это делается:

Что бы сценарий стал вторичным необходимо поместить соответствующую историю в директорию под названием ./modules, таким образом strun поймет, что данная история второстепенная:

$ mkdir -p modules/nginx-install
$ mv story.bash story.check modules/nginx-install

Для вызова второстепенной истории воспользуемся так называемый hook файлом — основным механизмом в Sparrow, позволяющим расширять логику работы strun клиента и добавлять вызовы стороннего кода перед выполнением основного сценария:

$ cat nginx-setup/hook.bash

run_story nginx-install

Не вдаваясь здесь в детали Hook API скажу лишь, что в данном примере в хук файле мы вызываем второстепенную историю nginx-install с помощью функции run_story. Обратите внимание, что аргумент функции — путь до директории, в которой находится второстепенная история, из которого исключается фрагмент modules.

Итак запустим переработанный код:

$  strun

/tmp/.outthentic/367/home/melezhik/projects/nginx-example/nginx-setup/story.t .. 
# Status: install ok installed
ok 1 - output match 'Status: install ok installed'
#  System start/stop links for /etc/init.d/nginx already exist.
#  * nginx is running
ok 2 - output match 'nginx is running'
1..2
ok
All tests successful.
Files=1, Tests=2,  1 wallclock secs ( 0.01 usr  0.00 sys +  0.54 cusr  0.06 csys =  0.61 CPU)
Result: PASS

Отлично, судя по выводу убеждаемся, что сценарий установки запускается перед сценарием настройки, чего и хотелось.

Подробнее про основные и второстепенные истории читайте в документации по клиенту strun.


Загрузка плагина в центральный репозиторий SparrowHub

Протестировав работу сценариев и убедившись, что все работает как надо, мы готовы поделится нашим скриптом с остальным миром и загрузить его в SparrowHub — центральный репозитарий. Для этого нужно сделать два простых шага:


  • зарегистрироваться на сайте https://sparrowhub.org
  • залогиниться и получить токен разработчика плагинов

После этого, вы сможете загружать плагины под своим авторством.

В корне нашего проекта, где лежат истории создадим файл с метаданными плагина:

$ cat sparrow.json
{
   "name" : "nginx-example",
   "version" : "0.1.0",
   "description" : "simple nginx server installer for Ubuntu",
   "url" : "https://github.com/melezhik/nginx-example.git"
}

Формат файла подробно описан здесь, не будем подробно останавливаться на нем. Важно лишь сказать, что Sparrow поддерживает версионирование плагинов, позволяя загружать и устанавливать разные версии одного плагина.

Из в директории со скриптами запустим команду sparrow менеджера, указав свои идентификаторы на SparrowHub

$ export sph_user=melezhik 
$ export sph_token=FOO-BAR-BAZ-FOO-BAR-BAZ
$ sparrow plg upload

sparrow.json file validated ... 
plugin nginx-example version 0.001000 upload OK

Все отлично. Плагин загружен и находится в SparrowHub репозитарии. Мы можем зайти на другой сервер и воспользоваться данным плагином:

$ ssh some-other-host
$ sparrow index update
$ sparrow plg install nginx-example
$ sparrow plg run nginx-example


Заключение

Нет возможности описать в данной статье все возможности и тонкости системы Sparrow и среды разработки Outthentic. Приведу тезисно, что еще можно было бы раскрыть. (Если у читателя будет интерес об этом можно будет в следующих статьях).


  • Outthenitc API для Perl и Ruby
  • Параметры для второстепенных историй
  • Outthentic: DSL — валидатор обычного текста (генераторы проверочных правил, асерты, кепчи, текстовые блоки и т.д.)
  • Конфигурация Sparrow плагинов (Runtime, YAML, Config: General)

PS Надеюсь на конструктивную критику :) и конечно же на привлечение новых разработчиков плагинов в проект SparrowHub.

Спасибо!

© Habrahabr.ru