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

February 16 2023
Почему именно Lua
Установка и зависимости
«Здравствуй, подлунный мир!»
Vagrant метаданные
Настройка Nginx
Вычисление метаданных с помощью Lua

Для удобного процесса разработки, быстрого переключения между проектами и эффективного взаимодействия бекэнд и фронтенд команд мы в WB—Tech работаем в виртуальном окружении Vagrant и VirtualBox.

Vagrant — кросс-платформенное ПО для создания виртуальной среды разработки. Для ускорения развертывания виртуальной машины можно использовать компилированные, версированные боксы. Версийность боксов в Vagrant описывается при помощи JSON документа.

{
    "name": "box_name",
    "description": "This box description.",
    "versions": [{
        "version": "42.0",
        "providers": [{
            "name": "virtualbox",
            "url": "http://somewhere.com/precise64_010_virtualbox.box",
            "checksum_type": "sha1",
            "checksum": "foo"
        }]
    }]
}

В самом Vagrantfile указать путь к метаданным в атрибуте config.vm.box_url.

config.vm.box = "box_name"
config.vm.box_version = "42.0"
config.vm.box_url = "http://somewhere.com/path/to/metadata.json"

Лайфхак: при обновлении версии бокса мы используем Nginx с дополнительным модулем, потому что описывать документ каждый раз вручную не практично. Формирование метаданных сделано при помощи простого скрипта на Lua. Мы хостим боксы самостоятельно с помощью Lua.

Почему именно Lua

Lua — скриптовый язык программирования. По возможностям, идеологии и реализации язык ближе всего к JavaScript, однако отличается более мощными и гибкими конструкциями.

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

Реализуемая модель ООП — прототипная (как и в JavaScript). Интерпретатор языка — свободно распространяемый с открытыми исходными текстами на языке C.

Установка и зависимости

Описание приведено для операционных систем семейства Debian.

  • Прежде всего нужен Nginx с модулем lua-nginx-module. Установите готовый пакет nginx-extras, либо соберите вручную.
  • Потребуется интерпретатор Lua, чтобы протестировать скрипт в интерактивной консоли.
  • Для компиляции модулей потребуется утилита make.
  • Менеджер пакетов luarocks: для поиска файлов в директории используем модуль luaposix, для конвертации словаря в JSON — JSON4Lua.
$ sudo apt-get -y install make nginx-extras lua5.1 luarocks$ # install lua modules$ sudo luarocks install luaposix$ sudo luarocks install JSON4Lua

«Здравствуй, подлунный мир!»

Фраза “Hello world!” на Lua так же проста, как и на Python.

Lua 5.1 .5 Copyright(C) 1994 - 2012 Lua.org, PUC - Rio > print "Hello world!"
Hello world!

Теперь попробуем тоже самое при помощи полнофункционального Nginx Lua API.

server {
    listen 80;
    location / hello - world {
        content_by_lua '            ngx.header.content_type = "text/plain"            ngx.say("Hello world!")        ';
    }
}
$ curl http://10.1.1.111/hello-worldHello world!

Для исполнения скрипта служит директива content_by_lua, для которого Nginx получает ответ через API. Если скрипт большой, не обязательно описывать его внутри конфигурации, можно подключить через директиву content_by_lua_file.

Vagrant метаданные

Настройка Nginx

На сервере мы складываем боксы в директорию hosted, создавая поддиректорию для каждого проекта. Сами боксы со строго указанным форматом имени {provider}-{version.subversion}.box.

Формируется такое дерево.

$ tree hosted/hosted/├── foo│   ├── docker-1.0.box│   ├── docker-1.3.box│   ├── virtualbox-1.0.box│   ├── virtualbox-1.4.box│   └── virtualbox-1.7.box└── bar    ├── virtualbox-1.0.box    ├── virtualbox-1.1.box    └── virtualbox-1.2.box2 directories, 8 files

Настроим Nginx так, чтобы для любого бокса начиналось скачивание, а для имени проекта возвращались вычисленные метаданные.

