Zaroz
Operar servicios

Opera un servicio en marcha

Una instancia de servicio es lo que ejecuta una suscripción: un servidor de juegos, una web, una base de datos, un Workload. Esta guía es la API del día a día para cualquiera de ellos. Lo que admite un servicio concreto depende de su producto: lee antes sus capacidades (GET /service-instances/{id}/capabilities), y espera 404 en una función que el producto no ofrece. Los ejemplos usan el ayudante de Usar la API de Zaroz.

Encontrar tus servicios

curl

TerminalCode
curl -sS "$ZAROZ_API/service-instances?project_id=$PROJECT&page_size=50" -H "$AUTH" curl -sS "$ZAROZ_API/service-instances/$SERVICE" -H "$AUTH" curl -sS "$ZAROZ_API/service-instances/$SERVICE/resources" -H "$AUTH" curl -sS -X PATCH "$ZAROZ_API/service-instances/$SERVICE" -H "$AUTH" -H "Content-Type: application/json" -d '{"name": "Survival"}'

TypeScript

TypeScriptCode
import { call } from "./zaroz"; const list = await call("GET", `/service-instances?project_id=${PROJECT}`); for (const s of list.body.data) console.log(s.id, s.name, s.run_state, s.subscription_status); const res = await call("GET", `/service-instances/${SERVICE}/resources`); const main = res.body.data.find((r: any) => r.is_declared_primary); const RESOURCE = main.id; // el id que usan las rutas de logs, métricas y archivos

Python

Code
from zaroz import call _, listing = call("GET", f"/service-instances?project_id={PROJECT}") for s in listing["data"]: print(s["id"], s["name"], s["run_state"], s["subscription_status"]) _, res = call("GET", f"/service-instances/{SERVICE}/resources") RESOURCE = next(r["id"] for r in res["data"] if r["is_declared_primary"]) # lo usan logs, métricas y archivos

run_state es not_provisioned, provisioning, running, stopping, suspended, crash_looping, degraded o failed. Se lee de la base de datos, no de una llamada en vivo al clúster, así que mira availability: cluster_unavailable significa que no se pudo refrescar ahora. Renombrar (PATCH) nunca cambia el slug; cambiar el slug es un campo aparte y explícito porque mueve todas las URL que apuntan al servicio.

Energía

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/service-instances/$SERVICE/start" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/service-instances/$SERVICE/stop" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/service-instances/$SERVICE/restart" -H "$AUTH"

TypeScript

TypeScriptCode
const r = await call("POST", `/service-instances/${SERVICE}/restart`); if (r.status === 409) console.log("ocupado:", r.body.code); // operation-in-progress, restore-in-progress

Python

Code
status, body = call("POST", f"/service-instances/{SERVICE}/restart") if status == 409: print("ocupado:", body["code"]) # operation-in-progress, restore-in-progress

start y stop son explícitos y no una palanca, así que dos llamadores que lean "en marcha" a la vez no se anulan. Detener apaga el servicio y conserva su volumen; no cancela ni borra nada. Una suscripción suspendida, cancelada, terminada o fallida responde 404 aquí: paga antes lo adeudado. Mientras otra operación tiene el servicio recibes 409 operation-in-progress indicando cuál y desde cuándo; durante la restauración de una copia el código es restore-in-progress. El permiso necesario es services.power.

Logs

La API lee la ventana retenida de la salida de un contenedor, de más antigua a más reciente. Una respuesta vacía significa "no está en la ventana retenida", nunca "no ocurrió"; el campo coverage dice en una frase qué se leyó, así que muéstralo junto a las líneas.

curl

TerminalCode
L=/service-instances/$SERVICE/resources/$RESOURCE/logs curl -sS "$ZAROZ_API$L?limit=200" -H "$AUTH" curl -sS -G "$ZAROZ_API$L" -H "$AUTH" --data-urlencode "search=error" --data-urlencode "cursor=$NEXT"

TypeScript

TypeScriptCode
let cursor: string | undefined; do { const q = new URLSearchParams({ limit: "500", search: "error", ...(cursor ? { cursor } : {}) }); const { body } = await call("GET", `/service-instances/${SERVICE}/resources/${RESOURCE}/logs?${q}`); for (const line of body.lines) console.log(line.at, line.message); cursor = body.next_cursor ?? undefined; } while (cursor);

Python

Code
from urllib.parse import urlencode cursor = None while True: q = {"limit": 500, "search": "error", **({"cursor": cursor} if cursor else {})} _, body = call("GET", f"/service-instances/{SERVICE}/resources/{RESOURCE}/logs?{urlencode(q)}") for line in body["lines"]: print(line["at"], line["message"]) cursor = body["next_cursor"] if not cursor: break

search es una subcadena sin distinguir mayúsculas, no una expresión regular, y se ejecuta en el servidor sobre toda la ventana retenida. cursor es opaco y reanuda con exactitud; nunca construyas uno. container elige un contenedor cuando el pod tiene varios (containers los lista) y previous=true lee la ejecución anterior, útil tras una caída. Las líneas no traen flujo ni nivel: nada en la plataforma interpreta una severidad.

