v3: machine config (#978)
* add and use machine config * assign tasks to worker nodes only * add secure flag to zod connection * changeset * add pre stop hook * don't use secure connection by default * pass more identifiers to provider and apply labels
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/sdk": patch
|
||||||
|
"@trigger.dev/core-apps": patch
|
||||||
|
"trigger.dev": patch
|
||||||
|
"@trigger.dev/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
add machine config and secure zod connection
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
HTTP_SERVER_PORT=8020
|
HTTP_SERVER_PORT=8020
|
||||||
PLATFORM_ENABLED=true
|
PLATFORM_ENABLED=true
|
||||||
PLATFORM_WS_PORT=3030
|
PLATFORM_WS_PORT=3030
|
||||||
|
SECURE_CONNECTION=false
|
||||||
@@ -26,6 +26,7 @@ const PLATFORM_ENABLED = ["1", "true"].includes(process.env.PLATFORM_ENABLED ??
|
|||||||
const PLATFORM_HOST = process.env.PLATFORM_HOST || "127.0.0.1";
|
const PLATFORM_HOST = process.env.PLATFORM_HOST || "127.0.0.1";
|
||||||
const PLATFORM_WS_PORT = process.env.PLATFORM_WS_PORT || 3030;
|
const PLATFORM_WS_PORT = process.env.PLATFORM_WS_PORT || 3030;
|
||||||
const PLATFORM_SECRET = process.env.PLATFORM_SECRET || "coordinator-secret";
|
const PLATFORM_SECRET = process.env.PLATFORM_SECRET || "coordinator-secret";
|
||||||
|
const SECURE_CONNECTION = ["1", "true"].includes(process.env.SECURE_CONNECTION ?? "false");
|
||||||
|
|
||||||
const logger = new SimpleLogger(`[${NODE_NAME}]`);
|
const logger = new SimpleLogger(`[${NODE_NAME}]`);
|
||||||
|
|
||||||
@@ -365,6 +366,7 @@ class TaskCoordinator {
|
|||||||
namespace: "coordinator",
|
namespace: "coordinator",
|
||||||
host: PLATFORM_HOST,
|
host: PLATFORM_HOST,
|
||||||
port: Number(PLATFORM_WS_PORT),
|
port: Number(PLATFORM_WS_PORT),
|
||||||
|
secure: SECURE_CONNECTION,
|
||||||
clientMessages: CoordinatorToPlatformMessages,
|
clientMessages: CoordinatorToPlatformMessages,
|
||||||
serverMessages: PlatformToCoordinatorMessages,
|
serverMessages: PlatformToCoordinatorMessages,
|
||||||
authToken: PLATFORM_SECRET,
|
authToken: PLATFORM_SECRET,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ HTTP_SERVER_PORT=8050
|
|||||||
|
|
||||||
PLATFORM_WS_PORT=3030
|
PLATFORM_WS_PORT=3030
|
||||||
PLATFORM_SECRET=provider-secret
|
PLATFORM_SECRET=provider-secret
|
||||||
|
SECURE_CONNECTION=false
|
||||||
|
|
||||||
# Use this if you are on macOS
|
# Use this if you are on macOS
|
||||||
# COORDINATOR_HOST="host.docker.internal"
|
# COORDINATOR_HOST="host.docker.internal"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
TaskOperationsIndexOptions,
|
TaskOperationsIndexOptions,
|
||||||
} from "@trigger.dev/core-apps";
|
} from "@trigger.dev/core-apps";
|
||||||
import { setTimeout } from "node:timers/promises";
|
import { setTimeout } from "node:timers/promises";
|
||||||
|
import { PostStartCauses, PreStopCauses } from "@trigger.dev/core/v3";
|
||||||
|
|
||||||
const MACHINE_NAME = process.env.MACHINE_NAME || "local";
|
const MACHINE_NAME = process.env.MACHINE_NAME || "local";
|
||||||
const COORDINATOR_PORT = process.env.COORDINATOR_PORT || 8020;
|
const COORDINATOR_PORT = process.env.COORDINATOR_PORT || 8020;
|
||||||
@@ -190,6 +191,9 @@ class DockerTaskOperations implements TaskOperations {
|
|||||||
async delete(opts: { runId: string }) {
|
async delete(opts: { runId: string }) {
|
||||||
await this.#initialize();
|
await this.#initialize();
|
||||||
|
|
||||||
|
const containerName = this.#getRunContainerName(opts.runId);
|
||||||
|
await this.#sendPreStop(containerName);
|
||||||
|
|
||||||
logger.log("noop: delete");
|
logger.log("noop: delete");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +212,26 @@ class DockerTaskOperations implements TaskOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #sendPostStart(containerName: string): Promise<void> {
|
async #sendPostStart(containerName: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const port = await this.#getHttpServerPort(containerName);
|
||||||
|
logger.debug(await this.#runLifecycleCommand(containerName, port, "postStart", "restore"));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("postStart error", { error });
|
||||||
|
throw new Error("postStart command failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #sendPreStop(containerName: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const port = await this.#getHttpServerPort(containerName);
|
||||||
|
logger.debug(await this.#runLifecycleCommand(containerName, port, "preStop", "terminate"));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("preStop error", { error });
|
||||||
|
throw new Error("preStop command failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #getHttpServerPort(containerName: string): Promise<number> {
|
||||||
// We first get the correct port, which is random during dev as we run with host networking and need to avoid clashes
|
// We first get the correct port, which is random during dev as we run with host networking and need to avoid clashes
|
||||||
// FIXME: Skip this in prod
|
// FIXME: Skip this in prod
|
||||||
const logs = logger.debug(await $`docker logs ${containerName}`);
|
const logs = logger.debug(await $`docker logs ${containerName}`);
|
||||||
@@ -219,19 +243,14 @@ class DockerTaskOperations implements TaskOperations {
|
|||||||
throw new Error("failed to extract port from logs");
|
throw new Error("failed to extract port from logs");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return port;
|
||||||
logger.debug(await this.#runLifecycleCommand(containerName, port, "postStart", "restore"));
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("postStart error", { error });
|
|
||||||
throw new Error("postStart command failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async #runLifecycleCommand(
|
async #runLifecycleCommand<THookType extends "postStart" | "preStop">(
|
||||||
containerName: string,
|
containerName: string,
|
||||||
port: number,
|
port: number,
|
||||||
type: "postStart" | "preStop",
|
type: THookType,
|
||||||
cause: "index" | "create" | "restore",
|
cause: THookType extends "postStart" ? PostStartCauses : PreStopCauses,
|
||||||
retryCount = 0
|
retryCount = 0
|
||||||
): Promise<ExecaChildProcess> {
|
): Promise<ExecaChildProcess> {
|
||||||
try {
|
try {
|
||||||
@@ -244,15 +263,15 @@ class DockerTaskOperations implements TaskOperations {
|
|||||||
`127.0.0.1:${port}/${type}?cause=${cause}`,
|
`127.0.0.1:${port}/${type}?cause=${cause}`,
|
||||||
]);
|
]);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (retryCount < 6) {
|
if (type === "postStart" && retryCount < 6) {
|
||||||
logger.debug("retriable postStart error", { retryCount, message: error?.message });
|
logger.debug(`retriable ${type} error`, { retryCount, message: error?.message });
|
||||||
await setTimeout(exponentialBackoff(retryCount + 1, 2, 50, 1150, 50));
|
await setTimeout(exponentialBackoff(retryCount + 1, 2, 50, 1150, 50));
|
||||||
|
|
||||||
return this.#runLifecycleCommand(containerName, port, type, cause, retryCount + 1);
|
return this.#runLifecycleCommand(containerName, port, type, cause, retryCount + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error("final postStart error", { message: error?.message });
|
logger.error(`final ${type} error`, { message: error?.message });
|
||||||
throw new Error(`postStart command failed after ${retryCount - 1} retries`);
|
throw new Error(`${type} command failed after ${retryCount - 1} retries`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ HTTP_SERVER_PORT=8060
|
|||||||
|
|
||||||
PLATFORM_WS_PORT=3030
|
PLATFORM_WS_PORT=3030
|
||||||
PLATFORM_SECRET=provider-secret
|
PLATFORM_SECRET=provider-secret
|
||||||
|
SECURE_CONNECTION=false
|
||||||
|
|
||||||
# Use this if you are on macOS
|
# Use this if you are on macOS
|
||||||
# COORDINATOR_HOST="host.docker.internal"
|
# COORDINATOR_HOST="host.docker.internal"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
TaskOperationsIndexOptions,
|
TaskOperationsIndexOptions,
|
||||||
TaskOperationsRestoreOptions,
|
TaskOperationsRestoreOptions,
|
||||||
} from "@trigger.dev/core-apps";
|
} from "@trigger.dev/core-apps";
|
||||||
|
import { Machine, PostStartCauses, PreStopCauses, EnvironmentType } from "@trigger.dev/core/v3";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
const RUNTIME_ENV = process.env.KUBERNETES_PORT ? "kubernetes" : "local";
|
const RUNTIME_ENV = process.env.KUBERNETES_PORT ? "kubernetes" : "local";
|
||||||
@@ -55,6 +56,12 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
metadata: {
|
metadata: {
|
||||||
labels: {
|
labels: {
|
||||||
app: "task-index",
|
app: "task-index",
|
||||||
|
"app.kubernetes.io/part-of": "trigger-worker",
|
||||||
|
"app.kubernetes.io/component": "index",
|
||||||
|
env: opts.envId,
|
||||||
|
envtype: this.#envTypeToLabelValue(opts.envType),
|
||||||
|
org: opts.orgId,
|
||||||
|
project: opts.projectId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
@@ -64,6 +71,9 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
name: "registry-trigger",
|
name: "registry-trigger",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
nodeSelector: {
|
||||||
|
nodetype: "worker",
|
||||||
|
},
|
||||||
containers: [
|
containers: [
|
||||||
{
|
{
|
||||||
name: this.#getIndexContainerName(opts.shortCode),
|
name: this.#getIndexContainerName(opts.shortCode),
|
||||||
@@ -79,6 +89,13 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
// memory: "50Mi",
|
// memory: "50Mi",
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
|
lifecycle: {
|
||||||
|
preStop: {
|
||||||
|
exec: {
|
||||||
|
command: this.#getLifecycleCommand("preStop", "terminate"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
env: [
|
env: [
|
||||||
{
|
{
|
||||||
name: "DEBUG",
|
name: "DEBUG",
|
||||||
@@ -151,6 +168,13 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
namespace: this.#namespace.metadata.name,
|
namespace: this.#namespace.metadata.name,
|
||||||
labels: {
|
labels: {
|
||||||
app: "task-run",
|
app: "task-run",
|
||||||
|
"app.kubernetes.io/part-of": "trigger-worker",
|
||||||
|
"app.kubernetes.io/component": "create",
|
||||||
|
env: opts.envId,
|
||||||
|
envtype: this.#envTypeToLabelValue(opts.envType),
|
||||||
|
org: opts.orgId,
|
||||||
|
project: opts.projectId,
|
||||||
|
run: opts.runId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
@@ -160,6 +184,9 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
name: "registry-trigger",
|
name: "registry-trigger",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
nodeSelector: {
|
||||||
|
nodetype: "worker",
|
||||||
|
},
|
||||||
containers: [
|
containers: [
|
||||||
{
|
{
|
||||||
name: this.#getRunContainerName(opts.runId),
|
name: this.#getRunContainerName(opts.runId),
|
||||||
@@ -169,9 +196,9 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
containerPort: 8000,
|
containerPort: 8000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// resources: {
|
resources: {
|
||||||
// limits: opts.machine,
|
limits: this.#getResourcesFromMachineConfig(opts.machine),
|
||||||
// },
|
},
|
||||||
lifecycle: {
|
lifecycle: {
|
||||||
postStart: {
|
postStart: {
|
||||||
exec: {
|
exec: {
|
||||||
@@ -180,7 +207,7 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
},
|
},
|
||||||
preStop: {
|
preStop: {
|
||||||
exec: {
|
exec: {
|
||||||
command: this.#getLifecycleCommand("preStop", "create"),
|
command: this.#getLifecycleCommand("preStop", "terminate"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -262,6 +289,14 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
namespace: this.#namespace.metadata.name,
|
namespace: this.#namespace.metadata.name,
|
||||||
labels: {
|
labels: {
|
||||||
app: "task-run",
|
app: "task-run",
|
||||||
|
"app.kubernetes.io/part-of": "trigger-worker",
|
||||||
|
"app.kubernetes.io/component": "restore",
|
||||||
|
env: opts.envId,
|
||||||
|
envtype: this.#envTypeToLabelValue(opts.envType),
|
||||||
|
org: opts.orgId,
|
||||||
|
project: opts.projectId,
|
||||||
|
run: opts.runId,
|
||||||
|
checkpoint: opts.checkpointId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
@@ -271,6 +306,9 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
name: "registry-trigger",
|
name: "registry-trigger",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
nodeSelector: {
|
||||||
|
nodetype: "worker",
|
||||||
|
},
|
||||||
initContainers: [
|
initContainers: [
|
||||||
{
|
{
|
||||||
name: "pull-base-image",
|
name: "pull-base-image",
|
||||||
@@ -309,9 +347,9 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
containerPort: 8000,
|
containerPort: 8000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// resources: {
|
resources: {
|
||||||
// limits: opts.machine,
|
limits: this.#getResourcesFromMachineConfig(opts.machine),
|
||||||
// },
|
},
|
||||||
lifecycle: {
|
lifecycle: {
|
||||||
postStart: {
|
postStart: {
|
||||||
exec: {
|
exec: {
|
||||||
@@ -320,7 +358,7 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
},
|
},
|
||||||
preStop: {
|
preStop: {
|
||||||
exec: {
|
exec: {
|
||||||
command: this.#getLifecycleCommand("preStop", "restore"),
|
command: this.#getLifecycleCommand("preStop", "terminate"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -355,7 +393,30 @@ class KubernetesTaskOperations implements TaskOperations {
|
|||||||
await this.#getPod(opts.runId, this.#namespace);
|
await this.#getPod(opts.runId, this.#namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getLifecycleCommand(type: "postStart" | "preStop", cause: "index" | "create" | "restore") {
|
#envTypeToLabelValue(type: EnvironmentType) {
|
||||||
|
switch (type) {
|
||||||
|
case "PRODUCTION":
|
||||||
|
return "prod";
|
||||||
|
case "STAGING":
|
||||||
|
return "stg";
|
||||||
|
case "DEVELOPMENT":
|
||||||
|
return "dev";
|
||||||
|
case "PREVIEW":
|
||||||
|
return "preview";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#getResourcesFromMachineConfig(config: Machine) {
|
||||||
|
return {
|
||||||
|
cpu: `${config.cpu}`,
|
||||||
|
memory: `${config.memory}G`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getLifecycleCommand<THookType extends "postStart" | "preStop">(
|
||||||
|
type: THookType,
|
||||||
|
cause: THookType extends "postStart" ? PostStartCauses : PreStopCauses
|
||||||
|
) {
|
||||||
return ["/bin/sh", "-c", `sleep 1; wget -q -O- 127.0.0.1:8000/${type}?cause=${cause}`];
|
return ["/bin/sh", "-c", `sleep 1; wget -q -O- 127.0.0.1:8000/${type}?cause=${cause}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Context, ROOT_CONTEXT, Span, SpanKind, context, trace } from "@opentelemetry/api";
|
import { Context, ROOT_CONTEXT, Span, SpanKind, context, trace } from "@opentelemetry/api";
|
||||||
import {
|
import {
|
||||||
|
Machine,
|
||||||
ProdTaskRunExecution,
|
ProdTaskRunExecution,
|
||||||
ProdTaskRunExecutionPayload,
|
ProdTaskRunExecutionPayload,
|
||||||
TaskRunError,
|
TaskRunError,
|
||||||
@@ -438,10 +439,27 @@ export class SharedQueueConsumer {
|
|||||||
queueId: queue.id,
|
queueId: queue.id,
|
||||||
runtimeEnvironmentId: environment.id,
|
runtimeEnvironmentId: environment.id,
|
||||||
},
|
},
|
||||||
|
include: {
|
||||||
|
backgroundWorkerTask: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isRetry = taskRunAttempt.number > 1;
|
const isRetry = taskRunAttempt.number > 1;
|
||||||
|
|
||||||
|
const { machineConfig } = taskRunAttempt.backgroundWorkerTask;
|
||||||
|
const machine = Machine.safeParse(machineConfig ?? {});
|
||||||
|
|
||||||
|
if (!machine.success) {
|
||||||
|
logger.error("Failed to parse machine config", {
|
||||||
|
queueMessage: message.data,
|
||||||
|
messageId: message.messageId,
|
||||||
|
attemptId: taskRunAttempt.id,
|
||||||
|
machineConfig,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.#ackAndDoMoreWork(message.messageId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (messageBody.data.checkpointEventId) {
|
if (messageBody.data.checkpointEventId) {
|
||||||
const restoreService = new RestoreCheckpointService();
|
const restoreService = new RestoreCheckpointService();
|
||||||
@@ -470,11 +488,16 @@ export class SharedQueueConsumer {
|
|||||||
backgroundWorkerId: deployment.worker.friendlyId,
|
backgroundWorkerId: deployment.worker.friendlyId,
|
||||||
data: {
|
data: {
|
||||||
type: "SCHEDULE_ATTEMPT",
|
type: "SCHEDULE_ATTEMPT",
|
||||||
id: taskRunAttempt.id,
|
|
||||||
image: deployment.imageReference,
|
image: deployment.imageReference,
|
||||||
envId: environment.id,
|
|
||||||
runId: taskRunAttempt.taskRunId,
|
|
||||||
version: deployment.version,
|
version: deployment.version,
|
||||||
|
machine: machine.data,
|
||||||
|
// identifiers
|
||||||
|
id: taskRunAttempt.id,
|
||||||
|
envId: environment.id,
|
||||||
|
envType: environment.type,
|
||||||
|
orgId: environment.organizationId,
|
||||||
|
projectId: environment.projectId,
|
||||||
|
runId: taskRunAttempt.taskRunId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ export async function createBackgroundTasks(
|
|||||||
exportName: task.exportName,
|
exportName: task.exportName,
|
||||||
retryConfig: task.retry,
|
retryConfig: task.retry,
|
||||||
queueConfig: task.queue,
|
queueConfig: task.queue,
|
||||||
|
machineConfig: task.machine,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,13 @@ export class IndexDeploymentService extends BaseService {
|
|||||||
version: "v1",
|
version: "v1",
|
||||||
shortCode: deployment.shortCode,
|
shortCode: deployment.shortCode,
|
||||||
imageTag: deployment.imageReference,
|
imageTag: deployment.imageReference,
|
||||||
envId: deployment.environmentId,
|
|
||||||
apiKey: deployment.environment.apiKey,
|
apiKey: deployment.environment.apiKey,
|
||||||
apiUrl: env.APP_ORIGIN,
|
apiUrl: env.APP_ORIGIN,
|
||||||
|
// identifiers
|
||||||
|
envId: deployment.environmentId,
|
||||||
|
envType: deployment.environment.type,
|
||||||
|
projectId: deployment.projectId,
|
||||||
|
orgId: deployment.environment.organizationId,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug("Index ACK received", { responses });
|
logger.debug("Index ACK received", { responses });
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { logger } from "~/services/logger.server";
|
|||||||
import { socketIo } from "../handleSocketIo.server";
|
import { socketIo } from "../handleSocketIo.server";
|
||||||
import { CreateCheckpointRestoreEventService } from "./createCheckpointRestoreEvent.server";
|
import { CreateCheckpointRestoreEventService } from "./createCheckpointRestoreEvent.server";
|
||||||
import { BaseService } from "./baseService.server";
|
import { BaseService } from "./baseService.server";
|
||||||
|
import { Machine } from "@trigger.dev/core/v3";
|
||||||
|
|
||||||
const RESTORABLE_RUN_STATUSES: TaskRunStatus[] = ["WAITING_TO_RESUME"];
|
const RESTORABLE_RUN_STATUSES: TaskRunStatus[] = ["WAITING_TO_RESUME"];
|
||||||
const RESTORABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["PAUSED"];
|
const RESTORABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["PAUSED"];
|
||||||
@@ -30,8 +31,14 @@ export class RestoreCheckpointService extends BaseService {
|
|||||||
attempt: {
|
attempt: {
|
||||||
select: {
|
select: {
|
||||||
status: true,
|
status: true,
|
||||||
|
backgroundWorkerTask: {
|
||||||
|
select: {
|
||||||
|
machineConfig: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
runtimeEnvironment: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -63,17 +70,34 @@ export class RestoreCheckpointService extends BaseService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { machineConfig } = checkpoint.attempt.backgroundWorkerTask;
|
||||||
|
const machine = Machine.safeParse(machineConfig ?? {});
|
||||||
|
|
||||||
|
if (!machine.success) {
|
||||||
|
logger.error("Failed to parse machine config", {
|
||||||
|
attemptId: checkpoint.attemptId,
|
||||||
|
machineConfig: checkpoint.attempt.backgroundWorkerTask.machineConfig,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const eventService = new CreateCheckpointRestoreEventService(this._prisma);
|
const eventService = new CreateCheckpointRestoreEventService(this._prisma);
|
||||||
await eventService.restore({ checkpointId: checkpoint.id });
|
await eventService.restore({ checkpointId: checkpoint.id });
|
||||||
|
|
||||||
socketIo.providerNamespace.emit("RESTORE", {
|
socketIo.providerNamespace.emit("RESTORE", {
|
||||||
version: "v1",
|
version: "v1",
|
||||||
checkpointId: checkpoint.id,
|
|
||||||
runId: checkpoint.runId,
|
|
||||||
type: checkpoint.type,
|
type: checkpoint.type,
|
||||||
location: checkpoint.location,
|
location: checkpoint.location,
|
||||||
reason: checkpoint.reason ?? undefined,
|
reason: checkpoint.reason ?? undefined,
|
||||||
imageRef: checkpoint.imageRef,
|
imageRef: checkpoint.imageRef,
|
||||||
|
machine: machine.data,
|
||||||
|
// identifiers
|
||||||
|
checkpointId: checkpoint.id,
|
||||||
|
envId: checkpoint.runtimeEnvironment.id,
|
||||||
|
envType: checkpoint.runtimeEnvironment.type,
|
||||||
|
orgId: checkpoint.runtimeEnvironment.organizationId,
|
||||||
|
projectId: checkpoint.runtimeEnvironment.projectId,
|
||||||
|
runId: checkpoint.runId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return checkpoint;
|
return checkpoint;
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ function getTasks(): Array<TaskMetadataWithFunctions> {
|
|||||||
filePath: (taskFile as any).filePath,
|
filePath: (taskFile as any).filePath,
|
||||||
queue: (task as any).__trigger.queue,
|
queue: (task as any).__trigger.queue,
|
||||||
retry: (task as any).__trigger.retry,
|
retry: (task as any).__trigger.retry,
|
||||||
|
machine: (task as any).__trigger.machine,
|
||||||
fns: (task as any).__trigger.fns,
|
fns: (task as any).__trigger.fns,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
CoordinatorToProdWorkerMessages,
|
CoordinatorToProdWorkerMessages,
|
||||||
|
PostStartCauses,
|
||||||
|
PreStopCauses,
|
||||||
ProdWorkerToCoordinatorMessages,
|
ProdWorkerToCoordinatorMessages,
|
||||||
TaskResource,
|
TaskResource,
|
||||||
WaitReason,
|
WaitReason,
|
||||||
@@ -567,23 +569,15 @@ class ProdWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "/preStop": {
|
case "/preStop": {
|
||||||
const schema = z.enum(["index", "create", "restore"]);
|
const cause = PreStopCauses.safeParse(url.searchParams.get("cause"));
|
||||||
|
|
||||||
const cause = schema.safeParse(url.searchParams.get("cause"));
|
|
||||||
|
|
||||||
if (!cause.success) {
|
if (!cause.success) {
|
||||||
logger.error("Failed to parse cause", { cause });
|
logger.error("Failed to parse cause", { cause });
|
||||||
return;
|
return reply.text("Failed to parse cause", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cause.data) {
|
switch (cause.data) {
|
||||||
case "index": {
|
case "terminate": {
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "create": {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "restore": {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -597,13 +591,11 @@ class ProdWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "/postStart": {
|
case "/postStart": {
|
||||||
const schema = z.enum(["index", "create", "restore"]);
|
const cause = PostStartCauses.safeParse(url.searchParams.get("cause"));
|
||||||
|
|
||||||
const cause = schema.safeParse(url.searchParams.get("cause"));
|
|
||||||
|
|
||||||
if (!cause.success) {
|
if (!cause.success) {
|
||||||
logger.error("Failed to parse cause", { cause });
|
logger.error("Failed to parse cause", { cause });
|
||||||
return;
|
return reply.text("Failed to parse cause", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cause.data) {
|
switch (cause.data) {
|
||||||
@@ -678,11 +670,7 @@ class ProdWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const task of this.#backgroundWorker.tasks) {
|
for (const task of this.#backgroundWorker.tasks) {
|
||||||
taskResources.push({
|
taskResources.push(task);
|
||||||
id: task.id,
|
|
||||||
filePath: task.filePath,
|
|
||||||
exportName: task.exportName,
|
|
||||||
});
|
|
||||||
|
|
||||||
packageVersion = task.packageVersion;
|
packageVersion = task.packageVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ function getTasks(): Array<TaskMetadataWithFunctions> {
|
|||||||
filePath: (taskFile as any).filePath,
|
filePath: (taskFile as any).filePath,
|
||||||
queue: (task as any).__trigger.queue,
|
queue: (task as any).__trigger.queue,
|
||||||
retry: (task as any).__trigger.retry,
|
retry: (task as any).__trigger.retry,
|
||||||
|
machine: (task as any).__trigger.machine,
|
||||||
fns: (task as any).__trigger.fns,
|
fns: (task as any).__trigger.fns,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createServer } from "node:http";
|
|||||||
import {
|
import {
|
||||||
ClientToSharedQueueMessages,
|
ClientToSharedQueueMessages,
|
||||||
clientWebsocketMessages,
|
clientWebsocketMessages,
|
||||||
|
EnvironmentType,
|
||||||
Machine,
|
Machine,
|
||||||
PlatformToProviderMessages,
|
PlatformToProviderMessages,
|
||||||
ProviderToPlatformMessages,
|
ProviderToPlatformMessages,
|
||||||
@@ -18,30 +19,46 @@ const MACHINE_NAME = process.env.MACHINE_NAME || "local";
|
|||||||
const PLATFORM_HOST = process.env.PLATFORM_HOST || "127.0.0.1";
|
const PLATFORM_HOST = process.env.PLATFORM_HOST || "127.0.0.1";
|
||||||
const PLATFORM_WS_PORT = process.env.PLATFORM_WS_PORT || 3030;
|
const PLATFORM_WS_PORT = process.env.PLATFORM_WS_PORT || 3030;
|
||||||
const PLATFORM_SECRET = process.env.PLATFORM_SECRET || "provider-secret";
|
const PLATFORM_SECRET = process.env.PLATFORM_SECRET || "provider-secret";
|
||||||
|
const SECURE_CONNECTION = ["1", "true"].includes(process.env.SECURE_CONNECTION ?? "false");
|
||||||
|
|
||||||
const logger = new SimpleLogger(`[${MACHINE_NAME}]`);
|
const logger = new SimpleLogger(`[${MACHINE_NAME}]`);
|
||||||
|
|
||||||
export interface TaskOperationsIndexOptions {
|
export interface TaskOperationsIndexOptions {
|
||||||
shortCode: string;
|
shortCode: string;
|
||||||
imageRef: string;
|
imageRef: string;
|
||||||
envId: string;
|
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
|
// identifiers
|
||||||
|
envId: string;
|
||||||
|
envType: EnvironmentType;
|
||||||
|
orgId: string;
|
||||||
|
projectId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskOperationsCreateOptions {
|
export interface TaskOperationsCreateOptions {
|
||||||
runId: string;
|
|
||||||
image: string;
|
image: string;
|
||||||
machine: Machine;
|
machine: Machine;
|
||||||
envId: string;
|
|
||||||
version: string;
|
version: string;
|
||||||
|
// identifiers
|
||||||
|
envId: string;
|
||||||
|
envType: EnvironmentType;
|
||||||
|
orgId: string;
|
||||||
|
projectId: string;
|
||||||
|
runId: string;
|
||||||
|
attemptId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskOperationsRestoreOptions {
|
export interface TaskOperationsRestoreOptions {
|
||||||
runId: string;
|
|
||||||
imageRef: string;
|
imageRef: string;
|
||||||
checkpointRef: string;
|
checkpointRef: string;
|
||||||
machine: Machine;
|
machine: Machine;
|
||||||
|
// identifiers
|
||||||
|
envId: string;
|
||||||
|
envType: EnvironmentType;
|
||||||
|
orgId: string;
|
||||||
|
projectId: string;
|
||||||
|
runId: string;
|
||||||
|
checkpointId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskOperations {
|
export interface TaskOperations {
|
||||||
@@ -87,6 +104,7 @@ export class ProviderShell implements Provider {
|
|||||||
namespace: "shared-queue",
|
namespace: "shared-queue",
|
||||||
host: PLATFORM_HOST,
|
host: PLATFORM_HOST,
|
||||||
port: Number(PLATFORM_WS_PORT),
|
port: Number(PLATFORM_WS_PORT),
|
||||||
|
secure: SECURE_CONNECTION,
|
||||||
clientMessages: ClientToSharedQueueMessages,
|
clientMessages: ClientToSharedQueueMessages,
|
||||||
serverMessages: SharedQueueToClientMessages,
|
serverMessages: SharedQueueToClientMessages,
|
||||||
authToken: PLATFORM_SECRET,
|
authToken: PLATFORM_SECRET,
|
||||||
@@ -101,11 +119,16 @@ export class ProviderShell implements Provider {
|
|||||||
if (message.data.type === "SCHEDULE_ATTEMPT") {
|
if (message.data.type === "SCHEDULE_ATTEMPT") {
|
||||||
try {
|
try {
|
||||||
this.tasks.create({
|
this.tasks.create({
|
||||||
envId: message.data.envId,
|
|
||||||
runId: message.data.runId,
|
|
||||||
image: message.data.image,
|
image: message.data.image,
|
||||||
machine: {},
|
machine: message.data.machine,
|
||||||
version: message.version,
|
version: message.data.version,
|
||||||
|
// identifiers
|
||||||
|
envId: message.data.envId,
|
||||||
|
envType: message.data.envType,
|
||||||
|
orgId: message.data.orgId,
|
||||||
|
projectId: message.data.projectId,
|
||||||
|
runId: message.data.runId,
|
||||||
|
attemptId: message.data.id,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("create failed", error);
|
logger.error("create failed", error);
|
||||||
@@ -138,6 +161,7 @@ export class ProviderShell implements Provider {
|
|||||||
namespace: "provider",
|
namespace: "provider",
|
||||||
host: PLATFORM_HOST,
|
host: PLATFORM_HOST,
|
||||||
port: Number(PLATFORM_WS_PORT),
|
port: Number(PLATFORM_WS_PORT),
|
||||||
|
secure: SECURE_CONNECTION,
|
||||||
clientMessages: ProviderToPlatformMessages,
|
clientMessages: ProviderToPlatformMessages,
|
||||||
serverMessages: PlatformToProviderMessages,
|
serverMessages: PlatformToProviderMessages,
|
||||||
authToken: PLATFORM_SECRET,
|
authToken: PLATFORM_SECRET,
|
||||||
@@ -165,9 +189,13 @@ export class ProviderShell implements Provider {
|
|||||||
await this.tasks.index({
|
await this.tasks.index({
|
||||||
shortCode: message.shortCode,
|
shortCode: message.shortCode,
|
||||||
imageRef: message.imageTag,
|
imageRef: message.imageTag,
|
||||||
envId: message.envId,
|
|
||||||
apiKey: message.apiKey,
|
apiKey: message.apiKey,
|
||||||
apiUrl: message.apiUrl,
|
apiUrl: message.apiUrl,
|
||||||
|
// identifiers
|
||||||
|
envId: message.envId,
|
||||||
|
envType: message.envType,
|
||||||
|
orgId: message.orgId,
|
||||||
|
projectId: message.projectId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("index failed", error);
|
logger.error("index failed", error);
|
||||||
@@ -206,13 +234,16 @@ export class ProviderShell implements Provider {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.tasks.restore({
|
await this.tasks.restore({
|
||||||
runId: message.runId,
|
|
||||||
checkpointRef: message.location,
|
checkpointRef: message.location,
|
||||||
machine: {
|
machine: message.machine,
|
||||||
cpu: "1",
|
|
||||||
memory: "100Mi",
|
|
||||||
},
|
|
||||||
imageRef: message.imageRef,
|
imageRef: message.imageRef,
|
||||||
|
// identifiers
|
||||||
|
envId: message.envId,
|
||||||
|
envType: message.envType,
|
||||||
|
orgId: message.orgId,
|
||||||
|
projectId: message.projectId,
|
||||||
|
runId: message.runId,
|
||||||
|
checkpointId: message.checkpointId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("restore failed", error);
|
logger.error("restore failed", error);
|
||||||
@@ -230,54 +261,34 @@ export class ProviderShell implements Provider {
|
|||||||
|
|
||||||
const reply = new HttpReply(res);
|
const reply = new HttpReply(res);
|
||||||
|
|
||||||
switch (req.url) {
|
try {
|
||||||
case "/health": {
|
const url = new URL(req.url ?? "", `http://${req.headers.host}`);
|
||||||
return reply.text("ok");
|
|
||||||
}
|
|
||||||
case "/whoami": {
|
|
||||||
return reply.text(`${MACHINE_NAME}`);
|
|
||||||
}
|
|
||||||
case "/close": {
|
|
||||||
this.#platformSocket.close();
|
|
||||||
return reply.text("platform socket closed");
|
|
||||||
}
|
|
||||||
case "/delete": {
|
|
||||||
const body = await getTextBody(req);
|
|
||||||
|
|
||||||
await this.tasks.delete({ runId: body });
|
switch (url.pathname) {
|
||||||
|
case "/health": {
|
||||||
|
return reply.text("ok");
|
||||||
|
}
|
||||||
|
case "/whoami": {
|
||||||
|
return reply.text(`${MACHINE_NAME}`);
|
||||||
|
}
|
||||||
|
case "/close": {
|
||||||
|
this.#platformSocket.close();
|
||||||
|
return reply.text("platform socket closed");
|
||||||
|
}
|
||||||
|
case "/delete": {
|
||||||
|
const body = await getTextBody(req);
|
||||||
|
|
||||||
return reply.text(`sent delete request: ${body}`);
|
await this.tasks.delete({ runId: body });
|
||||||
}
|
|
||||||
case "/invoke": {
|
return reply.text(`sent delete request: ${body}`);
|
||||||
const body = await getTextBody(req);
|
}
|
||||||
|
default: {
|
||||||
await this.tasks.create({
|
return reply.empty(404);
|
||||||
envId: "placeholder",
|
}
|
||||||
image: body,
|
|
||||||
machine: {
|
|
||||||
cpu: "1",
|
|
||||||
memory: "100Mi",
|
|
||||||
},
|
|
||||||
runId: "<missing>",
|
|
||||||
version: "<missing>",
|
|
||||||
});
|
|
||||||
|
|
||||||
return reply.text(`sent restore request: ${body}`);
|
|
||||||
}
|
|
||||||
case "/restore": {
|
|
||||||
const body = await getTextBody(req);
|
|
||||||
|
|
||||||
const items = body.split("&");
|
|
||||||
const image = items[0];
|
|
||||||
const baseImageTag = items[1] ?? image;
|
|
||||||
|
|
||||||
// await this.tasks.restore({});
|
|
||||||
|
|
||||||
return reply.text(`sent restore request: ${body}`);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return reply.empty(404);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("HTTP server error", { error });
|
||||||
|
reply.empty(500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { TaskRunExecution, TaskRunExecutionResult } from "./common";
|
import { TaskRunExecution, TaskRunExecutionResult } from "./common";
|
||||||
|
|
||||||
|
export const EnvironmentType = z.enum(["PRODUCTION", "STAGING", "DEVELOPMENT", "PREVIEW"])
|
||||||
|
export type EnvironmentType = z.infer<typeof EnvironmentType>;
|
||||||
|
|
||||||
|
export const MachineCpu = z
|
||||||
|
.union([z.literal(0.25), z.literal(0.5), z.literal(1), z.literal(2), z.literal(4)])
|
||||||
|
.default(0.5);
|
||||||
|
|
||||||
|
export type MachineCpu = z.infer<typeof MachineCpu>;
|
||||||
|
|
||||||
|
export const MachineMemory = z
|
||||||
|
.union([z.literal(0.25), z.literal(0.5), z.literal(1), z.literal(2), z.literal(4), z.literal(8)])
|
||||||
|
.default(1);
|
||||||
|
|
||||||
|
export type MachineMemory = z.infer<typeof MachineMemory>;
|
||||||
|
|
||||||
|
export const Machine = z.object({
|
||||||
|
version: z.literal("v1").default("v1"),
|
||||||
|
cpu: MachineCpu,
|
||||||
|
memory: MachineMemory,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Machine = z.infer<typeof Machine>;
|
||||||
|
|
||||||
export const TaskRunExecutionPayload = z.object({
|
export const TaskRunExecutionPayload = z.object({
|
||||||
execution: TaskRunExecution,
|
execution: TaskRunExecution,
|
||||||
traceContext: z.record(z.unknown()),
|
traceContext: z.record(z.unknown()),
|
||||||
@@ -39,11 +62,16 @@ export const BackgroundWorkerServerMessages = z.discriminatedUnion("type", [
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
type: z.literal("SCHEDULE_ATTEMPT"),
|
type: z.literal("SCHEDULE_ATTEMPT"),
|
||||||
id: z.string(),
|
|
||||||
image: z.string(),
|
image: z.string(),
|
||||||
envId: z.string(),
|
|
||||||
runId: z.string(),
|
|
||||||
version: z.string(),
|
version: z.string(),
|
||||||
|
machine: Machine,
|
||||||
|
// identifiers
|
||||||
|
id: z.string(), // attempt
|
||||||
|
envId: z.string(),
|
||||||
|
envType: EnvironmentType,
|
||||||
|
orgId: z.string(),
|
||||||
|
projectId: z.string(),
|
||||||
|
runId: z.string(),
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -229,6 +257,7 @@ export const TaskMetadata = z.object({
|
|||||||
packageVersion: z.string(),
|
packageVersion: z.string(),
|
||||||
queue: QueueOptions.optional(),
|
queue: QueueOptions.optional(),
|
||||||
retry: RetryOptions.optional(),
|
retry: RetryOptions.optional(),
|
||||||
|
machine: Machine.partial().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TaskMetadata = z.infer<typeof TaskMetadata>;
|
export type TaskMetadata = z.infer<typeof TaskMetadata>;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { QueueOptions, RetryOptions } from "./messages";
|
import { Machine, QueueOptions, RetryOptions } from "./messages";
|
||||||
|
|
||||||
export const TaskResource = z.object({
|
export const TaskResource = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
@@ -7,6 +7,7 @@ export const TaskResource = z.object({
|
|||||||
exportName: z.string(),
|
exportName: z.string(),
|
||||||
queue: QueueOptions.optional(),
|
queue: QueueOptions.optional(),
|
||||||
retry: RetryOptions.optional(),
|
retry: RetryOptions.optional(),
|
||||||
|
machine: Machine.partial().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TaskResource = z.infer<typeof TaskResource>;
|
export type TaskResource = z.infer<typeof TaskResource>;
|
||||||
|
|||||||
@@ -7,9 +7,17 @@ import {
|
|||||||
ProdTaskRunExecution,
|
ProdTaskRunExecution,
|
||||||
ProdTaskRunExecutionPayload,
|
ProdTaskRunExecutionPayload,
|
||||||
RetryOptions,
|
RetryOptions,
|
||||||
|
Machine,
|
||||||
|
EnvironmentType,
|
||||||
} from "./messages";
|
} from "./messages";
|
||||||
import { TaskResource } from "./resources";
|
import { TaskResource } from "./resources";
|
||||||
|
|
||||||
|
export const PostStartCauses = z.enum(["index", "create", "restore"]);
|
||||||
|
export type PostStartCauses = z.infer<typeof PostStartCauses>;
|
||||||
|
|
||||||
|
export const PreStopCauses = z.enum(["terminate"]);
|
||||||
|
export type PreStopCauses = z.infer<typeof PreStopCauses>;
|
||||||
|
|
||||||
const RegexSchema = z.custom<RegExp>((val) => {
|
const RegexSchema = z.custom<RegExp>((val) => {
|
||||||
try {
|
try {
|
||||||
// Check to see if val is a regex
|
// Check to see if val is a regex
|
||||||
@@ -42,13 +50,6 @@ export type ResolvedConfig = RequireKeys<
|
|||||||
"triggerDirectories" | "triggerUrl" | "projectDir" | "tsconfigPath"
|
"triggerDirectories" | "triggerUrl" | "projectDir" | "tsconfigPath"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const Machine = z.object({
|
|
||||||
cpu: z.string().default("1").optional(),
|
|
||||||
memory: z.string().default("500Mi").optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type Machine = z.infer<typeof Machine>;
|
|
||||||
|
|
||||||
export const WaitReason = z.enum(["WAIT_FOR_DURATION", "WAIT_FOR_TASK", "WAIT_FOR_BATCH"]);
|
export const WaitReason = z.enum(["WAIT_FOR_DURATION", "WAIT_FOR_TASK", "WAIT_FOR_BATCH"]);
|
||||||
|
|
||||||
export type WaitReason = z.infer<typeof WaitReason>;
|
export type WaitReason = z.infer<typeof WaitReason>;
|
||||||
@@ -85,9 +86,13 @@ export const PlatformToProviderMessages = {
|
|||||||
version: z.literal("v1").default("v1"),
|
version: z.literal("v1").default("v1"),
|
||||||
imageTag: z.string(),
|
imageTag: z.string(),
|
||||||
shortCode: z.string(),
|
shortCode: z.string(),
|
||||||
envId: z.string(),
|
|
||||||
apiKey: z.string(),
|
apiKey: z.string(),
|
||||||
apiUrl: z.string(),
|
apiUrl: z.string(),
|
||||||
|
// identifiers
|
||||||
|
envId: z.string(),
|
||||||
|
envType: EnvironmentType,
|
||||||
|
orgId: z.string(),
|
||||||
|
projectId: z.string(),
|
||||||
}),
|
}),
|
||||||
callback: z.discriminatedUnion("success", [
|
callback: z.discriminatedUnion("success", [
|
||||||
z.object({
|
z.object({
|
||||||
@@ -103,22 +108,22 @@ export const PlatformToProviderMessages = {
|
|||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
},
|
},
|
||||||
INVOKE: {
|
// TODO: this should be a shared queue message instead
|
||||||
message: z.object({
|
|
||||||
version: z.literal("v1").default("v1"),
|
|
||||||
name: z.string(),
|
|
||||||
machine: Machine,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
RESTORE: {
|
RESTORE: {
|
||||||
message: z.object({
|
message: z.object({
|
||||||
version: z.literal("v1").default("v1"),
|
version: z.literal("v1").default("v1"),
|
||||||
checkpointId: z.string(),
|
|
||||||
runId: z.string(),
|
|
||||||
type: z.enum(["DOCKER", "KUBERNETES"]),
|
type: z.enum(["DOCKER", "KUBERNETES"]),
|
||||||
location: z.string(),
|
location: z.string(),
|
||||||
reason: z.string().optional(),
|
reason: z.string().optional(),
|
||||||
imageRef: z.string(),
|
imageRef: z.string(),
|
||||||
|
machine: Machine,
|
||||||
|
// identifiers
|
||||||
|
checkpointId: z.string(),
|
||||||
|
envId: z.string(),
|
||||||
|
envType: EnvironmentType,
|
||||||
|
orgId: z.string(),
|
||||||
|
projectId: z.string(),
|
||||||
|
runId: z.string(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
DELETE: {
|
DELETE: {
|
||||||
|
|||||||
@@ -263,7 +263,8 @@ interface ZodSocketConnectionOptions<
|
|||||||
TServerMessages extends ZodSocketMessageCatalogSchema,
|
TServerMessages extends ZodSocketMessageCatalogSchema,
|
||||||
> {
|
> {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port?: number;
|
||||||
|
secure?: boolean;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
clientMessages: TClientMessages;
|
clientMessages: TClientMessages;
|
||||||
serverMessages: TServerMessages;
|
serverMessages: TServerMessages;
|
||||||
@@ -302,15 +303,24 @@ export class ZodSocketConnection<
|
|||||||
#logger: StructuredLogger;
|
#logger: StructuredLogger;
|
||||||
|
|
||||||
constructor(opts: ZodSocketConnectionOptions<TClientMessages, TServerMessages>) {
|
constructor(opts: ZodSocketConnectionOptions<TClientMessages, TServerMessages>) {
|
||||||
this.socket = io(`ws://${opts.host}:${opts.port}/${opts.namespace}`, {
|
const uri = `${opts.secure ? "wss" : "ws"}://${opts.host}:${
|
||||||
|
opts.port ?? (opts.secure ? "443" : "80")
|
||||||
|
}/${opts.namespace}`;
|
||||||
|
|
||||||
|
const logger = new SimpleStructuredLogger(opts.namespace, LogLevel.info);
|
||||||
|
logger.log("new zod socket", { uri });
|
||||||
|
|
||||||
|
this.socket = io(uri, {
|
||||||
transports: ["websocket"],
|
transports: ["websocket"],
|
||||||
auth: {
|
auth: {
|
||||||
token: opts.authToken,
|
token: opts.authToken,
|
||||||
},
|
},
|
||||||
extraHeaders: opts.extraHeaders,
|
extraHeaders: opts.extraHeaders,
|
||||||
|
reconnectionDelay: 500,
|
||||||
|
reconnectionDelayMax: 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#logger = new SimpleStructuredLogger(opts.namespace, LogLevel.info, {
|
this.#logger = logger.child({
|
||||||
socketId: this.socket.id,
|
socketId: this.socket.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "BackgroundWorkerTask" ADD COLUMN "machineConfig" JSONB;
|
||||||
@@ -1559,6 +1559,7 @@ model BackgroundWorkerTask {
|
|||||||
|
|
||||||
queueConfig Json?
|
queueConfig Json?
|
||||||
retryConfig Json?
|
retryConfig Json?
|
||||||
|
machineConfig Json?
|
||||||
|
|
||||||
@@unique([workerId, slug])
|
@@unique([workerId, slug])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
HandleErrorResult,
|
HandleErrorResult,
|
||||||
InitFnParams,
|
InitFnParams,
|
||||||
InitOutput,
|
InitOutput,
|
||||||
|
MachineCpu,
|
||||||
|
MachineMemory,
|
||||||
MiddlewareFnParams,
|
MiddlewareFnParams,
|
||||||
QueueOptions,
|
QueueOptions,
|
||||||
RetryOptions,
|
RetryOptions,
|
||||||
@@ -114,8 +116,8 @@ export type TaskOptions<TPayload, TOutput = any, TInitOutput extends InitOutput
|
|||||||
* - 2
|
* - 2
|
||||||
* - 4
|
* - 4
|
||||||
*/
|
*/
|
||||||
cpu?: 0.25 | 0.5 | 1 | 2 | 4;
|
cpu?: MachineCpu;
|
||||||
/** In GBs of RAM. The default is 0.5.
|
/** In GBs of RAM. The default is 1.
|
||||||
*
|
*
|
||||||
* Possible values:
|
* Possible values:
|
||||||
* - 0.25
|
* - 0.25
|
||||||
@@ -125,7 +127,7 @@ export type TaskOptions<TPayload, TOutput = any, TInitOutput extends InitOutput
|
|||||||
* - 4
|
* - 4
|
||||||
* - 8
|
* - 8
|
||||||
*/
|
*/
|
||||||
memory?: 0.25 | 0.5 | 1 | 2 | 4 | 8;
|
memory?: MachineMemory;
|
||||||
};
|
};
|
||||||
/** This gets called when a task is triggered. It's where you put the code you want to execute.
|
/** This gets called when a task is triggered. It's where you put the code you want to execute.
|
||||||
*
|
*
|
||||||
@@ -489,6 +491,7 @@ export function createTask<TInput, TOutput, TInitOutput extends InitOutput>(
|
|||||||
packageVersion: packageJson.version,
|
packageVersion: packageJson.version,
|
||||||
queue: params.queue,
|
queue: params.queue,
|
||||||
retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined,
|
retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined,
|
||||||
|
machine: params.machine,
|
||||||
fns: {
|
fns: {
|
||||||
run: params.run,
|
run: params.run,
|
||||||
init: params.init,
|
init: params.init,
|
||||||
|
|||||||
Reference in New Issue
Block a user