server {
    listen 80;
    set $box_url 'http://10.1.1.111/%s/%s-%s.box';
    set $box_prefix '/home/vagrant/proj/hosted/';
    location~ /*\.box$ {        root /home/vagrant/proj/hosted;        # just return box    }    location ~ /(?<box_name>\w+)/?$ {        content_by_lua_file /home/vagrant/proj/app/handler.lua;    }}

Переменные $box_url и $box_prefix будут использоваться при формировании метаданных.

Вычисление метаданных с помощью Lua

Теперь сформируем метаданные для версирования Vagrant боксов. Идея в том, что по запросу Lua будет осуществлять поиск сохраненных боксов в заданной директории на сервере, вычислять их хеш-суммы и создавать ответ в формате метаданных Vagrant.

Используя glob из библиотеки posix, найдем все боксы.

local box_root = ngx.var.box_prefix..ngx.var.box_name..
'/'
local posix = require "posix"
local glob = posix.glob(box_root..
    '*.box') --Если боксы не найдены, можно сразу возвращать 404
if not glob then ngx.status = ngx.HTTP_NOT_FOUND
return ngx.exit(ngx.HTTP_NOT_FOUND) end

Итерациями пройдем по найденным боксам и сформируем словарь с найденными версиями.

local versions = {}--Discover the boxesfor _, box in ipairs(glob) do --Обрабатываем найденый бокс, определяя версию и формируя описание local provider, version = make_provider(box) if version then
if versions[version] == nil then--Если версия встречается впервые, создаем запись для новой версии versions[version] = {
    version = version,
    providers = {
        provider
    }
}
else --Если версия уже была описана, обновляем список провайдеров table.insert(versions[version]['providers'], provider) end endend

Для вычисления хеш-суммы больших файлов боксов используем утилиты
OC — sha1sum, sha256sum, md5sum с помощью вызова процесса через io.popen.

local hash = 'sha1'
function get_hash(filepath) --Вычисляем хешсумму используя вызов консольной утилиты sha1sum local command = string.format('%ssum %s | cut -d " " -f1', hash, filepath) local hashsum = assert(io.popen(command, 'r')) local result = string.gsub(hashsum: read('*a'), '\n', '') hashsum: close() return resultend

Функция make_provider выполняется для каждого найденного бокса. Подразумевается, что боксы хранятся на сервере со строго заданным форматом имени: {provider}-{version.subversion}.box

Разбираем версию и имя провайдера, после чего формируем словарь, описывающий данный бокс.

local
function make_provider(filepath) --Make vagrant provider from given file local box_provider, box_version = string.match(filepath, string.format('%s(%%a+)-(.+).box', box_root)) return {
    --Название провайдера virtualbox или docker name = box_provider,
    --Прямая ссылка на бокс,
    которую будет запрашивать vagrant url = string.format(ngx.var.box_url, ngx.var.box_name, box_provider, box_version),
    --Алгоритм хешсуммы sha1,
    sha256,
    md5 checksum_type = hash,
    --Строка со значением хешсуммы checksum = get_hash(filepath)
}, box_versionend

Обработав все боксы и сформировав список версий, обернем все в дополнительный словарь.

--Make result responselocal vagrant = {
    name = ngx.var.box_name,
    description = string.format("Boxes for %s proj", ngx.var.box_name),
    versions = {}
}
for _, version in pairs(versions) do table.insert(vagrant['versions'], version) end

Ответ сервера JSON с найденными версиями.

ngx.header.content_type = "application/json; charset=utf-8"
local json = require "json"
ngx.say(json.encode(vagrant))

Полученный скрипт формирует JSON ответ c метаинформацией о боксах.

$ curl http: //10.1.1.111/example | jq  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current   
{
    "versions": [{
        "version": "1.7",
        "providers": [{
            "url": "http://10.1.1.111/example/virtualbox-1.7.box",
            "checksum": "3221c0fd58a4b2430efc5eeaf09cb8eaf877f3a9",
            "name": "virtualbox",
            "checksum_type": "sha1"
        }]
    }, {
        "version": "1.3",
        "providers": [{
            "url": "http://10.1.1.111/example/docker-1.3.box",
            "checksum": "def7148aa7ded879dbf5944af4785c2b09aba97a",
            "name": "docker",
            "checksum_type": "sha1"
        }]
    }, {
        "version": "1.4",
        "providers": [{
            "url": "http://10.1.1.111/example/virtualbox-1.4.box",
            "checksum": "63b06d8c065f5c2522c356d4d6ceb718ec3f8198",
            "name": "virtualbox",
            "checksum_type": "sha1"
        }]
    }, {
        "version": "1.0",
        "providers": [{
            "url": "http://10.1.1.111/example/docker-1.0.box",
            "checksum": "65cb550765d251604dcfeedc36ea61f66ce205c4",
            "name": "docker",
            "checksum_type": "sha1"
        }, {
            "url": "http://10.1.1.111/example/virtualbox-1.0.box",
            "checksum": "c0a9d5c3d6679cfcc4b1374e3ad42465f3dd596e",
            "name": "virtualbox",
            "checksum_type": "sha1"
        }]
    }],
    "name": "example",
    "description": "Boxes for example proj"
}

Полный пример скрипта можно посмотреть в репозитории на Github. И при желании поиграться, запустив настроенный Vagrant.

Если хотите убедиться, что все делаете правильно или проконсультироваться по поводу разработки вашего проекта, напишите нам.

Автор статьи
Кирилл Гришанин
Последние 10 лет руковожу командой аналитиков, дизайнеров и разработчиков

Подпишитесь на блог WB—Tech

Никакого спама, только анонсы новых статей

    Последние статьи

    Миграция внутренних пользователей Jira в новую директорию с сохранением данных об активности

    Рассказали, как осуществили перенос пользовательских данных из Jira (Internal Directory) в директорию Microsoft Active Directory.

    Как эффективно хранить и актуализировать корпоративные данные средствами low/no-code

    Рассказали, как организовали поток HR-данных, чтобы оргструктура и бонусно-бухгалтерские расчеты всегда были актуальны.

    Мало кода, больше результативности: платформы low-code и no-code

    О low-code и no-code платформах, примерах использования и разбор нужно ли быть программистом.

    ИП Гришанин Кирилл Олегович
    ИНН 774313842609

    Коворкинг Starthub

    Б. Новодмитровская ул., 36, стр. 12, вход 6,
    Москва, Россия, 127015

    Коворкинг Wework

    Ahad Ha'am 54,Tel Aviv-Yafo,Израиль

    © 2023 WB—Tech. Мы разрабатываем уникальные решения для компаний из России, США и Европы.