@keystrokehq/hosting 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docker.cjs +154 -0
- package/dist/docker.cjs.map +1 -0
- package/dist/docker.d.cts +23 -0
- package/dist/docker.d.cts.map +1 -0
- package/dist/docker.d.mts +23 -0
- package/dist/docker.d.mts.map +1 -0
- package/dist/docker.mjs +152 -0
- package/dist/docker.mjs.map +1 -0
- package/dist/index.cjs +135 -8422
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -122
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +68 -122
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +103 -8372
- package/dist/index.mjs.map +1 -1
- package/dist/org-artifacts.cjs +4 -0
- package/dist/org-artifacts.d.cts +2 -0
- package/dist/org-artifacts.d.mts +2 -0
- package/dist/org-artifacts.mjs +2 -0
- package/dist/plugin-4cuXkf42.d.cts +44 -0
- package/dist/plugin-4cuXkf42.d.cts.map +1 -0
- package/dist/plugin-eiAXqE7P.d.mts +44 -0
- package/dist/plugin-eiAXqE7P.d.mts.map +1 -0
- package/dist/runtime-BmfsHLrk.mjs +19 -0
- package/dist/runtime-BmfsHLrk.mjs.map +1 -0
- package/dist/runtime-CV7pSfMl.cjs +30 -0
- package/dist/runtime-CV7pSfMl.cjs.map +1 -0
- package/dist/runtime-Cp906goK.d.cts +88 -0
- package/dist/runtime-Cp906goK.d.cts.map +1 -0
- package/dist/runtime-Cp906goK.d.mts +88 -0
- package/dist/runtime-Cp906goK.d.mts.map +1 -0
- package/dist/wait-for-health-DM55_NQh.cjs +340 -0
- package/dist/wait-for-health-DM55_NQh.cjs.map +1 -0
- package/dist/wait-for-health-XEzPPQIO.mjs +209 -0
- package/dist/wait-for-health-XEzPPQIO.mjs.map +1 -0
- package/package.json +13 -3
package/dist/docker.cjs
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_wait_for_health = require("./wait-for-health-DM55_NQh.cjs");
|
|
3
|
+
let node_fs_promises = require("node:fs/promises");
|
|
4
|
+
//#region src/docker/create-docker-runtime.ts
|
|
5
|
+
const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
|
|
6
|
+
async function resolveDocker(options) {
|
|
7
|
+
if (options.docker) return options.docker;
|
|
8
|
+
const { default: DockerCtor } = await import("dockerode");
|
|
9
|
+
return new DockerCtor();
|
|
10
|
+
}
|
|
11
|
+
function createDockerRuntime(options = {}) {
|
|
12
|
+
return {
|
|
13
|
+
async start(input) {
|
|
14
|
+
if (input.projects.length === 0) throw new Error("Docker org runtime requires at least one project deploy");
|
|
15
|
+
const docker = await resolveDocker(options);
|
|
16
|
+
let launch = require_wait_for_health.resolveRuntimeLaunch(options, input.projects.map((project) => ({
|
|
17
|
+
projectId: project.projectId,
|
|
18
|
+
artifactStorageKey: project.artifactStorageKey,
|
|
19
|
+
artifactId: project.artifactId,
|
|
20
|
+
artifactVersion: project.artifactVersion
|
|
21
|
+
})), input.database, input.env);
|
|
22
|
+
if (options.workspacesMount) {
|
|
23
|
+
const { hostPath, containerPath } = options.workspacesMount;
|
|
24
|
+
await (0, node_fs_promises.mkdir)(hostPath, { recursive: true });
|
|
25
|
+
launch = {
|
|
26
|
+
...launch,
|
|
27
|
+
env: {
|
|
28
|
+
...launch.env,
|
|
29
|
+
KEYSTROKE_WORKSPACES_ROOT: containerPath
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
await ensureImage(docker, launch.image);
|
|
34
|
+
await removeExistingContainer(docker, input.organizationId);
|
|
35
|
+
const binds = [...options.workspacesMount ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`] : [], ...options.extraBinds ?? []];
|
|
36
|
+
const container = await docker.createContainer({
|
|
37
|
+
Image: launch.image,
|
|
38
|
+
name: containerName(input.organizationId),
|
|
39
|
+
Labels: {
|
|
40
|
+
"keystroke.organization.id": input.organizationId,
|
|
41
|
+
"keystroke.project.ids": input.projects.map((project) => project.projectId).join(",")
|
|
42
|
+
},
|
|
43
|
+
ExposedPorts: { [`${launch.port}/tcp`]: {} },
|
|
44
|
+
HostConfig: {
|
|
45
|
+
ExtraHosts: ["host.docker.internal:host-gateway"],
|
|
46
|
+
PortBindings: { [`${launch.port}/tcp`]: [{ HostPort: "0" }] },
|
|
47
|
+
...binds.length > 0 ? { Binds: binds } : {}
|
|
48
|
+
},
|
|
49
|
+
NetworkingConfig: { EndpointsConfig: { [MINIO_DOCKER_NETWORK]: {} } },
|
|
50
|
+
Env: require_wait_for_health.formatDockerEnv(launch.env)
|
|
51
|
+
});
|
|
52
|
+
await container.start();
|
|
53
|
+
const inspect = await container.inspect();
|
|
54
|
+
const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];
|
|
55
|
+
if (!binding?.HostPort) throw new Error("Failed to resolve org container host port");
|
|
56
|
+
const baseUrl = `http://127.0.0.1:${binding.HostPort}`;
|
|
57
|
+
await require_wait_for_health.waitForHealth(baseUrl, {
|
|
58
|
+
fetchImpl: options.fetchImpl,
|
|
59
|
+
timeoutMs: options.healthTimeoutMs
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
runtimeId: inspect.Id,
|
|
63
|
+
baseUrl
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
async destroy(input) {
|
|
67
|
+
try {
|
|
68
|
+
await (await resolveDocker(options)).getContainer(input.runtimeId).remove({ force: true });
|
|
69
|
+
} catch {}
|
|
70
|
+
},
|
|
71
|
+
async sleep(input) {
|
|
72
|
+
const container = (await resolveDocker(options)).getContainer(input.runtimeId);
|
|
73
|
+
const inspect = await container.inspect().catch(() => null);
|
|
74
|
+
if (!inspect) return;
|
|
75
|
+
if (inspect.State.Running) await container.stop({ t: 10 });
|
|
76
|
+
},
|
|
77
|
+
async wake(input) {
|
|
78
|
+
const container = (await resolveDocker(options)).getContainer(input.runtimeId);
|
|
79
|
+
if (!(await container.inspect()).State.Running) await container.start();
|
|
80
|
+
const refreshed = await container.inspect();
|
|
81
|
+
const ports = refreshed.NetworkSettings.Ports ?? {};
|
|
82
|
+
const binding = Object.values(ports).flat().find((entry) => entry?.HostPort)?.HostPort;
|
|
83
|
+
if (!binding) throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);
|
|
84
|
+
const baseUrl = `http://127.0.0.1:${binding}`;
|
|
85
|
+
await require_wait_for_health.waitForHealth(baseUrl, {
|
|
86
|
+
fetchImpl: options.fetchImpl,
|
|
87
|
+
timeoutMs: options.healthTimeoutMs
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
runtimeId: refreshed.Id,
|
|
91
|
+
baseUrl
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function containerName(organizationId) {
|
|
97
|
+
return `keystroke-org-${organizationId}`;
|
|
98
|
+
}
|
|
99
|
+
async function removeExistingContainer(docker, organizationId) {
|
|
100
|
+
try {
|
|
101
|
+
const existing = docker.getContainer(containerName(organizationId));
|
|
102
|
+
await existing.stop({ t: 5 }).catch(() => void 0);
|
|
103
|
+
await existing.remove({ force: true });
|
|
104
|
+
} catch {}
|
|
105
|
+
}
|
|
106
|
+
async function ensureImage(docker, image) {
|
|
107
|
+
try {
|
|
108
|
+
await docker.getImage(image).inspect();
|
|
109
|
+
return;
|
|
110
|
+
} catch {}
|
|
111
|
+
await new Promise((resolvePromise, reject) => {
|
|
112
|
+
docker.pull(image, (error, stream) => {
|
|
113
|
+
if (error) {
|
|
114
|
+
reject(error);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!stream) {
|
|
118
|
+
reject(/* @__PURE__ */ new Error(`Failed to pull image ${image}`));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
docker.modem.followProgress(stream, (progressError) => {
|
|
122
|
+
if (progressError) {
|
|
123
|
+
reject(progressError);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
resolvePromise();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/docker/plugin.ts
|
|
133
|
+
function dockerPlugin(options = {}) {
|
|
134
|
+
const env = options.env ?? process.env;
|
|
135
|
+
return {
|
|
136
|
+
name: "docker",
|
|
137
|
+
workerRuntime: require_wait_for_health.resolveWorkerRuntimeConfig(env),
|
|
138
|
+
createRuntime: () => createDockerRuntime({
|
|
139
|
+
...options,
|
|
140
|
+
env
|
|
141
|
+
})
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/default-plugin.ts
|
|
146
|
+
/** Local Docker hosting — default for platform and dev. */
|
|
147
|
+
function defaultHostingPlugin(options = {}) {
|
|
148
|
+
return dockerPlugin(options);
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.defaultHostingPlugin = defaultHostingPlugin;
|
|
152
|
+
exports.dockerPlugin = dockerPlugin;
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=docker.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.cjs","names":["resolveRuntimeLaunch","formatDockerEnv","waitForHealth","resolveWorkerRuntimeConfig"],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n\n async sleep(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect().catch(() => null);\n if (!inspect) {\n return;\n }\n if (inspect.State.Running) {\n await container.stop({ t: 10 });\n }\n },\n\n async wake(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect();\n if (!inspect.State.Running) {\n await container.start();\n }\n\n const refreshed = await container.inspect();\n const ports = refreshed.NetworkSettings.Ports ?? {};\n const binding = Object.values(ports)\n .flat()\n .find((entry) => entry?.HostPort)?.HostPort;\n if (!binding) {\n throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);\n }\n\n const baseUrl = `http://127.0.0.1:${binding}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: refreshed.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { n as HostingPlugin, t as HostingOptions } from "./plugin-4cuXkf42.cjs";
|
|
2
|
+
import Docker from "dockerode";
|
|
3
|
+
|
|
4
|
+
//#region src/docker/create-docker-runtime.d.ts
|
|
5
|
+
type WorkspacesMount = {
|
|
6
|
+
hostPath: string;
|
|
7
|
+
containerPath: string;
|
|
8
|
+
};
|
|
9
|
+
type DockerPluginOptions = HostingOptions & {
|
|
10
|
+
docker?: Docker; /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */
|
|
11
|
+
workspacesMount?: WorkspacesMount; /** Additional Docker bind mounts (`host:container[:options]`). */
|
|
12
|
+
extraBinds?: string[];
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/docker/plugin.d.ts
|
|
16
|
+
declare function dockerPlugin(options?: DockerPluginOptions): HostingPlugin;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/default-plugin.d.ts
|
|
19
|
+
/** Local Docker hosting — default for platform and dev. */
|
|
20
|
+
declare function defaultHostingPlugin(options?: DockerPluginOptions): HostingPlugin;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { type DockerPluginOptions, defaultHostingPlugin, dockerPlugin };
|
|
23
|
+
//# sourceMappingURL=docker.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.cts","names":[],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"mappings":";;;;KASY,eAAA;EACV,QAAA;EACA,aAAa;AAAA;AAAA,KAGH,mBAAA,GAAsB,cAAA;EAChC,MAAA,GAAS,MAAA,EADC;EAGV,eAAA,GAAkB,eAAA;EAElB,UAAA;AAAA;;;iBCXc,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;;iBCJ9D,oBAAA,CAAqB,OAAA,GAAS,mBAAA,GAA2B,aAAa"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { n as HostingPlugin, t as HostingOptions } from "./plugin-eiAXqE7P.mjs";
|
|
2
|
+
import Docker from "dockerode";
|
|
3
|
+
|
|
4
|
+
//#region src/docker/create-docker-runtime.d.ts
|
|
5
|
+
type WorkspacesMount = {
|
|
6
|
+
hostPath: string;
|
|
7
|
+
containerPath: string;
|
|
8
|
+
};
|
|
9
|
+
type DockerPluginOptions = HostingOptions & {
|
|
10
|
+
docker?: Docker; /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */
|
|
11
|
+
workspacesMount?: WorkspacesMount; /** Additional Docker bind mounts (`host:container[:options]`). */
|
|
12
|
+
extraBinds?: string[];
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/docker/plugin.d.ts
|
|
16
|
+
declare function dockerPlugin(options?: DockerPluginOptions): HostingPlugin;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/default-plugin.d.ts
|
|
19
|
+
/** Local Docker hosting — default for platform and dev. */
|
|
20
|
+
declare function defaultHostingPlugin(options?: DockerPluginOptions): HostingPlugin;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { type DockerPluginOptions, defaultHostingPlugin, dockerPlugin };
|
|
23
|
+
//# sourceMappingURL=docker.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.mts","names":[],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"mappings":";;;;KASY,eAAA;EACV,QAAA;EACA,aAAa;AAAA;AAAA,KAGH,mBAAA,GAAsB,cAAA;EAChC,MAAA,GAAS,MAAA,EADC;EAGV,eAAA,GAAkB,eAAA;EAElB,UAAA;AAAA;;;iBCXc,YAAA,CAAa,OAAA,GAAS,mBAAA,GAA2B,aAAa;;;;iBCJ9D,oBAAA,CAAqB,OAAA,GAAS,mBAAA,GAA2B,aAAa"}
|
package/dist/docker.mjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { a as formatDockerEnv, c as resolveRuntimeLaunch, h as resolveWorkerRuntimeConfig, t as waitForHealth } from "./wait-for-health-XEzPPQIO.mjs";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
//#region src/docker/create-docker-runtime.ts
|
|
4
|
+
const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
|
|
5
|
+
async function resolveDocker(options) {
|
|
6
|
+
if (options.docker) return options.docker;
|
|
7
|
+
const { default: DockerCtor } = await import("dockerode");
|
|
8
|
+
return new DockerCtor();
|
|
9
|
+
}
|
|
10
|
+
function createDockerRuntime(options = {}) {
|
|
11
|
+
return {
|
|
12
|
+
async start(input) {
|
|
13
|
+
if (input.projects.length === 0) throw new Error("Docker org runtime requires at least one project deploy");
|
|
14
|
+
const docker = await resolveDocker(options);
|
|
15
|
+
let launch = resolveRuntimeLaunch(options, input.projects.map((project) => ({
|
|
16
|
+
projectId: project.projectId,
|
|
17
|
+
artifactStorageKey: project.artifactStorageKey,
|
|
18
|
+
artifactId: project.artifactId,
|
|
19
|
+
artifactVersion: project.artifactVersion
|
|
20
|
+
})), input.database, input.env);
|
|
21
|
+
if (options.workspacesMount) {
|
|
22
|
+
const { hostPath, containerPath } = options.workspacesMount;
|
|
23
|
+
await mkdir(hostPath, { recursive: true });
|
|
24
|
+
launch = {
|
|
25
|
+
...launch,
|
|
26
|
+
env: {
|
|
27
|
+
...launch.env,
|
|
28
|
+
KEYSTROKE_WORKSPACES_ROOT: containerPath
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
await ensureImage(docker, launch.image);
|
|
33
|
+
await removeExistingContainer(docker, input.organizationId);
|
|
34
|
+
const binds = [...options.workspacesMount ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`] : [], ...options.extraBinds ?? []];
|
|
35
|
+
const container = await docker.createContainer({
|
|
36
|
+
Image: launch.image,
|
|
37
|
+
name: containerName(input.organizationId),
|
|
38
|
+
Labels: {
|
|
39
|
+
"keystroke.organization.id": input.organizationId,
|
|
40
|
+
"keystroke.project.ids": input.projects.map((project) => project.projectId).join(",")
|
|
41
|
+
},
|
|
42
|
+
ExposedPorts: { [`${launch.port}/tcp`]: {} },
|
|
43
|
+
HostConfig: {
|
|
44
|
+
ExtraHosts: ["host.docker.internal:host-gateway"],
|
|
45
|
+
PortBindings: { [`${launch.port}/tcp`]: [{ HostPort: "0" }] },
|
|
46
|
+
...binds.length > 0 ? { Binds: binds } : {}
|
|
47
|
+
},
|
|
48
|
+
NetworkingConfig: { EndpointsConfig: { [MINIO_DOCKER_NETWORK]: {} } },
|
|
49
|
+
Env: formatDockerEnv(launch.env)
|
|
50
|
+
});
|
|
51
|
+
await container.start();
|
|
52
|
+
const inspect = await container.inspect();
|
|
53
|
+
const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];
|
|
54
|
+
if (!binding?.HostPort) throw new Error("Failed to resolve org container host port");
|
|
55
|
+
const baseUrl = `http://127.0.0.1:${binding.HostPort}`;
|
|
56
|
+
await waitForHealth(baseUrl, {
|
|
57
|
+
fetchImpl: options.fetchImpl,
|
|
58
|
+
timeoutMs: options.healthTimeoutMs
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
runtimeId: inspect.Id,
|
|
62
|
+
baseUrl
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
async destroy(input) {
|
|
66
|
+
try {
|
|
67
|
+
await (await resolveDocker(options)).getContainer(input.runtimeId).remove({ force: true });
|
|
68
|
+
} catch {}
|
|
69
|
+
},
|
|
70
|
+
async sleep(input) {
|
|
71
|
+
const container = (await resolveDocker(options)).getContainer(input.runtimeId);
|
|
72
|
+
const inspect = await container.inspect().catch(() => null);
|
|
73
|
+
if (!inspect) return;
|
|
74
|
+
if (inspect.State.Running) await container.stop({ t: 10 });
|
|
75
|
+
},
|
|
76
|
+
async wake(input) {
|
|
77
|
+
const container = (await resolveDocker(options)).getContainer(input.runtimeId);
|
|
78
|
+
if (!(await container.inspect()).State.Running) await container.start();
|
|
79
|
+
const refreshed = await container.inspect();
|
|
80
|
+
const ports = refreshed.NetworkSettings.Ports ?? {};
|
|
81
|
+
const binding = Object.values(ports).flat().find((entry) => entry?.HostPort)?.HostPort;
|
|
82
|
+
if (!binding) throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);
|
|
83
|
+
const baseUrl = `http://127.0.0.1:${binding}`;
|
|
84
|
+
await waitForHealth(baseUrl, {
|
|
85
|
+
fetchImpl: options.fetchImpl,
|
|
86
|
+
timeoutMs: options.healthTimeoutMs
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
runtimeId: refreshed.Id,
|
|
90
|
+
baseUrl
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function containerName(organizationId) {
|
|
96
|
+
return `keystroke-org-${organizationId}`;
|
|
97
|
+
}
|
|
98
|
+
async function removeExistingContainer(docker, organizationId) {
|
|
99
|
+
try {
|
|
100
|
+
const existing = docker.getContainer(containerName(organizationId));
|
|
101
|
+
await existing.stop({ t: 5 }).catch(() => void 0);
|
|
102
|
+
await existing.remove({ force: true });
|
|
103
|
+
} catch {}
|
|
104
|
+
}
|
|
105
|
+
async function ensureImage(docker, image) {
|
|
106
|
+
try {
|
|
107
|
+
await docker.getImage(image).inspect();
|
|
108
|
+
return;
|
|
109
|
+
} catch {}
|
|
110
|
+
await new Promise((resolvePromise, reject) => {
|
|
111
|
+
docker.pull(image, (error, stream) => {
|
|
112
|
+
if (error) {
|
|
113
|
+
reject(error);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!stream) {
|
|
117
|
+
reject(/* @__PURE__ */ new Error(`Failed to pull image ${image}`));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
docker.modem.followProgress(stream, (progressError) => {
|
|
121
|
+
if (progressError) {
|
|
122
|
+
reject(progressError);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
resolvePromise();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/docker/plugin.ts
|
|
132
|
+
function dockerPlugin(options = {}) {
|
|
133
|
+
const env = options.env ?? process.env;
|
|
134
|
+
return {
|
|
135
|
+
name: "docker",
|
|
136
|
+
workerRuntime: resolveWorkerRuntimeConfig(env),
|
|
137
|
+
createRuntime: () => createDockerRuntime({
|
|
138
|
+
...options,
|
|
139
|
+
env
|
|
140
|
+
})
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/default-plugin.ts
|
|
145
|
+
/** Local Docker hosting — default for platform and dev. */
|
|
146
|
+
function defaultHostingPlugin(options = {}) {
|
|
147
|
+
return dockerPlugin(options);
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { defaultHostingPlugin, dockerPlugin };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=docker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.mjs","names":[],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n\n async sleep(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect().catch(() => null);\n if (!inspect) {\n return;\n }\n if (inspect.State.Running) {\n await container.stop({ t: 10 });\n }\n },\n\n async wake(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect();\n if (!inspect.State.Running) {\n await container.start();\n }\n\n const refreshed = await container.inspect();\n const ports = refreshed.NetworkSettings.Ports ?? {};\n const binding = Object.values(ports)\n .flat()\n .find((entry) => entry?.HostPort)?.HostPort;\n if (!binding) {\n throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);\n }\n\n const baseUrl = `http://127.0.0.1:${binding}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: refreshed.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
|