Para seguir la salida en vivo, genera una URL firmada de corta duración y ábrela: POST .../logs/stream-ticket devuelve { "url", "expires_at" }. La concesión queda en el registro de actividad de la organización. (Para Workloads, mira la retención más larga en Logs y métricas de unidades.)

Métricas

curl

TerminalCode
curl -sS "$ZAROZ_API/service-instances/$SERVICE/resources/$RESOURCE/metrics?window_minutes=15" -H "$AUTH"

TypeScript

TypeScriptCode
const m = await call("GET", `/service-instances/${SERVICE}/resources/${RESOURCE}/metrics?window_minutes=15`); for (const s of m.body.series) console.log(s.id, s.limit, s.points.length); // cpu, memory, net_in, net_out

Python

Code
_, m = call("GET", f"/service-instances/{SERVICE}/resources/{RESOURCE}/metrics?window_minutes=15") for s in m["series"]: print(s["id"], s["limit"], len(s["points"])) # cpu, memory, net_in, net_out

window_minutes es 5, 15 o 30 (por defecto); cualquier otro valor se rechaza porque la retención es de treinta minutos. Cada ventana devuelve 60 intervalos, así que uno dura 5, 15 o 30 segundos. Un intervalo sin muestras trae valor null para que un gráfico pueda cortar la línea. Cada punto es un par de marca de tiempo y valor; las series se llaman cpu, memory, net_in, net_out y limit es el techo con el que se mide (o null si no hay). Convierte las unidades tú.

Variables de entorno

curl

TerminalCode
E=/service-instances/$SERVICE/environment curl -sS "$ZAROZ_API$E" -H "$AUTH" curl -sS -X PUT "$ZAROZ_API$E/DATABASE_URL" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"value": "postgres://…", "secret": true}' curl -sS -X POST "$ZAROZ_API$E/DATABASE_URL/reveal" -H "$AUTH" # queda en el registro de actividad curl -sS -X DELETE "$ZAROZ_API$E/OLD_FLAG" -H "$AUTH"

TypeScript

TypeScriptCode
const E = `/service-instances/${SERVICE}/environment`; const set = await call("PUT", `${E}/LOG_LEVEL`, { value: "debug", secret: false }); console.log(set.body.applies, set.body.pending); // "restart", etc.

Python

Code
E = f"/service-instances/{SERVICE}/environment" _, set_ = call("PUT", f"{E}/LOG_LEVEL", {"value": "debug", "secret": False}) print(set_["applies"], set_["pending"]) # "restart", etc.

Una variable por llamada. El valor de un secreto es siempre null al listar: puedes escribirlo o revelarlo, nunca leerlo de la lista. Enviar un secreto sin value conserva el guardado. applies es el veredicto de la plataforma sobre cuándo llega el cambio al contenedor en marcha: immediately, restart (hace falta un pod nuevo; hoy todo cambio en una app de contenedor) o deploy. pending indica si el re-renderizado ocurrió de verdad: si no (por ejemplo la suscripción aún no está activa) el valor antiguo sigue vivo, lo que importa al rotar una credencial filtrada. Un cambio toma un bloqueo de configuración (409 mientras otra operación tiene el servicio) y aparece en el historial de despliegues como config_change. ZAROZ_AUTH_TOKEN está reservada.

Archivos

curl

TerminalCode
F=/service-instances/$SERVICE/resources/$RESOURCE/files curl -sS -G "$ZAROZ_API$F" -H "$AUTH" --data-urlencode "path=/data" curl -sS -G "$ZAROZ_API$F/content" -H "$AUTH" --data-urlencode "path=/data/server.properties" curl -sS -X PUT -G "$ZAROZ_API$F/content" -H "$AUTH" -H "Content-Type: application/json" \ --data-urlencode "path=/data/motd.txt" -d '{"content": "Welcome\n"}'

TypeScript

TypeScriptCode
const q = new URLSearchParams({ path: "/data/motd.txt" }); await call("PUT", `/service-instances/${SERVICE}/resources/${RESOURCE}/files/content?${q}`, { content: "Welcome\n" }); const back = await call("GET", `/service-instances/${SERVICE}/resources/${RESOURCE}/files/content?${q}`); console.log(back.body.content);

Python

Code
from urllib.parse import urlencode q = urlencode({"path": "/data/motd.txt"}) call("PUT", f"/service-instances/{SERVICE}/resources/{RESOURCE}/files/content?{q}", {"content": "Welcome\n"}) _, back = call("GET", f"/service-instances/{SERVICE}/resources/{RESOURCE}/files/content?{q}") print(back["content"])

Esto es para archivos de texto de hasta 1 MiB (un archivo mayor se rechaza con un mensaje que nombra el límite, nunca se trunca, y uno que no es UTF-8 se rechaza por el mismo motivo). La ruta es absoluta dentro del punto de montaje del volumen y el servidor la normaliza. Para algo mayor, binario o muchos archivos a la vez, usa SFTP (GET /service-instances/{id}/sftp, móntalo como unidad) o POST .../files/download-url para un enlace de descarga firmado.

Tareas programadas

