[Nginx, JavaScript, DevOps] Я сделал свой PyPI-репозитарий с авторизацией и S3. На Nginx
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В данной статье хочу поделится опытом работы с NJS, интерпретатора JavaScript для Nginx разрабатываемого в компании Nginx inc, описав на реальном примере его основные возможности. NJS это подмножество ЯП JavaScript, которое позволяет расширить функциональность Nginx. На вопрос зачем свой интерпретатор??? подробно ответил Дмитрий Волынцев. Если вкратце: NJS это nginx-way, а JavaScript более прогрессивный, «родной» и без GC в отличии от Lua.A long time ago...На прошлой работе в наследство мне достался gitlab с некоторым количеством разношерстых CI/CD-пайплайнов с docker-compose, dind и прочими прелестями, которые были переведены на рельсы kaniko. Образы же, которые использовались ранее в CI, переехали в первозданном виде. Работали они исправно до того дня, пока у нашего gitlab не сменился IP и CI превратился в тыкву. Проблема была в том, что в одном из docker-образов, участвовавшего в CI, был git, который по ssh тянул Python-модули. Для ssh нужен приватный ключ и ... он был в образе вместе с known_hosts. И любой CI завершался ошибкой проверки ключа из-за несовпадения реального IP и указанного в known_hosts. Из имеющихся Dockfile-ов был быстро собран новый образ и добавлена опция StrictHostKeyChecking no. Но неприятный привкус остался и появилось желание перенести либы в приватный PyPI-репозиторий. Дополнительным бонусом, после перехода на приватный PyPI, становился более простой пайплайн и нормальное описание requirements.txtВыбор сделан, Господа!Мы всё крутим в облаках и Kubernetes и в итоге хотелось получить небольшой сервис который представлял из себя stateless-контейнером с внешнем хранилищем. Ну а так как мы используем S3, то и приоритет был за ним. И по возможности с аутентификацией в gitlab (можно и самому дописать по необходимости).Беглый поиск дал несколько результатов s3pypi, pypicloud и вариант с «ручным» созданием html-файлов для репы. Последний вариант отпал сам-собой.s3pypi: Это cli для использования хостинга на S3. Выкладываем файлы, генерим html и заливаем в тот же бакет. Для домашнего использования подойдет.pypicloud: Казался интересным проектом, но после прочтения доки пришло разочарование. Не смотря на хорошую документацию и возможности расширения под свои задачи, на деле оказался избыточен и сложный в настройке. Поправить код под свои задачи, по тогдашним прикидкам, заняло бы 3-5 дней. Так же сервису необходима БД. Оставили его на случай, если более ничего не найдем.Более углубленный поиск дал модуль для Nginx, ngx_aws_auth. Результатом его тестирования стал XML отображаемый в браузере, по которому было видно содержимое бакета S3. Последний коммит, на момент поиска, был год назад. Репозиторий выглядел заброшенным.Обратившись к первоисточнику и прочитав PEP-503 понял, что XML можно конвертировать в HTML налету и отдавать его pip. Ещё немного погуглив по словам Nginx и S3 наткнулся на пример аутентификации в S3 написанный на JS для Nginx. Так я познакомился с NJS.Взяв за основу этот пример, через час наблюдал в своем браузере тот же XML, что и при использовании модуля ngx_aws_auth, но написано уже все было на JS.Решение на nginx мне очень нравилось. Во-первых хорошая документация и множество примеров, во-вторых мы получаем все плюшки Nginx по работе с файлами (из коробки), в-третьих любой человек умеющий писать конфиги для Nginx, сможет разобраться что к чему. Так же плюсом для меня является минимализм, по сравнению с Python или Go (если писать с нуля), не говоря уж об nexus.TL;DR Через 2 дня тестовая версия PyPi уже была использована в CI.Как это работает?В Nginx подгружается модуль ngx_http_js_module, включен в официальный docker-образ. Импортируем наш скрипт c помощью директивы js_importв конфигурацию Nginx. Вызов функции осуществляется директивой js_content. Для установки переменных используется директива js_set, которая аргументом принимает только функцию описанную в скрипте. А вот выполнять подзапросы в NJS мы можем только с помощью Nginx, ни каких Вам там XMLHttpRequest. Для этого в конфигурации Nginx должен быть добавлен соответствующий локейшн. А в скрипте должен быть описан подзапрос (subrequest) к этому локейшену. Чтобы иметь возможность обратиться к функции из конфига Nginx, в самом скрипте имя функции необходимо экспортировать export default.nginx.conf
load_module modules/ngx_http_js_module.so;
http {
js_import imported_name from script.js;
server {
listen 8080;
...
location = /sub-query {
internal;
proxy_pass http://upstream;
}
location / {
js_content imported_name.request;
}
}
script.js
function request(r) {
function call_back(resp) {
// handler's code
r.return(resp.status, resp.responseBody);
}
r.subrequest('/sub-query', { method: r.method }, call_back);
}
export default {request}
При запросе в браузере http://localhost:8080/ мы попадаем в location /в котором директива js_content вызывает функцию request описанную в нашем скрипте script.js. В свою очередь в функции request осуществляется подзапрос к location = /sub-query, с методом (в текущем примере GET) полученным из аргумента (r), неявно передаваемым при вызове этой функции. Обработка ответа подзапроса будет осуществлена в функции call_back.Пробуем S3Чтобы сделать запрос к приватному S3-хранилищу, нам необходимы:ACCESS_KEYSECRET_KEYS3_BUCKETИз используемого http-метода, текущая дата/время, S3_NAME и URI генерируется определенного вида строка, которая подписывается (HMAC_SHA1) с помощью SECRET_KEY. Далее строку, вида AWS $ACCESS_KEY:$HASH, можно использовать в заголовке авторизации. Та же дата/время, что была использована для генерации строки на предыдущем шаге, должна быть добавлена в заголовок X-amz-date. В коде это выглядит так:nginx.conf
load_module modules/ngx_http_js_module.so;
http {
js_import s3 from s3.js;
js_set $s3_datetime s3.date_now;
js_set $s3_auth s3.s3_sign;
server {
listen 8080;
...
location ~* /s3-query/(?<s3_path>.*) {
internal;
proxy_set_header X-amz-date $s3_datetime;
proxy_set_header Authorization $s3_auth;
proxy_pass $s3_endpoint/$s3_path;
}
location ~ "^/(?<prefix>[\w-]*)[/]?(?<postfix>[\w-\.]*)$" {
js_content s3.request;
}
}
s3.js(пример авторизации AWS Sign v2, переведена в статус deprecated)
var crypt = require('crypto');
var s3_bucket = process.env.S3_BUCKET;
var s3_access_key = process.env.S3_ACCESS_KEY;
var s3_secret_key = process.env.S3_SECRET_KEY;
var _datetime = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '');
function date_now() {
return _datetime
}
function s3_sign(r) {
var s2s = r.method + '\n\n\n\n';
s2s += `x-amz-date:${date_now()}\n`;
s2s += '/' + s3_bucket;
s2s += r.uri.endsWith('/') ? '/' : r.variables.s3_path;
return `AWS ${s3_access_key}:${crypt.createHmac('sha1', s3_secret_key).update(s2s).digest('base64')}`;
}
function request(r) {
var v = r.variables;
function call_back(resp) {
r.return(resp.status, resp.responseBody);
}
var _subrequest_uri = r.uri;
if (r.uri === '/') {
// root
_subrequest_uri = '/?delimiter=/';
} else if (v.prefix !== '' && v.postfix === '') {
// directory
var slash = v.prefix.endsWith('/') ? '' : '/';
_subrequest_uri = '/?prefix=' + v.prefix + slash;
}
r.subrequest(`/s3-query${_subrequest_uri}`, { method: r.method }, call_back);
}
export default {request, s3_sign, date_now}
Немного пояснения про _subrequest_uri: это переменная которая в зависимости от изначального uri формирует запрос к S3. Если нужно получить содержимое «корня», в таком случае необходимо сформировать uri-запрос с указанием разделителя delimiter, который вернет список всех xml-элементов CommonPrefixes, что соответствует директориям (в случае с PyPI, список всех пакетов). Если нужно получить список содержимого в определенной директории (список всех версий пакетов), тогда uri-запрос должен содержать поле prefix с именем директории (пакета) обязательно заканчивающийся на слэш /. В противном случае возможны коллизии при запросе содержимого директории, например. Есть директории aiohttp-request и aiohttp-requests и если в запросе будет указано /?prefix=aiohttp-request, тогда в ответе будет содержимое обеих директорий. Если же на конце будет слэш, /?prefix=aiohttp-request/, то в ответе будет только нужная директория. И если мы запрашиваем файл, то результирующий uri не должен отличать от изначального.Сохраняем, перезапускаем Nginx. В браузере вводим адрес нашего Nginx, результатом работы запроса будет XML, например:Список директорий
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>myback-space</Name>
<Prefix></Prefix>
<Marker></Marker>
<MaxKeys>10000</MaxKeys>
<Delimiter>/</Delimiter>
<IsTruncated>false</IsTruncated>
<CommonPrefixes>
<Prefix>new/</Prefix>
</CommonPrefixes>
<CommonPrefixes>
<Prefix>old/</Prefix>
</CommonPrefixes>
</ListBucketResult>
Из списка директорий понадобятся только элементы CommonPrefixes.Добавив, в браузере, к нашему адресу нужную нам директорию, получим ее содержимое так же в виде XML:Список файлов в директории
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name> myback-space</Name>
<Prefix>old/</Prefix>
<Marker></Marker>
<MaxKeys>10000</MaxKeys>
<Delimiter></Delimiter>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>old/giphy.mp4</Key>
<LastModified>2020-08-21T20:27:46.000Z</LastModified>
<ETag>"00000000000000000000000000000000-1"</ETag>
<Size>1350084</Size>
<Owner>
<ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
<DisplayName></DisplayName>
</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>old/hsd-k8s.jpg</Key>
<LastModified>2020-08-31T16:40:01.000Z</LastModified>
<ETag>"b2d76df4aeb4493c5456366748218093"</ETag>
<Size>93183</Size>
<Owner>
<ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
<DisplayName></DisplayName>
</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
Из списка файлов возьмем только элементы Key.Остается полученный XML распарсить и отдать в виде HTML, предварительно заменив заголовок Content-Type на text/html.
function request(r) {
var v = r.variables;
function call_back(resp) {
var body = resp.responseBody;
if (r.method !== 'PUT' && resp.status < 400 && v.postfix === '') {
r.headersOut['Content-Type'] = "text/html; charset=utf-8";
body = toHTML(body);
}
r.return(resp.status, body);
}
var _subrequest_uri = r.uri;
...
}
function toHTML(xml_str) {
var keysMap = {
'CommonPrefixes': 'Prefix',
'Contents': 'Key',
};
var pattern = `<k>(?<v>.*?)<\/k>`;
var out = [];
for(var group_key in keysMap) {
var reS;
var reGroup = new RegExp(pattern.replace(/k/g, group_key), 'g');
while(reS = reGroup.exec(xml_str)) {
var data = new RegExp(pattern.replace(/k/g, keysMap[group_key]), 'g');
var reValue = data.exec(reS);
var a_text = '';
if (group_key === 'CommonPrefixes') {
a_text = reValue.groups.v.replace(/\//g, '');
} else {
a_text = reValue.groups.v.split('/').slice(-1);
}
out.push(`<a href="/${reValue.groups.v}">${a_text}</a>`);
}
}
return '<html><body>\n' + out.join('</br>\n') + '\n</html></body>'
}
Пробуем PyPIПроверяем, что ни где и ни чего не ломается на заведомо рабочих пакетах.
# Создаем для тестов новое окружение
python3 -m venv venv
. ./venv/bin/activate
# Скачиваем рабочие пакеты.
pip download aiohttp
# Загружаем в приватную репу
for wheel in *.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done
rm -f *.whl
# Устанавливаем из приватной репы
pip install aiohttp -i http://localhost:8080
Повторяем с нашими либами.
# Создаем для тестов новое окружение
python3 -m venv venv
. ./venv/bin/activate
pip install setuptools wheel
python setup.py bdist_wheel
for wheel in dist/*.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done
pip install our_pkg --extra-index-url http://localhost:8080
В CI, создание и загрузка пакета выглядит так:
pip install setuptools wheel
python setup.py bdist_wheel
curl -sSfT dist/*.whl -u "gitlab-ci-token:${CI_JOB_TOKEN}" "https://pypi.our-domain.com/${CI_PROJECT_NAME}"
АутентификацияВ Gitlab возможно использовать JWT для аутентификации/авторизации внешних сервисов. Воспользовавшись директивой auth_request в Nginx, перенаправим аутентификационные данные в подзапрос содержащий вызов функции в скрипте. В скрипте будет сделан ещё один подзапрос на url Gitlab-а и если аутентификационные данные указаны были верно, то Gitlab вернет код 200 и будет разрешена загрузка/скачивание пакета. Почему не воспользоваться одним подзапросом и сразу не отправить данные в Gitlab? Потому, что придется тогда править файл конфигурации Nginx каждый раз, как у нас будут какие-то изменения в авторизации, а это достаточно муторное занятие. Так же, если в Kubernetes используется политика read-only root filesystem, то это ещё больше добавляет сложностей при подмене nginx.conf через configmap. И становится абсолютно невозможна конфигурация Nginx через configmap при одновременном использовании политик запрещающих подключение томов (pvc) и read-only root filesystem (такое тоже бывает).Используя промежуточным звеном NJS, мы получаем возможность менять указанные параметры в конфиге nginx с помощью переменных окружения и делать какие-нибудь проверки в скрипте (например, неверно указанного URL).nginx.conf
location = /auth-provider {
internal;
proxy_pass $auth_url;
}
location = /auth {
internal;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
js_content auth.auth;
}
location ~ "^/(?<prefix>[\w-]*)[/]?(?<postfix>[\w-\.]*)$" {
auth_request /auth;
js_content s3.request;
}
s3.js
var env = process.env;
var env_bool = new RegExp(/[Tt]rue|[Yy]es|[Oo]n|[TtYy]|1/);
var auth_disabled = env_bool.test(env.DISABLE_AUTH);
var gitlab_url = env.AUTH_URL;
function url() {
return `${gitlab_url}/jwt/auth?service=container_registry`
}
function auth(r) {
if (auth_disabled) {
r.return(202, '{"auth": "disabled"}');
return null
}
r.subrequest('/auth-provider',
{method: 'GET', body: ''},
function(res) {
r.return(res.status, "");
}
);
}
export default {auth, url}
Скорее всего назревает вопрос: -А почему бы не использовать готовые модули? Там ведь всё уже сделано! Например, var AWS = require('aws-sdk') и не надо писать "велосипед" с S3-аутентификацией!Перейдем к минусамДля меня, невозможность импортирование внешние JS-модулей, стало неприятной, но ожидаемой особенностью. Описанный в примере выше require('crypto'), это build-in-модули и require работает только для них. Так же нет возможности переиспользовать код из скриптов и приходится копи-пастить его по разным файлам. Надеюсь, что когда-нибудь этот функционал будет реализован.Так же для текущего проекта в Nginx должно быть отключено сжатие gzip off;Потому, что нет gzip-модуля в NJS и подключить его невозможно, соотвественно нет возможности работать с сжатыми данными. Правда, не особо это и минус для данного кейса. Текста не много, а передаваемые файлы уже сжатые и дополнительное сжатие им не особо поможет. Так же это не на столько нагруженный или критичный сервис, чтобы заморачиваться с отдачей контента на несколько миллисекунд быстрее.Отладка скрипта долгая и возможна только через «принты» в error.log. В зависимости от выставленного уровня логирования info, warn или error возможно использовать 3 метода r.log, r.warn, r.error соответственно. Некоторые скрипты пытаюсь отлаживать в Chrome (v8) или консольной тулзе njs, но не все возможно там проверить. При отладке кода, ака функциональное тестирование, history выглядит примерно так:
docker-compose restart nginx
curl localhost:8080/
docker-compose logs --tail 10 nginx
и таких последовательностей может быть сотни.Написание кода с использование подзапросов и переменных для них, превращается в запутанный клубок. Иногда начинаешь метаться по разным окнам IDE пытаясь разобраться в последовательности действий твоего кода. Это не сложно, но иногда сильно напрягает.Нет полноценной поддержки ES6.Может есть и ещё какие-то недостатки, но ни с чем я более не сталкивался. Поделитесь инфой если у Вас есть отрицательный опыт эксплуатации NJS.ЗаключениеNJS - легковесный open-source интерпретатор, позволяющий реализовать в Nginx разные сценарии на ЯП JavaScript. При его разработке было уделено большое внимание производительности. Конечно много чего в нём ещё не хватает, но проект развивается силами небольшой команды и они активно добавляют новые фичи и фиксят баги. Я же надеюсь, что когда-нибудь NJS позволит подключать внешние модули, что сделает функционал Nginx практически неограниченным. Но есть NGINX Plus и каких-то фич скорее всего не будет!Репозиторий с полным кодом к статьеnjs-pypi с поддержкой AWS Sign v4Описание директив модуля ngx_http_js_moduleОфициальный репозиторий NJS и документацияПримеры использования NJS от Дмитрия Волынцеваnjs - родной JavaScript-скриптинг в nginx / Выступление Дмитрия Волныева на Saint HighLoad++ 2019NJS в production / Выступление Василия Сошникова на HighLoad++ 2019Подпись и аутентификация REST-запросов в AWS
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, JavaScript, Математика] Математика верстальщику не нужна, или Временные функции и траектории для покадровых 2D анимаций на сайтах
- [JavaScript, Программирование, Разработка веб-сайтов] JavaScript: делегирование событий простыми словами (перевод)
- [Разработка веб-сайтов, JavaScript, Программирование, Отладка] Обработка ошибок в JavaScript
- [DevOps, Kubernetes] Простое объяснение CRD в Kubernetes и как его использовать (перевод)
- [JavaScript, TypeScript] SEO npm-пакета: почему важно правильно настраивать конфиг и писать тесты
- [GitHub, Node.JS, Open source] Midmare — свободный от HTTP-Layer модуль Node.js
- [JavaScript, Браузеры, Разработка веб-сайтов] Про Shadow DOM
- [*nix, DevOps, Git, Kubernetes, Системное администрирование] Разбираемся с Custom Tooling в Argo CD
- [Разработка под Android] Так для чего же нам все таки нужен MVI в мобильной разработке
- [Google Cloud Platform, Google Chrome, Google Web Toolkit, Google API, JavaScript] Экстренная психологическая помощь | Prototyping Weekend
Теги для поиска: #_nginx, #_javascript, #_devops, #_nginx, #_javascript, #_pypi, #_devops, #_aws_s3, #_development, #_scripting, #_pip, #_gitlabci, #_nginx, #_javascript, #_devops
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 05:47
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В данной статье хочу поделится опытом работы с NJS, интерпретатора JavaScript для Nginx разрабатываемого в компании Nginx inc, описав на реальном примере его основные возможности. NJS это подмножество ЯП JavaScript, которое позволяет расширить функциональность Nginx. На вопрос зачем свой интерпретатор??? подробно ответил Дмитрий Волынцев. Если вкратце: NJS это nginx-way, а JavaScript более прогрессивный, «родной» и без GC в отличии от Lua.A long time ago...На прошлой работе в наследство мне достался gitlab с некоторым количеством разношерстых CI/CD-пайплайнов с docker-compose, dind и прочими прелестями, которые были переведены на рельсы kaniko. Образы же, которые использовались ранее в CI, переехали в первозданном виде. Работали они исправно до того дня, пока у нашего gitlab не сменился IP и CI превратился в тыкву. Проблема была в том, что в одном из docker-образов, участвовавшего в CI, был git, который по ssh тянул Python-модули. Для ssh нужен приватный ключ и ... он был в образе вместе с known_hosts. И любой CI завершался ошибкой проверки ключа из-за несовпадения реального IP и указанного в known_hosts. Из имеющихся Dockfile-ов был быстро собран новый образ и добавлена опция StrictHostKeyChecking no. Но неприятный привкус остался и появилось желание перенести либы в приватный PyPI-репозиторий. Дополнительным бонусом, после перехода на приватный PyPI, становился более простой пайплайн и нормальное описание requirements.txtВыбор сделан, Господа!Мы всё крутим в облаках и Kubernetes и в итоге хотелось получить небольшой сервис который представлял из себя stateless-контейнером с внешнем хранилищем. Ну а так как мы используем S3, то и приоритет был за ним. И по возможности с аутентификацией в gitlab (можно и самому дописать по необходимости).Беглый поиск дал несколько результатов s3pypi, pypicloud и вариант с «ручным» созданием html-файлов для репы. Последний вариант отпал сам-собой.s3pypi: Это cli для использования хостинга на S3. Выкладываем файлы, генерим html и заливаем в тот же бакет. Для домашнего использования подойдет.pypicloud: Казался интересным проектом, но после прочтения доки пришло разочарование. Не смотря на хорошую документацию и возможности расширения под свои задачи, на деле оказался избыточен и сложный в настройке. Поправить код под свои задачи, по тогдашним прикидкам, заняло бы 3-5 дней. Так же сервису необходима БД. Оставили его на случай, если более ничего не найдем.Более углубленный поиск дал модуль для Nginx, ngx_aws_auth. Результатом его тестирования стал XML отображаемый в браузере, по которому было видно содержимое бакета S3. Последний коммит, на момент поиска, был год назад. Репозиторий выглядел заброшенным.Обратившись к первоисточнику и прочитав PEP-503 понял, что XML можно конвертировать в HTML налету и отдавать его pip. Ещё немного погуглив по словам Nginx и S3 наткнулся на пример аутентификации в S3 написанный на JS для Nginx. Так я познакомился с NJS.Взяв за основу этот пример, через час наблюдал в своем браузере тот же XML, что и при использовании модуля ngx_aws_auth, но написано уже все было на JS.Решение на nginx мне очень нравилось. Во-первых хорошая документация и множество примеров, во-вторых мы получаем все плюшки Nginx по работе с файлами (из коробки), в-третьих любой человек умеющий писать конфиги для Nginx, сможет разобраться что к чему. Так же плюсом для меня является минимализм, по сравнению с Python или Go (если писать с нуля), не говоря уж об nexus.TL;DR Через 2 дня тестовая версия PyPi уже была использована в CI.Как это работает?В Nginx подгружается модуль ngx_http_js_module, включен в официальный docker-образ. Импортируем наш скрипт c помощью директивы js_importв конфигурацию Nginx. Вызов функции осуществляется директивой js_content. Для установки переменных используется директива js_set, которая аргументом принимает только функцию описанную в скрипте. А вот выполнять подзапросы в NJS мы можем только с помощью Nginx, ни каких Вам там XMLHttpRequest. Для этого в конфигурации Nginx должен быть добавлен соответствующий локейшн. А в скрипте должен быть описан подзапрос (subrequest) к этому локейшену. Чтобы иметь возможность обратиться к функции из конфига Nginx, в самом скрипте имя функции необходимо экспортировать export default.nginx.conf load_module modules/ngx_http_js_module.so;
http { js_import imported_name from script.js; server { listen 8080; ... location = /sub-query { internal; proxy_pass http://upstream; } location / { js_content imported_name.request; } } function request(r) {
function call_back(resp) { // handler's code r.return(resp.status, resp.responseBody); } r.subrequest('/sub-query', { method: r.method }, call_back); } export default {request} load_module modules/ngx_http_js_module.so;
http { js_import s3 from s3.js; js_set $s3_datetime s3.date_now; js_set $s3_auth s3.s3_sign; server { listen 8080; ... location ~* /s3-query/(?<s3_path>.*) { internal; proxy_set_header X-amz-date $s3_datetime; proxy_set_header Authorization $s3_auth; proxy_pass $s3_endpoint/$s3_path; } location ~ "^/(?<prefix>[\w-]*)[/]?(?<postfix>[\w-\.]*)$" { js_content s3.request; } } var crypt = require('crypto');
var s3_bucket = process.env.S3_BUCKET; var s3_access_key = process.env.S3_ACCESS_KEY; var s3_secret_key = process.env.S3_SECRET_KEY; var _datetime = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, ''); function date_now() { return _datetime } function s3_sign(r) { var s2s = r.method + '\n\n\n\n'; s2s += `x-amz-date:${date_now()}\n`; s2s += '/' + s3_bucket; s2s += r.uri.endsWith('/') ? '/' : r.variables.s3_path; return `AWS ${s3_access_key}:${crypt.createHmac('sha1', s3_secret_key).update(s2s).digest('base64')}`; } function request(r) { var v = r.variables; function call_back(resp) { r.return(resp.status, resp.responseBody); } var _subrequest_uri = r.uri; if (r.uri === '/') { // root _subrequest_uri = '/?delimiter=/'; } else if (v.prefix !== '' && v.postfix === '') { // directory var slash = v.prefix.endsWith('/') ? '' : '/'; _subrequest_uri = '/?prefix=' + v.prefix + slash; } r.subrequest(`/s3-query${_subrequest_uri}`, { method: r.method }, call_back); } export default {request, s3_sign, date_now} <?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Name>myback-space</Name> <Prefix></Prefix> <Marker></Marker> <MaxKeys>10000</MaxKeys> <Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <CommonPrefixes> <Prefix>new/</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>old/</Prefix> </CommonPrefixes> </ListBucketResult> <?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <Name> myback-space</Name> <Prefix>old/</Prefix> <Marker></Marker> <MaxKeys>10000</MaxKeys> <Delimiter></Delimiter> <IsTruncated>false</IsTruncated> <Contents> <Key>old/giphy.mp4</Key> <LastModified>2020-08-21T20:27:46.000Z</LastModified> <ETag>"00000000000000000000000000000000-1"</ETag> <Size>1350084</Size> <Owner> <ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID> <DisplayName></DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> </Contents> <Contents> <Key>old/hsd-k8s.jpg</Key> <LastModified>2020-08-31T16:40:01.000Z</LastModified> <ETag>"b2d76df4aeb4493c5456366748218093"</ETag> <Size>93183</Size> <Owner> <ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID> <DisplayName></DisplayName> </Owner> <StorageClass>STANDARD</StorageClass> </Contents> </ListBucketResult> function request(r) {
var v = r.variables; function call_back(resp) { var body = resp.responseBody; if (r.method !== 'PUT' && resp.status < 400 && v.postfix === '') { r.headersOut['Content-Type'] = "text/html; charset=utf-8"; body = toHTML(body); } r.return(resp.status, body); } var _subrequest_uri = r.uri; ... } function toHTML(xml_str) { var keysMap = { 'CommonPrefixes': 'Prefix', 'Contents': 'Key', }; var pattern = `<k>(?<v>.*?)<\/k>`; var out = []; for(var group_key in keysMap) { var reS; var reGroup = new RegExp(pattern.replace(/k/g, group_key), 'g'); while(reS = reGroup.exec(xml_str)) { var data = new RegExp(pattern.replace(/k/g, keysMap[group_key]), 'g'); var reValue = data.exec(reS); var a_text = ''; if (group_key === 'CommonPrefixes') { a_text = reValue.groups.v.replace(/\//g, ''); } else { a_text = reValue.groups.v.split('/').slice(-1); } out.push(`<a href="/${reValue.groups.v}">${a_text}</a>`); } } return '<html><body>\n' + out.join('</br>\n') + '\n</html></body>' } # Создаем для тестов новое окружение
python3 -m venv venv . ./venv/bin/activate # Скачиваем рабочие пакеты. pip download aiohttp # Загружаем в приватную репу for wheel in *.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done rm -f *.whl # Устанавливаем из приватной репы pip install aiohttp -i http://localhost:8080 # Создаем для тестов новое окружение
python3 -m venv venv . ./venv/bin/activate pip install setuptools wheel python setup.py bdist_wheel for wheel in dist/*.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done pip install our_pkg --extra-index-url http://localhost:8080 pip install setuptools wheel
python setup.py bdist_wheel curl -sSfT dist/*.whl -u "gitlab-ci-token:${CI_JOB_TOKEN}" "https://pypi.our-domain.com/${CI_PROJECT_NAME}" location = /auth-provider {
internal; proxy_pass $auth_url; } location = /auth { internal; proxy_set_header Content-Length ""; proxy_pass_request_body off; js_content auth.auth; } location ~ "^/(?<prefix>[\w-]*)[/]?(?<postfix>[\w-\.]*)$" { auth_request /auth; js_content s3.request; } var env = process.env;
var env_bool = new RegExp(/[Tt]rue|[Yy]es|[Oo]n|[TtYy]|1/); var auth_disabled = env_bool.test(env.DISABLE_AUTH); var gitlab_url = env.AUTH_URL; function url() { return `${gitlab_url}/jwt/auth?service=container_registry` } function auth(r) { if (auth_disabled) { r.return(202, '{"auth": "disabled"}'); return null } r.subrequest('/auth-provider', {method: 'GET', body: ''}, function(res) { r.return(res.status, ""); } ); } export default {auth, url} docker-compose restart nginx
curl localhost:8080/ docker-compose logs --tail 10 nginx =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 05:47
Часовой пояс: UTC + 5