Skip to content

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

Глобальные функции

Вывод лога. Все аргументы приводятся к строке и соединяются пробелами. Лог доступен через 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

Аргументы:

АргументТипПо умолчаниюОписание
urlstringURL запроса (обязательный)
methodstring"GET"HTTP-метод
bodystringNoneТело запроса
headersdict{}Заголовки запроса
paramsdictNoneQuery-параметры (см. ниже)

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:

СвойствоТипОписание
.statusintHTTP-код статуса
.bodystringТело ответа как строка
.bytesbytesСырое тело ответа
.json()methodПарсит тело как JSON
.content_typestringЗначение заголовка Content-Type
.headersdictЗаголовки ответа (значения — списки строк)

Ошибки:

  • 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().

АргументТипОбязательныйПо умолчаниюОписание
parallelboolДаЗапускать ветки параллельно (true) или последовательно (false)
workersintНет0Максимальное количество параллельных веток (0 = без ограничений)
stop_after_errorboolНет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

Контекст — это общее хранилище переменных, передаваемое между нодами пайплайна. Каждая нода получает клон контекста от предыдущей ноды, что позволяет ветвление с изолированными состояниями.

Жизненный цикл

  1. Инициализация — контекст создаётся при старте пайплайна. Источник: context из запроса или пустой {}.
  2. Передача — при переходе к следующей ноде контекст клонируется (ctx.Clone()).
  3. Ветвление — при runProcess() или logicConditionNode создаются независимые клоны для каждой ветки.
  4. Завершение — финальный контекст сохраняется в инстансе пайплайна.

Доступ к переменным

СпособОписание
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.previouslist[string]Артефакты, созданные последним job-нодом
artefacts.alllist[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_idstringUUID артефакта (обязательный)
deststringтекущий каталогПоддиректория относительно рабочего каталога (опциональный)

Возвращаемое значение: 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)

Аргументы:

АргументТипОписание
fileslist[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_idstringДаUUID артефакта
namestringДаИмя файла или директории
dest_dirstringНет""Поддиректория относительно {SANDBOX_DIR}/{pipeline_id}/
extractboolНет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/...

Аргументы:

АргументТипПо умолчаниюОписание
srcstringПуть к файлу или директории во временном каталоге (обязательный)
dest_dirstring""Поддиректория относительно {SANDBOX_DIR}/{pipeline_id}/ (опциональный)

Возвращаемое значение: string — полный путь куда скопировано

Ограничения:

  • src должен существовать в рабочем каталоге
  • dest_dir не может содержать .. (защита от path traversal)
  • Переменная SANDBOX_DIR должна быть настроена в конфигурации