INT 21h

Hi, I am Vladimir Smagin, SysAdmin and Kaptain. Telegram Email / GIT / RSS / GPG

SQLite в Bash

№ 7583 В разделе "Sysadmin" от September 9th, 2015,
В подшивках: , ,

Часто приходится делать на баше различные велосипеды с семафорами, флажками и т.д. для контроля запуска cron задач, например. У всех чешутся написать какой-нибудь велосипед с хранением флажков в виде файлов. Нахрен это все. Все написано до нас. Многие сисадмины и даже программисты прекрасно наслышаны об SQLite, но не все знают, что использовать ее под консолью даже проще, чем под каким-нибудь питоном, т.к. имеет готовый податливый для парсинга текстовый интерфейс. SQLite настолько простая, быстрая и маленькая, что применяется даже в embedded системах, браузерах, сотовых телефонах и других местах, где скорость работы и размер СУБД имеет решающее значение.

Установка sudo apt-get install sqlite3

Теперь можно ставить задачи с обменом данными. Например, есть сервер разработки и сервер развертывания. На сервере разработки осуществляется сборка продукта, а сервер развертывания должен забрать полученый релиз и сгенерировать репозиторий. Директория собраного продукта по NFS расшарена с сервером развертывания и никакой другой связи между серверами не предусмотрено. Т.е. никакого JSON между серверами кидать не получится. Выход только файловая база данных со всеми релизами и отметками о сборке репозитория.

Итак, мы имеем таблицу в которую поместим поля (INTEGER)”ревизия”, (TEXT)”время”, (INTEGER)”состояние развертывания” и (INTEGER)”администратор уведомлен”. Выполняется естественно разово при создании файла БД. Уведомление администратора происходит единожды при ошибке репозитория.

sqlite3 /product/dir/releases.db "create table releases (revision INTEGER PRIMARY KEY, build_timestamp TEXT, dist_state INTEGER DEFAULT 0, notified INTEGER DEFAULT 0);"

Тут необходимо принять следующие значения dist_state:

  • 0 – не развернуто
  • 1 – разворачивается
  • 2 – ошибка развертывания
  • 3 – развернуто и раздается

По окончанию сборки продукта добавляем запись о новом билде в БД. Команды добавляются в скрипт post-build чтобы они выполнились после того как файлы станет можно использовать:

timestamp=$(date +'%Y-%m-%d %H:%M:%S')
sqlite3 /product/dir/releases.db "insert into releases (build_timestamp) values ('$timestamp');"

Сборка добавлена в базу данных с пометкой о том, что она еще не развернута (dist_state=0 не указан, т.к. он и так по умолчанию 0). База данных лежит где-нибудь внутри расшареной директории и удаленный сервер каждые 15 минут запускает проверяющий базу данных скрипт, который запустит сборку, если релиз обнаружен.

Теперь рассмотрим скрипт, который будет проверять эту базу данных каждые 15 минут, изменять ее и запускать сборку репозитория. Первое, что нужно сделать это спросить базу данных не наличиствует ли у нее запись у которой dist_state=0:

#!/bin/bash
is_release=$(sqlite3 /product/dir/releases.db  "select build_timestamp from releases where dist_state=0 order by revision asc limit 1";)

if [ -z "$is_release" ] ; 
then
    echo "No release to distribute"
else
    echo "Lets do some serious work! New release build $is_release"
    /opt/repository/build.sh
fi

Этот запрос вернет временную метку только что выложеного релиза с учетом порядка. Это значит, что если за 15 минут было выложено 2 релиза, то сначала будет обработан тот, что выложен раньше. limit 1 нужен чтобы обход базы был только до первого элемента (это полезно для производительности). Если новых релизов найдено не было, то будет просто пустая переменная.

Но этого мало. По ходу выполнения сборки необходимо менять статусы, иначе через 15 минут проверяющий скрипт запустится и заново выполнит сборку этого же самого релиза. Мало того, нужно еще проверить не выполняется ли уже сборка чтобы не стало еще хуже. Самое страшное это наличие ошибки сборки репозитория из-за которой все встает колом и администратору посылается уведомление.

#!/bin/bash
db_file="/product/dir/releases.db"
is_release=$(sqlite3 $db_file "select revision from releases where dist_state=0 order by revision asc limit 1";)
is_job=$(sqlite3 $db_file  "select revision from releases where dist_state=1 order by revision asc limit 1";)
is_error=$(sqlite3 $db_file  "select revision from releases where dist_state=2 order by revision asc limit 1";)

#in case of tragedy
if [ -n "$is_error" ] ; then
    #чтобы не выебать мозг админу сотней писем нужно проверить уведомлялся ли он уже об ошибке
    is_notified=$(sqlite3 $db_file "select revision from releases where notified=0 and revision=$is_error asc limit 1";)
    if [ -n "$is_notified" ] ; then 
        mail -s "Tragedy on release server is present" admin@roga-and-co.net
        #поднять флаг что администратор уведомлен
        sqlite3 $db_file "update releases set notified=1 where revision=$is_notified;"
    fi
#преждевременно завершаем скрипт
exit 1; fi

#если никаких ошибок в базе нет, то продолжаем подготовку к сборке. проверим
#наличие новых сборок и уже выполняющихся задач
if [ -z "$is_release" ] ; 
then
    echo "No release to distribute"
else
    if [ -z "$is_job" ] ; then
        echo "Sorry. Job already running. Next time, thug."
        exit 1
    else
        echo "Lets do some serious work! New revision build: $is_release"
        result_code=$(/opt/repository/build.sh)
        #тут следует оговориться, что ваш скрипт сборки должен вернуть 2 в случае ошибки или 3 если успешно
        sqlite3 $db_file "update releases set dist_state=$result_code where revision=$is_release;"
    fi
fi

Как вы видите все просто до опупения. Хотя конечно такое извраты в архитектуре лучше изначально не допускать и использовать нормальные системы релизов 🙂

Нет комментариев »

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Яндекс.Метрика

Fortune cookie: Today's spam: you looked really hot