Создание CloudFoundry/IBM Bluemix buildpack или веб сервис на Awk (gawk)

gawk — глазеть разинув рот, таращить глаза

d471bad737c54eb1b36484964272f13a.jpg

Дальше никаких шуток, я действительно опишу как запустить Awk (Gawk) веб сервис на IBM Bluemix.

CloudFoundry и созданная на его основе платформа IBM Bluemix поддерживают много разных языков программирования и веб фреймворков. Все это благодаря поддержке билдпаков (мой перевод слова buildpack). Билдпак можно рассматривать как плагин, который отвечает за установку и настройку среды выполнения приложения. Создают билдпак обычно в двух случаях.

Бывает, что язык или фреймворк поддерживается, но для приложения нужны сторонние библиотеки, например OpenCV, которые невозможно поставить стандатрным для этого языка менеджером зависимостей (npm, pip, bundler). В этом случае модифицируется существующий билдпак, в который добавляется установка нужных библиотек. Большинство билдпаков лежат на github, делается форк, с помощью которого можно будет деплоить приложение.

Второй случай более редкий — язык пока не поддерживается, т.е. билдпак пока никто не создал. В этом случае придется сделать его с нуля, что и будет описано чуть ниже. Я долго искал какой язык пока не поддерживается CloudFoundry, выбор пал на Brainfuck. К сожалению, когда я дописал билдпак, я понял, что быстро написать веб сервер на этом языке у меня не получится. Поиск показал, что и у других с этой задачей возникли сложности.

Чтобы было чуть понятнее в чем проблема — билдпак настраивает среду, в которую затем помещается пользовательское приложение, ему через переменные окружения передаются порт и имя хоста, где приложение должно начать обрабатывать HTTP запросы. Т.е. простого hello world мало. Я продолжил поиски и нашел реализацию веб серверов на PostScript и Awk (точнее gawk). В итоге я выбрал Awk и начал писать новый билдпак.

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

detect — определяет можно ли применять данный билдпак к приложению. Если да, то скрипт должен вернуть 0 и напечатать имя языка или фреймворка. В нашем случае мы проверяем наличие app.awk в корне приложения, введем такое соглашение.

#!/usr/bin/env bash
# bin/detect 

# Any awk web app must have app.awk file
if [ -f $1/app.awk ]; then
  echo "gawk"
  exit 0
fi

exit 1
 

compile должен устновить все что нужно и скомпилировать пользовательское приложение, если это требуется. В нашем случае надо загрузить и установить gawk. Чтобы экономить время во время разворачивания приложения можно было собрать его вручную под разные версии Ubuntu и выбирать подходящую на этом шаге. Но сборка gawk не такой затратный процесс, кроме того, поддерживается кэш, так что для большинства проектов компиляция будет выполняться один раз. Зато мы точно получим файл, собранный под нужную версию ОС и архитектуру.

#!/usr/bin/env bash
# bin/compile  

set -e

build_dir=$1
cache_dir=$2

awk_cache=$cache_dir/gawk-4.1.3
awk_url="http://ftp.gnu.org/gnu/gawk/gawk-4.1.3.tar.gz"

if [ -f $awk_cache/gawk ]; then
    echo "Using cached gawk"
else
    # download and compile gawk
    mkdir -p $awk_cache
    curl -o $cache_dir/gawk.tar.gz "$awk_url"
    cd $cache_dir
    tar zxvf  gawk.tar.gz 
    cd $awk_cache
    ./configure
    make
fi

# copy gawk to build dir
cp $awk_cache/gawk $build_dir 

# set path and test gawk
PATH=$build_dir:$PATH
gawk -V

# create a launcher to wrap env vars passing
echo "#!/usr/bin/env bash\n" > $build_dir/awk-start
echo "/app/gawk -v PORT=\"\$PORT\" -v HOST=\"\$VCAP_APP_HOST\" -f \$1" >> $build_dir/awk-start
chmod +x $build_dir/awk-start

Первый аргумент — место сборки файлов (build_dir), второй — кэш. Наша задача положить нужные для запуска файлы в build_dir, в нашем случает это gawk. Кроме того, Awk скрипт не имеет доступа к переменным окружения, а нам нужно знать какой порт нам выделяет CloudFoundry для запуска (переменная окружения PORT). Поэтому я сделал небольшую обертку для удобного запуска.

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

#!/usr/bin/env bash
# bin/release 

cat <

Проверяем работу скриптов локально, потом выкладываем все на github, запоминаем адрес https://github.com/hashmap/gawk-buildpack

Теперь пишем приложение. Создаем папку gawktest, в ней файл app.awk

# A Web Server in Awk
# a simple, single user, web server built with gawk.
# based on code form http://awk.info/?tools/server by Michael Sanders

BEGIN { 
  host     = "/inet/tcp/" PORT "/0/0"  # host string 
  status   = 200                       # 200 == OK 
  reason   = "OK"                      # server response 
  RS = ORS = "\r\n"                    # header line terminators 
  doc      = Setup()                   # html document 
  len      = length(doc) + length(ORS) # length of document 
  print "Staring AWK server"
  while (1) { 
     print "HTTP/1.0", status, reason |& host 
     print "Connection: Close"        |& host 
     print "Pragma: no-cache"         |& host 
     print "Content-length:", len     |& host 
     print ORS doc                    |& host 
     close(host)     # close client connection 
     host |& getline # wait for new client request 
  } 
} 

function Setup() { 
  tmp = "\
  Simple gawk server\
  \
  Hello from awk!\
  










\
  " 
  return tmp 
} 

Проверяем работу приложения локально:

 gawk -v PORT=8080 -f app.awk
 

теперь можно отправлять на Bluemix, делаем стандартный cf push, но еще указываем адрес нашего билдпака:

gawktest  cf push gawktest -b https://github.com/hashmap/gawk-buildpack
Updating app gawktest in org xxx@gmail.com / space dev as xxx@gmail.com...
OK

Uploading gawktest...
Uploading app files from: /Users/alex/projects/gawktest
Uploading 32.3K, 3 files
Done uploading
OK

Stopping app gawktest in org xxx@gmail.com / space dev as xxx@gmail.com...
OK

Starting app gawktest in org xxx@gmail.com / space dev as xxx@gmail.com...
-----> Downloaded app package (8.0K)
-----> Downloaded app buildpack cache (11M)
Cloning into '/tmp/buildpacks/gawk-buildpack'...
Using cached gawk
GNU Awk 4.1.3, API: 1.1
Copyright (C) 1989, 1991-2015 Free Software Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
-----> Uploading droplet (724K)

1 of 1 instances running

App started


OK

App gawktest was started using this command `/app/awk-start app.awk`

Showing health and status for app gawktest in org xxx@gmail.com / space dev as xxx@gmail.com...
OK

requested state: started
instances: 1/1
usage: 64M x 1 instances
urls: gawktest.mybluemix.net
last uploaded: Wed Jul 15 12:21:13 UTC 2015
stack: lucid64
buildpack: https://github.com/hashmap/gawk-buildpack

     state     since                    cpu    memory        disk         details
#0   running   2015-07-15 03:22:08 PM   0.0%   1.1M of 64M   1.9M of 1G

Все работает и доступно, пока по крайней мере, не забывайте, что сервер однопользовательский. Т.к. это не первый запуск, то уже собранный gawk просто скопировался из кэша. При первом запуске нового приложения можно будет увидеть как он скачивается, распаковывается и собирается. Также для отладки печатается версия gawk и его лицензия.

Если кто напишет веб фреймворк на Brainfuck — дайте знать.

© Habrahabr.ru