Una programación ejecuta una cadena de tareas según una expresión cron. Cinco campos (minuto, hora, día del mes, mes, día de la semana), un nombre de zona horaria y una política de solapamiento: skip (no empezar mientras la ejecución anterior siga), queue o concurrent. Las tareas se ejecutan en orden y el primer fallo termina la ejecución: console_command, run_executable, power_action, create_backup, wait, como máximo 25. Es la forma más simple de tener una copia nocturna:

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/service-instances/$SERVICE/schedules" -H "$AUTH" -H "Content-Type: application/json" -d '{ "name": "Nightly backup", "cron_expression": "0 4 * * *", "timezone": "Europe/Madrid", "overlap_policy": "skip", "timeout_seconds": 3600, "action": {"kind": "tasks", "tasks": [{"kind": "create_backup"}]} }' curl -sS -X POST "$ZAROZ_API/service-instances/$SERVICE/schedules/$SCHEDULE/runs" -H "$AUTH" # ejecutarla ahora curl -sS "$ZAROZ_API/service-instances/$SERVICE/runs" -H "$AUTH"

TypeScript

TypeScriptCode
const s = await call("POST", `/service-instances/${SERVICE}/schedules`, { name: "Nightly backup", cron_expression: "0 4 * * *", timezone: "Europe/Madrid", overlap_policy: "skip", timeout_seconds: 3600, action: { kind: "tasks", tasks: [{ kind: "create_backup" }] }, }); console.log(s.body.next_fire_at, s.body.upcoming_fire_times);

Python

Code
_, s = call("POST", f"/service-instances/{SERVICE}/schedules", { "name": "Nightly backup", "cron_expression": "0 4 * * *", "timezone": "Europe/Madrid", "overlap_policy": "skip", "timeout_seconds": 3600, "action": {"kind": "tasks", "tasks": [{"kind": "create_backup"}]}, }) print(s["next_fire_at"], s["upcoming_fire_times"])

El planificador lee la fila nueva en unos diez segundos; la respuesta ya te da next_fire_at y los siguientes instantes, así que puedes comprobar tu expresión. Las programaciones de un servicio de tipo cron job usan {"kind": "run_job"} en lugar de tareas. Cada ejecución queda registrada (también las que no se ejecutaron): outcome es succeeded, failed, interrupted, skipped_overlap, skipped_condition o missed. Una programación puede llevar además conditions de guarda (una ventana horaria, una exclusión de calendario, la ejecución anterior, una comprobación HTTP, el éxito de otra programación) y estar paused. Consulta Copias de seguridad para saber qué hace una copia.

Actividad

TerminalCode
curl -sS "$ZAROZ_API/service-instances/$SERVICE/activity?page_size=25" -H "$AUTH"
TypeScriptCode
const a = await call("GET", `/service-instances/${SERVICE}/activity?page_size=25`);
Code
_, a = call("GET", f"/service-instances/{SERVICE}/activity?page_size=25")

Lo más reciente primero: lo que el propietario hizo en este servicio (y con qué persona o token). Los eventos internos de la plataforma se excluyen.

Compartir un servicio con alguien

Da acceso a una persona a un servicio sin hacerla miembro de la organización. Solo ve lo que concedes, no puede volver a compartirlo y nunca ve otros servicios ni los miembros de la organización. Los permisos son una lista de: console, files, power, settings, databases, database_browse, database_rows, database_schema, external_access, schedules, billing (ver el servicio va implícito en cualquier comparto). billing lee facturas, redimensiona y retira una cancelación programada; nunca cancela, renueva ni paga.

curl

TerminalCode
S=/service-instances/$SERVICE/shares curl -sS -X POST "$ZAROZ_API$S" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"email": "[email protected]", "permissions": ["console", "power"]}' curl -sS "$ZAROZ_API$S" -H "$AUTH" curl -sS -X PATCH "$ZAROZ_API$S/$SHARE" -H "$AUTH" -H "Content-Type: application/json" -d '{"permissions": ["console"]}' curl -sS -X DELETE "$ZAROZ_API$S/$SHARE" -H "$AUTH" curl -sS "$ZAROZ_API/me/shared-service-instances" -H "$AUTH" # lo compartido CONMIGO

TypeScript

TypeScriptCode
const share = await call("POST", `/service-instances/${SERVICE}/shares`, { email: "[email protected]", permissions: ["console", "power"], }); console.log(share.body.status); // "pending" hasta que abra "Compartido conmigo"

Python

Code
_, share = call("POST", f"/service-instances/{SERVICE}/shares", { "email": "[email protected]", "permissions": ["console", "power"], }) print(share["status"]) # "pending" hasta que abra "Compartido conmigo"

Compartir requiere el rol Owner o Admin y un producto que lo ofrezca (shareable en sus capacidades; si no, 404). La respuesta es el mismo comparto pending tenga o no cuenta esa dirección, y pasa a accepted cuando lo abre. Editar los permisos surte efecto en su siguiente petición. Rechazos: 409 already_shared, 409 share_limit_reached, 422 cannot_share_with_self y 429 pasadas 20 comparticiones nuevas por hora y servicio.

Last modified on