Appearance
Starlark API — Flow Manager
Все функции доступны внутри transformCodeNode и logicConditionNode. Язык — Starlark (Python-подобный sandboxed-язык).
Ограничения:
- Таймаут выполнения: 30 секунд
- Поддержка верхнеуровневых конструкций:
if,for,while(TopLevelControl: true) - TLS-обязателен для
fetch(), max body 10MB, таймаут 10с
Глобальные переменные
SANDBOX_DIR
Путь к постоянному каталогу-песочнице. Доступен как глобальная переменная и через ctx.SandboxDir. Используется для files.export() и artefacts.save().
python
print(SANDBOX_DIR) # → ./sandbox
print(ctx.SandboxDir) # → ./sandboxГлобальные функции
print(*args)
Вывод лога. Все аргументы приводятся к строке и соединяются пробелами. Лог доступен через NATS-событие node.log.
python
print("Статус:", getVar("status"))
print("x =", 42, "y =", 100)setVar(name, value)
Устанавливает переменную в общем контексте. Переменная становится доступна всем последующим нодам пайплайна.
python
setVar("user_id", 123)
setVar("status", "approved")Ошибки:
- Если передано не ровно 2 аргумента
- Если первый аргумент не является строкой
getVar(name, [default])
Возвращает значение переменной из контекста. Если переменная не найдена и передан default — возвращает его. Если default не передан и переменной нет — ошибка.
python
user = getVar("user_id")
status = getVar("status", "pending") # вернёт "pending", если переменной нетОшибки:
- Переменная не найдена и
defaultне передан:variable "X" not found
fetch(url, [method], [body], [headers], [params])
Выполняет HTTP-запрос. Поддерживает позиционные и именованные аргументы.
python
# GET-запрос
resp = fetch("https://api.example.com/users")
print(resp.status, resp.body)
# POST с JSON
resp = fetch("https://api.example.com/data", method="POST", body='{"key":"value"}')
data = resp.json()
# С заголовками
resp = fetch("https://api.example.com/auth", headers={"Authorization": "Bearer token"})
# Query-параметры объектом — экранируются автоматически
resp = fetch("https://api.example.com/search", params={"q": "hello world", "limit": 10})
# → https://api.example.com/search?limit=10&q=hello+worldАргументы:
| Аргумент | Тип | По умолчанию | Описание |
|---|---|---|---|
url | string | — | URL запроса (обязательный) |
method | string | "GET" | HTTP-метод |
body | string | None | Тело запроса |
headers | dict | {} | Заголовки запроса |
params | dict | None | Query-параметры (см. ниже) |
Query-параметры (params):
Ключи — строки, значения — строка, число, bool, список/кортеж или None.
python
# Список или кортеж — повторяющийся параметр
fetch(url, params={"tag": ["go", "python"]}) # ?tag=go&tag=python
# bool приводится к true/false, а не к питоновским True/False
fetch(url, params={"active": True}) # ?active=true
# None пропускается — удобно для опциональных параметров,
# не нужно вырезать ключ из словаря вручную
cursor = getVar("cursor", None)
fetch(url, params={"limit": 50, "cursor": cursor}) # ?limit=50, если cursor пустПараметры дополняют query, уже присутствующий в URL, а не затирают его:
python
fetch("https://api.example.com/list?page=1", params={"limit": 10})
# → https://api.example.com/list?page=1&limit=10Возвращаемое значение FetchResult:
| Свойство | Тип | Описание |
|---|---|---|
.status | int | HTTP-код статуса |
.body | string | Тело ответа как строка |
.bytes | bytes | Сырое тело ответа |
.json() | method | Парсит тело как JSON |
.content_type | string | Значение заголовка Content-Type |
.headers | dict | Заголовки ответа (значения — списки строк) |
Ошибки:
paramsне является словарём:fetch: 'params' must be a dict, got X- Нестроковый ключ:
fetch: all 'params' keys must be strings, got X - Неподдерживаемый тип значения (например, вложенный
dict):fetch: 'params' value for "key" must be a string, number or bool, got X
Ограничения безопасности (SSRF):
- Блокируются приватные IP:
10.x,172.16-31.x,192.168.x,127.x - Блокируются:
169.254.169.254,metadata.google.internal - Блокируются суффиксы:
*.internal,*.local,*.localhost - Блокируются не-HTTP схемы
setupProcess(parallel, [workers], [stop_after_error])
Настраивает режим выполнения веток, создаваемых через runProcess(). Вызывается перед runProcess().
| Аргумент | Тип | Обязательный | По умолчанию | Описание |
|---|---|---|---|---|
parallel | bool | Да | — | Запускать ветки параллельно (true) или последовательно (false) |
workers | int | Нет | 0 | Максимальное количество параллельных веток (0 = без ограничений) |
stop_after_error | bool | Нет | false | При true — все ветки падают при ошибке в одной; при false — цепочка успешна, если хотя бы одна ветка завершилась успешно |
python
# Параллельный запуск, падать при первой ошибке
setupProcess(True, stop_after_error=True)
for item in getVar("task_list"):
runProcess({"task": item})python
# Параллельный запуск с ограничением 3 веток
setupProcess(True, workers=3)
for team in ["backend", "frontend", "qa"]:
runProcess({"team": team})python
# Последовательный запуск, не падать при ошибках
setupProcess(False, stop_after_error=False)
for step in ["validate", "transform", "load"]:
runProcess({"step": step})runProcess([obj])
Запускает ветку выполнения. Параметр obj (обычно dict) используется для создания контекста ветки — ключи и значения мёржаются с клонированным контекстом.
Поведение:
- Устанавливает
ctx.StopFlow = true(текущая нода не продолжается) - Каждая ветка получает свой клон контекста (
ctx.Clone()) с мёрджем данных изobj - Режим параллельности/последовательности определяется через
setupProcess()
python
# Настроить параллельный запуск до 5 веток
setupProcess(parallel=True, workers=5)
teams = ["backend", "frontend", "qa", "devops"]
for team in teams:
runProcess({"team": team, "action": "deploy"})python
# Простой фан-аут без настройки (последовательно, без ограничений)
runProcess({"branch": "email"})
runProcess({"branch": "slack"})Ошибки:
- Если передано больше 1 аргумента
Контекст execution context
Контекст — это общее хранилище переменных, передаваемое между нодами пайплайна. Каждая нода получает клон контекста от предыдущей ноды, что позволяет ветвление с изолированными состояниями.
Жизненный цикл
- Инициализация — контекст создаётся при старте пайплайна. Источник:
contextиз запроса или пустой{}. - Передача — при переходе к следующей ноде контекст клонируется (
ctx.Clone()). - Ветвление — при
runProcess()илиlogicConditionNodeсоздаются независимые клоны для каждой ветки. - Завершение — финальный контекст сохраняется в инстансе пайплайна.
Доступ к переменным
| Способ | Описание |
|---|---|
ctx.get(key) | Возвращает значение или None |
ctx.set(key, value) | Устанавливает значение |
getVar(key, [default]) | Глобальная функция (эквивалент ctx.get) |
setVar(key, value) | Глобальная функция (эквивалент ctx.set) |
python
# Через методы ctx
ctx.set("processed", True)
value = ctx.get("user_id")
# Через глобальные функции (эквивалентно)
setVar("processed", True)
value = getVar("user_id")Встроенные переменные контекста
После выполнения jobNode в контекст автоматически добавляется переменная artefacts — словарь со списками ID артефактов:
| Ключ | Тип | Описание |
|---|---|---|
artefacts.previous | list[string] | Артефакты, созданные последним job-нодом |
artefacts.all | list[string] | Все артефакты, накопленные с начала пайплайна |
python
# После jobNode — получить артефакты из контекста
prev = getVar("artefacts")["previous"]
all_ = getVar("artefacts")["all"]
print("Артефакты этого шага:", prev)
print("Все артефакты:", all_)Пример: передача артефакта между нодами
python
# Нода 1 (после jobNode): артефакт уже в контексте
artifact_id = getVar("artefacts")["previous"][0]
# Нода 2: читаем файл из артефакта
data = artefacts.read(artifact_id, "result.json")
print(data)Артефакты (artefacts)
Глобальный модуль для работы с артефактами (ZIP-файлы в storage-сервисе). Доступен как глобальная переменная artefacts.
Для сохранения артефактов beyond выполнения пайплайна используйте artefacts.save() — он извлекает ZIP-архив в постоянный каталог {SANDBOX_DIR}/{pipeline_id}/.
artefacts.list(artefact_id)
Возвращает список имён файлов в артефакте.
python
files = artefacts.list("550e8400-e29b-41d4-a716-446655440000")
for f in files:
print(f) # "data.csv", "result.json"Возвращаемое значение: list[string] — имена файлов
artefacts.read(artefact_id, filename)
Читает содержимое файла из артефакта.
python
data = artefacts.read(artefact_id, "data.csv")
print("Прочитано символов:", len(data))Возвращаемое значение: string
artefacts.extract(artefact_id, [dest])
Извлекает все файлы из артефакта во временный каталог.
python
# В текущий рабочий каталог (ARTIFACTS_TMP_DIR/<instance_id>)
artefacts.extract(artefact_id)
# В указанную поддиректорию
artefacts.extract(artefact_id, "my_data")Аргументы:
| Аргумент | Тип | По умолчанию | Описание |
|---|---|---|---|
artefact_id | string | — | UUID артефакта (обязательный) |
dest | string | текущий каталог | Поддиректория относительно рабочего каталога (опциональный) |
Возвращаемое значение: string — путь куда распаковано
artefacts.create(files)
Создаёт новый ZIP-артефакт из файлов на диске и загружает его в storage.
python
# Из файлов в рабочем каталоге
artifact_id = artefacts.create([
"output/result.txt",
"output/meta.json",
])
# С явным указанием имён
artifact_id = artefacts.create([
{"name": "result.txt", "path": "output/result.txt"},
{"name": "data.json", "path": "output/data.json"},
])
print("Создан артефакт:", artifact_id)Аргументы:
| Аргумент | Тип | Описание |
|---|---|---|
files | list[string | dict] | Список путей к файлам или словарей {"name": string, "path": string} |
Возвращаемое значение: string — UUID созданного артефакта
artefacts.save(artefact_id, name, [dest_dir], [extract])
Сохраняет артефакт в постоянный каталог-песочницу (SANDBOX_DIR/{pipeline_id}/). По умолчанию ZIP-архив сохраняется как файл. С extract=True — распаковывается в директорию. Файлы, сохранённые через эту функцию, не удаляются после завершения выполнения пайплайна.
python
# Сохранение ZIP-архива как файла
artefacts.save(artefact_id, "workspace.zip")
# → SANDBOX_DIR/{pipeline_id}/workspace.zip
# Сохранение в поддиректорию
artefacts.save(artefact_id, "build.zip", "releases")
# → SANDBOX_DIR/{pipeline_id}/releases/build.zip
# Распаковка артефакта в директорию
artefacts.save(artefact_id, "source", extract=True)
# → SANDBOX_DIR/{pipeline_id}/source/file1.txt
# → SANDBOX_DIR/{pipeline_id}/source/file2.txt
# Распаковка в поддиректорию
artefacts.save(artefact_id, "code", "projects", extract=True)
# → SANDBOX_DIR/{pipeline_id}/projects/code/file1.txtАргументы:
| Аргумент | Тип | Обязательный | По умолчанию | Описание |
|---|---|---|---|---|
artefact_id | string | Да | — | UUID артефакта |
name | string | Да | — | Имя файла или директории |
dest_dir | string | Нет | "" | Поддиректория относительно {SANDBOX_DIR}/{pipeline_id}/ |
extract | bool | Нет | false | Распаковать ZIP в директорию с именем name |
Возвращаемое значение: string — полный путь куда сохранено
Ограничения:
nameне может содержать..или/(защита от path traversal)dest_dirне может содержать..- Максимальный распакованный размер: 1 GiB
- Переменная
SANDBOX_DIRдолжна быть настроена в конфигурации
Платформенный клиент (up)
Глобальный модуль для доступа к API всех сервисов платформы. Доступен как глобальная переменная up. Использует Service Token для межсервисного взаимодействия.
Доступные подмодули
| Подмодуль | Описание |
|---|---|
up.auth | Аутентификация и управление пользователями |
up.spaces | Управление пространствами |
up.documents | Управление документами |
up.commands | Управление командами и задачами |
up.scheduler | Управление календарями и событиями |
up.storage | Работа с файловым хранилищем |
up.props | Управление свойствами |
up.gateway | Проверка состояния платформы |
up.auth
up.auth.login(login, password)
Авторизация пользователя.
python
resp = up.auth.login("user@example.com", "password123")
print(resp)up.auth.register(login, email, password, [first_name, last_name])
Регистрация нового пользователя.
python
resp = up.auth.register("newuser", "user@example.com", "pass123", "Иван", "Петров")up.auth.logout()
Выход из системы.
python
up.auth.logout()up.auth.refresh()
Обновление сессии.
python
resp = up.auth.refresh()up.auth.get_profile()
Получение профиля текущего пользователя.
python
profile = up.auth.get_profile()
print(profile["login"], profile["email"])up.auth.update_profile([login], [first_name], [last_name])
Обновление профиля пользователя.
python
up.auth.update_profile(first_name="Новое Имя")up.auth.change_password(current_password, new_password)
Смена пароля.
python
up.auth.change_password("old_pass", "new_pass")up.spaces
up.spaces.create(name)
Создание нового пространства.
python
space = up.spaces.create("Мой проект")
print(space["id"])up.spaces.list()
Получение списка пространств.
python
spaces = up.spaces.list()
for s in spaces:
print(s["name"])up.documents
up.documents.create(space_id, name, body, type, [parent_id])
Создание документа.
python
doc = up.documents.create(
"space-uuid",
"Новый документ",
"Содержимое документа",
"text",
"parent-uuid" # опционально
)up.documents.get(space_id, document_id)
Получение документа.
python
doc = up.documents.get("space-uuid", "doc-uuid")
print(doc["body"])up.documents.list(space_id)
Список документов в пространстве.
python
docs = up.documents.list("space-uuid")up.documents.tree(space_id)
Дерево документов.
python
tree = up.documents.tree("space-uuid")up.documents.update(space_id, document_id, [name], [body], [type], [parent_id])
Обновление документа.
python
up.documents.update("space-uuid", "doc-uuid", name="Обновленное имя")up.documents.delete(space_id, document_id)
Мягкое удаление документа.
python
up.documents.delete("space-uuid", "doc-uuid")up.documents.hard_delete(space_id, document_id)
Полное удаление документа.
python
up.documents.hard_delete("space-uuid", "doc-uuid")up.documents.restore(space_id, document_id)
Восстановление документа.
python
up.documents.restore("space-uuid", "doc-uuid")up.documents.move(space_id, document_id, parent_id)
Перемещение документа.
python
up.documents.move("space-uuid", "doc-uuid", "new-parent-uuid")up.documents.duplicate(space_id, document_id, [name])
Дублирование документа.
python
up.documents.duplicate("space-uuid", "doc-uuid", "Копия документа")up.commands
up.commands.create(name, description, type, source, [variables], [tags])
Создание команды.
python
cmd = up.commands.create(
"Деплой",
"Скрипт деплоя на прод",
"shell",
"./deploy.sh",
variables=[{"name": "env", "description": "Окружение", "type": "string", "default": "staging"}],
tags=["deploy", "prod"]
)up.commands.list([offset], [limit], [search], [type], [tag], [sort_by], [sort_order])
Список команд.
python
cmds = up.commands.list(search="деплой", limit=10)up.commands.get(command_id)
Получение команды.
python
cmd = up.commands.get("cmd-uuid")up.commands.update(command_id, [name], [description], [type], [source])
Обновление команды.
python
up.commands.update("cmd-uuid", name="Новое имя")up.commands.delete(command_id)
Удаление команды.
python
up.commands.delete("cmd-uuid")up.commands.share(command_id, users)
Предоставление доступа к команде.
python
up.commands.share("cmd-uuid", [{"user_id": "user-uuid", "role": "executor"}])up.commands.create_job(command_id, [status], [variables])
Создание задачи.
python
job = up.commands.create_job("cmd-uuid", variables=[{"name": "env", "value": "production"}])up.commands.get_jobs([offset], [limit], [status], [command_id])
Получение списка задач.
python
jobs = up.commands.get_jobs(status="completed", limit=5)up.commands.get_job(job_id)
Получение задачи.
python
job = up.commands.get_job("job-uuid")up.commands.stop_job(job_id)
Остановка задачи.
python
up.commands.stop_job("job-uuid")up.commands.get_job_logs(job_id, [offset], [limit])
Получение логов задачи.
python
logs = up.commands.get_job_logs("job-uuid", limit=100)up.scheduler
up.scheduler.create_calendar(name, description, color, visibility)
Создание календаря.
python
cal = up.scheduler.create_calendar("Рабочий", "Рабочие события", "#4285F4", "private")up.scheduler.list_calendars()
Список календарей.
python
cals = up.scheduler.list_calendars()up.scheduler.get_calendar(calendar_id)
Получение календаря.
python
cal = up.scheduler.get_calendar("cal-uuid")up.scheduler.update_calendar(calendar_id, [name], [description], [color], [visibility])
Обновление календаря.
python
up.scheduler.update_calendar("cal-uuid", name="Новое имя")up.scheduler.delete_calendar(calendar_id)
Удаление календаря.
python
up.scheduler.delete_calendar("cal-uuid")up.scheduler.share_calendar(calendar_id, user_id)
Предоставление доступа к календарю.
python
up.scheduler.share_calendar("cal-uuid", "user-uuid")up.scheduler.create_event(calendar_id, title, description, start_at, end_at, [location], [all_day])
Создание события.
python
event = up.scheduler.create_event(
"cal-uuid",
"Совещание",
"Обсуждение проекта",
"2024-01-15T10:00:00Z",
"2024-01-15T11:00:00Z",
location="Конференц-зал"
)up.scheduler.get_events(calendar_id, [from], [to])
Получение событий.
python
events = up.scheduler.get_events("cal-uuid", from="2024-01-01T00:00:00Z", to="2024-01-31T23:59:59Z")up.scheduler.get_event(event_id)
Получение события.
python
event = up.scheduler.get_event("event-uuid")up.scheduler.update_event(event_id, [title], [description], [start_at], [end_at])
Обновление события.
python
up.scheduler.update_event("event-uuid", title="Новое название")up.scheduler.delete_event(event_id)
Удаление события.
python
up.scheduler.delete_event("event-uuid")up.storage
up.storage.list([offset], [limit], [category], [sort_by], [sort_order])
Список файлов.
python
files = up.storage.list(category="artefacts", limit=10)up.storage.get(file_id)
Получение информации о файле.
python
file_info = up.storage.get("file-uuid")up.storage.get_url(file_id)
Получение URL для скачивания файла.
python
url = up.storage.get_url("file-uuid")
print(url)up.storage.delete(file_id)
Удаление файла.
python
up.storage.delete("file-uuid")up.props
up.props.create(name, value, [description])
Создание свойства.
python
prop = up.props.create("api_key", "sk-123456", "API ключ для внешнего сервиса")up.props.list([offset], [limit], [search], [sort_by], [sort_order])
Список свойств.
python
props = up.props.list(search="api")up.props.get(prop_id)
Получение свойства.
python
prop = up.props.get("prop-uuid")up.props.update(prop_id, [name], [description], [value])
Обновление свойства.
python
up.props.update("prop-uuid", value="new-api-key")up.props.delete(prop_id)
Удаление свойства.
python
up.props.delete("prop-uuid")up.gateway
up.gateway.health()
Проверка здоровья платформы.
python
health = up.gateway.health()
print(health)up.gateway.ready()
Проверка готовности платформы.
python
ready = up.gateway.ready()up.gateway.live()
Проверка доступности платформы.
python
live = up.gateway.live()up.gateway.services()
Получение списка сервисов.
python
services = up.gateway.services()
for svc in services:
print(svc["name"], svc["status"])Файлы (files)
Глобальный модуль для работы с файлами в рабочем каталоге пайплайна. Доступен как глобальная переменная files. Все пути разрешаются относительно рабочего каталога (ARTIFACTS_TMP_DIR/<instance_id>).
Для сохранения файлов beyond выполнения пайплайна используйте files.export() — он копирует файлы в постоянный каталог {SANDBOX_DIR}/{pipeline_id}/.
files.list([path])
Возвращает список файлов и директорий.
python
items = files.list() # корень рабочего каталога
items = files.list("output") # поддиректория output
for item in items:
print(item["name"], item["size"], item["is_dir"])Возвращаемое значение: list[dict] — список {"name": string, "size": int, "is_dir": bool}
files.read(path)
Читает содержимое файла.
python
data = files.read("output/result.txt")
print(data)Возвращаемое значение: string
files.write(path, data)
Записывает данные в файл (создаёт родительские директории автоматически).
python
files.write("output/report.txt", "Hello World")
files.write("output/config.json", '{"key": "value"}')Поддерживаемые типы данных: string, bytes
files.delete(path)
Удаляет файл или директорию.
python
files.delete("output/old_file.txt")
files.delete("output/old_dir")files.mkdir(path)
Создаёт директорию (включая родительские).
python
files.mkdir("output/subdir")files.export(src, [dest_dir])
Копирует файл или директорию из временного рабочего каталога в постоянный каталог-песочницу (SANDBOX_DIR/{pipeline_id}/). Файлы, экспортированные через эту функцию, не удаляются после завершения выполнения пайплайна.
python
# Экспорт файла в корень песочницы
files.export("output/build.tar.gz")
# → SANDBOX_DIR/{pipeline_id}/output/build.tar.gz
# Экспорт файла в указанную поддиректорию
files.export("output/report.pdf", "/reports")
# → SANDBOX_DIR/{pipeline_id}/reports/output/report.pdf
# Экспорт всей директории
files.export("build/", "/exports")
# → SANDBOX_DIR/{pipeline_id}/exports/build/...Аргументы:
| Аргумент | Тип | По умолчанию | Описание |
|---|---|---|---|
src | string | — | Путь к файлу или директории во временном каталоге (обязательный) |
dest_dir | string | "" | Поддиректория относительно {SANDBOX_DIR}/{pipeline_id}/ (опциональный) |
Возвращаемое значение: string — полный путь куда скопировано
Ограничения:
srcдолжен существовать в рабочем каталогеdest_dirне может содержать..(защита от path traversal)- Переменная
SANDBOX_DIRдолжна быть настроена в конфигурации