mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: add .env docker stack
This commit is contained in:
parent
f7a29accb1
commit
009859faa9
@ -211,12 +211,12 @@ const Service = (
|
|||||||
<TabsList
|
<TabsList
|
||||||
className={cn(
|
className={cn(
|
||||||
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
||||||
data?.serverId ? "md:grid-cols-6" : "md:grid-cols-7",
|
data?.serverId ? "md:grid-cols-7" : "md:grid-cols-7",
|
||||||
data?.composeType === "docker-compose"
|
data?.composeType === "docker-compose"
|
||||||
? ""
|
? ""
|
||||||
: "md:grid-cols-6",
|
: "md:grid-cols-7",
|
||||||
data?.serverId && data?.composeType === "stack"
|
data?.serverId && data?.composeType === "stack"
|
||||||
? "md:grid-cols-5"
|
? "md:grid-cols-6"
|
||||||
: "",
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -1,99 +1,105 @@
|
|||||||
import {
|
import {
|
||||||
createWriteStream,
|
createWriteStream,
|
||||||
existsSync,
|
existsSync,
|
||||||
mkdirSync,
|
mkdirSync,
|
||||||
writeFileSync,
|
readFileSync,
|
||||||
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import boxen from "boxen";
|
import boxen from "boxen";
|
||||||
import {
|
import {
|
||||||
writeDomainsToCompose,
|
writeDomainsToCompose,
|
||||||
writeDomainsToComposeRemote,
|
writeDomainsToComposeRemote,
|
||||||
} from "../docker/domain";
|
} from "../docker/domain";
|
||||||
import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
|
import {
|
||||||
|
encodeBase64,
|
||||||
|
getEnviromentVariablesObject,
|
||||||
|
prepareEnvironmentVariables,
|
||||||
|
} from "../docker/utils";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
export type ComposeNested = InferResultType<
|
export type ComposeNested = InferResultType<
|
||||||
"compose",
|
"compose",
|
||||||
{ project: true; mounts: true; domains: true }
|
{ project: true; mounts: true; domains: true }
|
||||||
>;
|
>;
|
||||||
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const { sourceType, appName, mounts, composeType, domains } = compose;
|
const { sourceType, appName, mounts, composeType, domains } = compose;
|
||||||
try {
|
try {
|
||||||
const { COMPOSE_PATH } = paths();
|
const { COMPOSE_PATH } = paths();
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
await writeDomainsToCompose(compose, domains);
|
await writeDomainsToCompose(compose, domains);
|
||||||
createEnvFile(compose);
|
createEnvFile(compose);
|
||||||
await processComposeFile(compose);
|
|
||||||
|
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
Build Compose 🐳
|
Build Compose 🐳
|
||||||
Detected: ${mounts.length} mounts 📂
|
Detected: ${mounts.length} mounts 📂
|
||||||
Command: docker ${command}
|
Command: docker ${command}
|
||||||
Source Type: docker ${sourceType} ✅
|
Source Type: docker ${sourceType} ✅
|
||||||
Compose Type: ${composeType} ✅`;
|
Compose Type: ${composeType} ✅`;
|
||||||
const logBox = boxen(logContent, {
|
const logBox = boxen(logContent, {
|
||||||
padding: {
|
padding: {
|
||||||
left: 1,
|
left: 1,
|
||||||
right: 1,
|
right: 1,
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
},
|
},
|
||||||
width: 80,
|
width: 80,
|
||||||
borderStyle: "double",
|
borderStyle: "double",
|
||||||
});
|
});
|
||||||
writeStream.write(`\n${logBox}\n`);
|
writeStream.write(`\n${logBox}\n`);
|
||||||
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
|
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
await spawnAsync(
|
||||||
|
"docker",
|
||||||
|
[...command.split(" ")],
|
||||||
|
(data) => {
|
||||||
|
if (writeStream.writable) {
|
||||||
|
writeStream.write(data.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cwd: projectPath,
|
||||||
|
env: {
|
||||||
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
|
PATH: process.env.PATH,
|
||||||
|
...(composeType === "stack" && {
|
||||||
|
...getEnviromentVariablesObject(compose.env, compose.project.env),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await spawnAsync(
|
writeStream.write("Docker Compose Deployed: ✅");
|
||||||
"docker",
|
} catch (error) {
|
||||||
[...command.split(" ")],
|
writeStream.write(`Error ❌ ${(error as Error).message}`);
|
||||||
(data) => {
|
throw error;
|
||||||
if (writeStream.writable) {
|
} finally {
|
||||||
writeStream.write(data.toString());
|
writeStream.end();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
cwd: projectPath,
|
|
||||||
env: {
|
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
|
||||||
PATH: process.env.PATH,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
writeStream.write("Docker Compose Deployed: ✅");
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`Error ❌ ${(error as Error).message}`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBuildComposeCommand = async (
|
export const getBuildComposeCommand = async (
|
||||||
compose: ComposeNested,
|
compose: ComposeNested,
|
||||||
logPath: string,
|
logPath: string
|
||||||
) => {
|
) => {
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
||||||
compose;
|
compose;
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
const envCommand = getCreateEnvFileCommand(compose);
|
const envCommand = getCreateEnvFileCommand(compose);
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
const processComposeFileCommand = getProcessComposeFileCommand(compose);
|
const exportEnvCommand = getExportEnvCommand(compose);
|
||||||
|
|
||||||
const newCompose = await writeDomainsToComposeRemote(
|
const newCompose = await writeDomainsToComposeRemote(
|
||||||
compose,
|
compose,
|
||||||
domains,
|
domains,
|
||||||
logPath,
|
logPath
|
||||||
);
|
);
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
Build Compose 🐳
|
Build Compose 🐳
|
||||||
Detected: ${mounts.length} mounts 📂
|
Detected: ${mounts.length} mounts 📂
|
||||||
@ -101,17 +107,17 @@ Command: docker ${command}
|
|||||||
Source Type: docker ${sourceType} ✅
|
Source Type: docker ${sourceType} ✅
|
||||||
Compose Type: ${composeType} ✅`;
|
Compose Type: ${composeType} ✅`;
|
||||||
|
|
||||||
const logBox = boxen(logContent, {
|
const logBox = boxen(logContent, {
|
||||||
padding: {
|
padding: {
|
||||||
left: 1,
|
left: 1,
|
||||||
right: 1,
|
right: 1,
|
||||||
bottom: 1,
|
bottom: 1,
|
||||||
},
|
},
|
||||||
width: 80,
|
width: 80,
|
||||||
borderStyle: "double",
|
borderStyle: "double",
|
||||||
});
|
});
|
||||||
|
|
||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
set -e
|
set -e
|
||||||
{
|
{
|
||||||
echo "${logBox}" >> "${logPath}"
|
echo "${logBox}" >> "${logPath}"
|
||||||
@ -122,7 +128,7 @@ Compose Type: ${composeType} ✅`;
|
|||||||
|
|
||||||
cd "${projectPath}";
|
cd "${projectPath}";
|
||||||
|
|
||||||
${processComposeFileCommand}
|
${exportEnvCommand}
|
||||||
|
|
||||||
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
||||||
|
|
||||||
@ -133,129 +139,102 @@ Compose Type: ${composeType} ✅`;
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return await execAsyncRemote(compose.serverId, bashCommand);
|
return await execAsyncRemote(compose.serverId, bashCommand);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sanitizeCommand = (command: string) => {
|
const sanitizeCommand = (command: string) => {
|
||||||
const sanitizedCommand = command.trim();
|
const sanitizedCommand = command.trim();
|
||||||
|
|
||||||
const parts = sanitizedCommand.split(/\s+/);
|
const parts = sanitizedCommand.split(/\s+/);
|
||||||
|
|
||||||
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
|
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
|
||||||
|
|
||||||
return restCommand.join(" ");
|
return restCommand.join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createCommand = (compose: ComposeNested) => {
|
export const createCommand = (compose: ComposeNested) => {
|
||||||
const { composeType, appName, sourceType } = compose;
|
const { composeType, appName, sourceType } = compose;
|
||||||
|
|
||||||
if (compose.command) {
|
if (compose.command) {
|
||||||
return `${sanitizeCommand(compose.command)}`;
|
return `${sanitizeCommand(compose.command)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path =
|
const path =
|
||||||
sourceType === "raw"
|
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
||||||
? composeType === "stack"
|
let command = "";
|
||||||
? "docker-compose.processed.yml"
|
|
||||||
: "docker-compose.yml"
|
|
||||||
: composeType === "stack"
|
|
||||||
? join(dirname(compose.composePath), "docker-compose.processed.yml")
|
|
||||||
: compose.composePath;
|
|
||||||
|
|
||||||
const baseCommand =
|
if (composeType === "stack") {
|
||||||
composeType === "docker-compose"
|
command = `stack deploy -c ${path} ${appName} --prune`;
|
||||||
? `compose -p ${appName} -f ${path} up -d --build --remove-orphans`
|
}
|
||||||
: `stack deploy -c ${path} ${appName} --prune`;
|
|
||||||
const customCommand = sanitizeCommand(compose.command);
|
return command;
|
||||||
return customCommand ? `${baseCommand} ${customCommand}` : baseCommand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createEnvFile = (compose: ComposeNested) => {
|
const createEnvFile = (compose: ComposeNested) => {
|
||||||
const { COMPOSE_PATH } = paths();
|
const { COMPOSE_PATH } = paths();
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
const composeFilePath =
|
const composeFilePath =
|
||||||
join(COMPOSE_PATH, appName, "code", composePath) ||
|
join(COMPOSE_PATH, appName, "code", composePath) ||
|
||||||
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
|
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
|
||||||
|
|
||||||
const envFilePath = join(dirname(composeFilePath), ".env");
|
const envFilePath = join(dirname(composeFilePath), ".env");
|
||||||
let envContent = env || "";
|
let envContent = env || "";
|
||||||
if (!envContent.includes("DOCKER_CONFIG")) {
|
if (!envContent.includes("DOCKER_CONFIG")) {
|
||||||
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compose.randomize) {
|
if (compose.randomize) {
|
||||||
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
|
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envFileContent = prepareEnvironmentVariables(
|
const envFileContent = prepareEnvironmentVariables(
|
||||||
envContent,
|
envContent,
|
||||||
compose.project.env,
|
compose.project.env
|
||||||
).join("\n");
|
).join("\n");
|
||||||
|
|
||||||
if (!existsSync(dirname(envFilePath))) {
|
if (!existsSync(dirname(envFilePath))) {
|
||||||
mkdirSync(dirname(envFilePath), { recursive: true });
|
mkdirSync(dirname(envFilePath), { recursive: true });
|
||||||
}
|
}
|
||||||
writeFileSync(envFilePath, envFileContent);
|
writeFileSync(envFilePath, envFileContent);
|
||||||
};
|
|
||||||
|
|
||||||
export const processComposeFile = async (compose: ComposeNested) => {
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
if (compose.composeType === "stack") {
|
|
||||||
const command = getProcessComposeFileCommand(compose);
|
|
||||||
await execAsync(command, {
|
|
||||||
cwd: join(COMPOSE_PATH, compose.appName, "code"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProcessComposeFileCommand = (compose: ComposeNested) => {
|
|
||||||
const { composeType, sourceType } = compose;
|
|
||||||
|
|
||||||
const composePath =
|
|
||||||
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
|
||||||
|
|
||||||
const destinationPath =
|
|
||||||
sourceType === "raw"
|
|
||||||
? "docker-compose.processed.yml"
|
|
||||||
: join(dirname(compose.composePath), "docker-compose.processed.yml");
|
|
||||||
|
|
||||||
let command = "";
|
|
||||||
if (composeType === "stack") {
|
|
||||||
command = [
|
|
||||||
"export $(grep -v '^#' .env | xargs)",
|
|
||||||
`docker stack config -c ${composePath} > ${destinationPath}`,
|
|
||||||
].join(" && ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return command;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
const composeFilePath =
|
const composeFilePath =
|
||||||
join(COMPOSE_PATH, appName, "code", composePath) ||
|
join(COMPOSE_PATH, appName, "code", composePath) ||
|
||||||
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
|
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
|
||||||
|
|
||||||
const envFilePath = join(dirname(composeFilePath), ".env");
|
const envFilePath = join(dirname(composeFilePath), ".env");
|
||||||
|
|
||||||
let envContent = env || "";
|
let envContent = env || "";
|
||||||
if (!envContent.includes("DOCKER_CONFIG")) {
|
if (!envContent.includes("DOCKER_CONFIG")) {
|
||||||
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compose.randomize) {
|
if (compose.randomize) {
|
||||||
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
|
envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const envFileContent = prepareEnvironmentVariables(
|
const envFileContent = prepareEnvironmentVariables(
|
||||||
envContent,
|
envContent,
|
||||||
compose.project.env,
|
compose.project.env
|
||||||
).join("\n");
|
).join("\n");
|
||||||
|
|
||||||
const encodedContent = encodeBase64(envFileContent);
|
const encodedContent = encodeBase64(envFileContent);
|
||||||
return `
|
return `
|
||||||
touch ${envFilePath};
|
touch ${envFilePath};
|
||||||
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getExportEnvCommand = (compose: ComposeNested) => {
|
||||||
|
if (compose.composeType !== "stack") return "";
|
||||||
|
|
||||||
|
const envVars = getEnviromentVariablesObject(compose.env, compose.project.env);
|
||||||
|
const exports = Object.entries(envVars)
|
||||||
|
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return exports ? `\n# Export environment variables\n${exports}\n` : "";
|
||||||
|
};
|
||||||
|
@ -15,526 +15,529 @@ import { spawnAsync } from "../process/spawnAsync";
|
|||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
|
|
||||||
interface RegistryAuth {
|
interface RegistryAuth {
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const pullImage = async (
|
export const pullImage = async (
|
||||||
dockerImage: string,
|
dockerImage: string,
|
||||||
onData?: (data: any) => void,
|
onData?: (data: any) => void,
|
||||||
authConfig?: Partial<RegistryAuth>,
|
authConfig?: Partial<RegistryAuth>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (!dockerImage) {
|
if (!dockerImage) {
|
||||||
throw new Error("Docker image not found");
|
throw new Error("Docker image not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authConfig?.username && authConfig?.password) {
|
if (authConfig?.username && authConfig?.password) {
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
"docker",
|
"docker",
|
||||||
[
|
[
|
||||||
"login",
|
"login",
|
||||||
authConfig.registryUrl || "",
|
authConfig.registryUrl || "",
|
||||||
"-u",
|
"-u",
|
||||||
authConfig.username,
|
authConfig.username,
|
||||||
"-p",
|
"-p",
|
||||||
authConfig.password,
|
authConfig.password,
|
||||||
],
|
],
|
||||||
onData,
|
onData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await spawnAsync("docker", ["pull", dockerImage], onData);
|
await spawnAsync("docker", ["pull", dockerImage], onData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const pullRemoteImage = async (
|
export const pullRemoteImage = async (
|
||||||
dockerImage: string,
|
dockerImage: string,
|
||||||
serverId: string,
|
serverId: string,
|
||||||
onData?: (data: any) => void,
|
onData?: (data: any) => void,
|
||||||
authConfig?: Partial<RegistryAuth>,
|
authConfig?: Partial<RegistryAuth>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (!dockerImage) {
|
if (!dockerImage) {
|
||||||
throw new Error("Docker image not found");
|
throw new Error("Docker image not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteDocker = await getRemoteDocker(serverId);
|
const remoteDocker = await getRemoteDocker(serverId);
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
remoteDocker.pull(
|
remoteDocker.pull(
|
||||||
dockerImage,
|
dockerImage,
|
||||||
{ authconfig: authConfig },
|
{ authconfig: authConfig },
|
||||||
(err, stream) => {
|
(err, stream) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteDocker.modem.followProgress(
|
remoteDocker.modem.followProgress(
|
||||||
stream as Readable,
|
stream as Readable,
|
||||||
(err: Error | null, res) => {
|
(err: Error | null, res) => {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
}
|
}
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(event) => {
|
(event) => {
|
||||||
onData?.(event);
|
onData?.(event);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const containerExists = async (containerName: string) => {
|
export const containerExists = async (containerName: string) => {
|
||||||
const container = docker.getContainer(containerName);
|
const container = docker.getContainer(containerName);
|
||||||
try {
|
try {
|
||||||
await container.inspect();
|
await container.inspect();
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stopService = async (appName: string) => {
|
export const stopService = async (appName: string) => {
|
||||||
try {
|
try {
|
||||||
await execAsync(`docker service scale ${appName}=0 `);
|
await execAsync(`docker service scale ${appName}=0 `);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const stopServiceRemote = async (serverId: string, appName: string) => {
|
export const stopServiceRemote = async (serverId: string, appName: string) => {
|
||||||
try {
|
try {
|
||||||
await execAsyncRemote(serverId, `docker service scale ${appName}=0 `);
|
await execAsyncRemote(serverId, `docker service scale ${appName}=0 `);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getContainerByName = (name: string): Promise<ContainerInfo> => {
|
export const getContainerByName = (name: string): Promise<ContainerInfo> => {
|
||||||
const opts = {
|
const opts = {
|
||||||
limit: 1,
|
limit: 1,
|
||||||
filters: {
|
filters: {
|
||||||
name: [name],
|
name: [name],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
docker.listContainers(opts, (err, containers) => {
|
docker.listContainers(opts, (err, containers) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else if (containers?.length === 0) {
|
} else if (containers?.length === 0) {
|
||||||
reject(new Error(`No container found with name: ${name}`));
|
reject(new Error(`No container found with name: ${name}`));
|
||||||
} else if (containers && containers?.length > 0 && containers[0]) {
|
} else if (containers && containers?.length > 0 && containers[0]) {
|
||||||
resolve(containers[0]);
|
resolve(containers[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const cleanUpUnusedImages = async (serverId?: string) => {
|
export const cleanUpUnusedImages = async (serverId?: string) => {
|
||||||
try {
|
try {
|
||||||
const command = "docker image prune --force";
|
const command = "docker image prune --force";
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanStoppedContainers = async (serverId?: string) => {
|
export const cleanStoppedContainers = async (serverId?: string) => {
|
||||||
try {
|
try {
|
||||||
const command = "docker container prune --force";
|
const command = "docker container prune --force";
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanUpUnusedVolumes = async (serverId?: string) => {
|
export const cleanUpUnusedVolumes = async (serverId?: string) => {
|
||||||
try {
|
try {
|
||||||
const command = "docker volume prune --force";
|
const command = "docker volume prune --force";
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanUpInactiveContainers = async () => {
|
export const cleanUpInactiveContainers = async () => {
|
||||||
try {
|
try {
|
||||||
const containers = await docker.listContainers({ all: true });
|
const containers = await docker.listContainers({ all: true });
|
||||||
const inactiveContainers = containers.filter(
|
const inactiveContainers = containers.filter(
|
||||||
(container) => container.State !== "running",
|
(container) => container.State !== "running"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const container of inactiveContainers) {
|
for (const container of inactiveContainers) {
|
||||||
await docker.getContainer(container.Id).remove({ force: true });
|
await docker.getContainer(container.Id).remove({ force: true });
|
||||||
console.log(`Cleaning up inactive container: ${container.Id}`);
|
console.log(`Cleaning up inactive container: ${container.Id}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error cleaning up inactive containers:", error);
|
console.error("Error cleaning up inactive containers:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanUpDockerBuilder = async (serverId?: string) => {
|
export const cleanUpDockerBuilder = async (serverId?: string) => {
|
||||||
const command = "docker builder prune --all --force";
|
const command = "docker builder prune --all --force";
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cleanUpSystemPrune = async (serverId?: string) => {
|
export const cleanUpSystemPrune = async (serverId?: string) => {
|
||||||
const command = "docker system prune --all --force --volumes";
|
const command = "docker system prune --all --force --volumes";
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const startService = async (appName: string) => {
|
export const startService = async (appName: string) => {
|
||||||
try {
|
try {
|
||||||
await execAsync(`docker service scale ${appName}=1 `);
|
await execAsync(`docker service scale ${appName}=1 `);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const startServiceRemote = async (serverId: string, appName: string) => {
|
export const startServiceRemote = async (serverId: string, appName: string) => {
|
||||||
try {
|
try {
|
||||||
await execAsyncRemote(serverId, `docker service scale ${appName}=1 `);
|
await execAsyncRemote(serverId, `docker service scale ${appName}=1 `);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeService = async (
|
export const removeService = async (
|
||||||
appName: string,
|
appName: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
deleteVolumes = false,
|
deleteVolumes = false
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const command = `docker service rm ${appName}`;
|
const command = `docker service rm ${appName}`;
|
||||||
|
|
||||||
if (serverId) {
|
if (serverId) {
|
||||||
await execAsyncRemote(serverId, command);
|
await execAsyncRemote(serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await execAsync(command);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prepareEnvironmentVariables = (
|
export const prepareEnvironmentVariables = (
|
||||||
serviceEnv: string | null,
|
serviceEnv: string | null,
|
||||||
projectEnv?: string | null,
|
projectEnv?: string | null
|
||||||
) => {
|
) => {
|
||||||
const projectVars = parse(projectEnv ?? "");
|
const projectVars = parse(projectEnv ?? "");
|
||||||
const serviceVars = parse(serviceEnv ?? "");
|
const serviceVars = parse(serviceEnv ?? "");
|
||||||
|
|
||||||
const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
|
const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
|
||||||
let resolvedValue = value;
|
let resolvedValue = value;
|
||||||
if (projectVars) {
|
if (projectVars) {
|
||||||
resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
|
resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
|
||||||
if (projectVars[ref] !== undefined) {
|
if (projectVars[ref] !== undefined) {
|
||||||
return projectVars[ref];
|
return projectVars[ref];
|
||||||
}
|
}
|
||||||
throw new Error(`Invalid project environment variable: project.${ref}`);
|
throw new Error(`Invalid project environment variable: project.${ref}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return `${key}=${resolvedValue}`;
|
return `${key}=${resolvedValue}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return resolvedVars;
|
return resolvedVars;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prepareBuildArgs = (input: string | null) => {
|
export const getEnviromentVariablesObject = (
|
||||||
const pairs = (input ?? "").split("\n");
|
input: string | null,
|
||||||
|
projectEnv?: string | null
|
||||||
|
) => {
|
||||||
|
const envs = prepareEnvironmentVariables(input, projectEnv);
|
||||||
|
|
||||||
const jsonObject: Record<string, string> = {};
|
const jsonObject: Record<string, string> = {};
|
||||||
|
|
||||||
for (const pair of pairs) {
|
for (const pair of envs) {
|
||||||
const [key, value] = pair.split("=");
|
const [key, value] = pair.split("=");
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
jsonObject[key] = value;
|
jsonObject[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateVolumeMounts = (mounts: ApplicationNested["mounts"]) => {
|
export const generateVolumeMounts = (mounts: ApplicationNested["mounts"]) => {
|
||||||
if (!mounts || mounts.length === 0) {
|
if (!mounts || mounts.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mounts
|
return mounts
|
||||||
.filter((mount) => mount.type === "volume")
|
.filter((mount) => mount.type === "volume")
|
||||||
.map((mount) => ({
|
.map((mount) => ({
|
||||||
Type: "volume" as const,
|
Type: "volume" as const,
|
||||||
Source: mount.volumeName || "",
|
Source: mount.volumeName || "",
|
||||||
Target: mount.mountPath,
|
Target: mount.mountPath,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
type Resources = {
|
type Resources = {
|
||||||
memoryLimit: string | null;
|
memoryLimit: string | null;
|
||||||
memoryReservation: string | null;
|
memoryReservation: string | null;
|
||||||
cpuLimit: string | null;
|
cpuLimit: string | null;
|
||||||
cpuReservation: string | null;
|
cpuReservation: string | null;
|
||||||
};
|
};
|
||||||
export const calculateResources = ({
|
export const calculateResources = ({
|
||||||
memoryLimit,
|
memoryLimit,
|
||||||
memoryReservation,
|
memoryReservation,
|
||||||
cpuLimit,
|
cpuLimit,
|
||||||
cpuReservation,
|
cpuReservation,
|
||||||
}: Resources): ResourceRequirements => {
|
}: Resources): ResourceRequirements => {
|
||||||
return {
|
return {
|
||||||
Limits: {
|
Limits: {
|
||||||
MemoryBytes: memoryLimit ? Number.parseInt(memoryLimit) : undefined,
|
MemoryBytes: memoryLimit ? Number.parseInt(memoryLimit) : undefined,
|
||||||
NanoCPUs: cpuLimit ? Number.parseInt(cpuLimit) : undefined,
|
NanoCPUs: cpuLimit ? Number.parseInt(cpuLimit) : undefined,
|
||||||
},
|
},
|
||||||
Reservations: {
|
Reservations: {
|
||||||
MemoryBytes: memoryReservation
|
MemoryBytes: memoryReservation
|
||||||
? Number.parseInt(memoryReservation)
|
? Number.parseInt(memoryReservation)
|
||||||
: undefined,
|
: undefined,
|
||||||
NanoCPUs: cpuReservation ? Number.parseInt(cpuReservation) : undefined,
|
NanoCPUs: cpuReservation ? Number.parseInt(cpuReservation) : undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateConfigContainer = (application: ApplicationNested) => {
|
export const generateConfigContainer = (application: ApplicationNested) => {
|
||||||
const {
|
const {
|
||||||
healthCheckSwarm,
|
healthCheckSwarm,
|
||||||
restartPolicySwarm,
|
restartPolicySwarm,
|
||||||
placementSwarm,
|
placementSwarm,
|
||||||
updateConfigSwarm,
|
updateConfigSwarm,
|
||||||
rollbackConfigSwarm,
|
rollbackConfigSwarm,
|
||||||
modeSwarm,
|
modeSwarm,
|
||||||
labelsSwarm,
|
labelsSwarm,
|
||||||
replicas,
|
replicas,
|
||||||
mounts,
|
mounts,
|
||||||
networkSwarm,
|
networkSwarm,
|
||||||
} = application;
|
} = application;
|
||||||
|
|
||||||
const haveMounts = mounts.length > 0;
|
const haveMounts = mounts.length > 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(healthCheckSwarm && {
|
...(healthCheckSwarm && {
|
||||||
HealthCheck: healthCheckSwarm,
|
HealthCheck: healthCheckSwarm,
|
||||||
}),
|
}),
|
||||||
...(restartPolicySwarm
|
...(restartPolicySwarm
|
||||||
? {
|
? {
|
||||||
RestartPolicy: restartPolicySwarm,
|
RestartPolicy: restartPolicySwarm,
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(placementSwarm
|
...(placementSwarm
|
||||||
? {
|
? {
|
||||||
Placement: placementSwarm,
|
Placement: placementSwarm,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
// if app have mounts keep manager as constraint
|
// if app have mounts keep manager as constraint
|
||||||
Placement: {
|
Placement: {
|
||||||
Constraints: haveMounts ? ["node.role==manager"] : [],
|
Constraints: haveMounts ? ["node.role==manager"] : [],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(labelsSwarm && {
|
...(labelsSwarm && {
|
||||||
Labels: labelsSwarm,
|
Labels: labelsSwarm,
|
||||||
}),
|
}),
|
||||||
...(modeSwarm
|
...(modeSwarm
|
||||||
? {
|
? {
|
||||||
Mode: modeSwarm,
|
Mode: modeSwarm,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
// use replicas value if no modeSwarm provided
|
// use replicas value if no modeSwarm provided
|
||||||
Mode: {
|
Mode: {
|
||||||
Replicated: {
|
Replicated: {
|
||||||
Replicas: replicas,
|
Replicas: replicas,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(rollbackConfigSwarm && {
|
...(rollbackConfigSwarm && {
|
||||||
RollbackConfig: rollbackConfigSwarm,
|
RollbackConfig: rollbackConfigSwarm,
|
||||||
}),
|
}),
|
||||||
...(updateConfigSwarm
|
...(updateConfigSwarm
|
||||||
? { UpdateConfig: updateConfigSwarm }
|
? { UpdateConfig: updateConfigSwarm }
|
||||||
: {
|
: {
|
||||||
// default config if no updateConfigSwarm provided
|
// default config if no updateConfigSwarm provided
|
||||||
UpdateConfig: {
|
UpdateConfig: {
|
||||||
Parallelism: 1,
|
Parallelism: 1,
|
||||||
Order: "start-first",
|
Order: "start-first",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
...(networkSwarm
|
...(networkSwarm
|
||||||
? {
|
? {
|
||||||
Networks: networkSwarm,
|
Networks: networkSwarm,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
Networks: [{ Target: "dokploy-network" }],
|
Networks: [{ Target: "dokploy-network" }],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => {
|
export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => {
|
||||||
if (!mounts || mounts.length === 0) {
|
if (!mounts || mounts.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mounts
|
return mounts
|
||||||
.filter((mount) => mount.type === "bind")
|
.filter((mount) => mount.type === "bind")
|
||||||
.map((mount) => ({
|
.map((mount) => ({
|
||||||
Type: "bind" as const,
|
Type: "bind" as const,
|
||||||
Source: mount.hostPath || "",
|
Source: mount.hostPath || "",
|
||||||
Target: mount.mountPath,
|
Target: mount.mountPath,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateFileMounts = (
|
export const generateFileMounts = (
|
||||||
appName: string,
|
appName: string,
|
||||||
service:
|
service:
|
||||||
| ApplicationNested
|
| ApplicationNested
|
||||||
| MongoNested
|
| MongoNested
|
||||||
| MariadbNested
|
| MariadbNested
|
||||||
| MysqlNested
|
| MysqlNested
|
||||||
| PostgresNested
|
| PostgresNested
|
||||||
| RedisNested,
|
| RedisNested
|
||||||
) => {
|
) => {
|
||||||
const { mounts } = service;
|
const { mounts } = service;
|
||||||
const { APPLICATIONS_PATH } = paths(!!service.serverId);
|
const { APPLICATIONS_PATH } = paths(!!service.serverId);
|
||||||
if (!mounts || mounts.length === 0) {
|
if (!mounts || mounts.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return mounts
|
return mounts
|
||||||
.filter((mount) => mount.type === "file")
|
.filter((mount) => mount.type === "file")
|
||||||
.map((mount) => {
|
.map((mount) => {
|
||||||
const fileName = mount.filePath;
|
const fileName = mount.filePath;
|
||||||
const absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
const absoluteBasePath = path.resolve(APPLICATIONS_PATH);
|
||||||
const directory = path.join(absoluteBasePath, appName, "files");
|
const directory = path.join(absoluteBasePath, appName, "files");
|
||||||
const sourcePath = path.join(directory, fileName || "");
|
const sourcePath = path.join(directory, fileName || "");
|
||||||
return {
|
return {
|
||||||
Type: "bind" as const,
|
Type: "bind" as const,
|
||||||
Source: sourcePath,
|
Source: sourcePath,
|
||||||
Target: mount.mountPath,
|
Target: mount.mountPath,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createFile = async (
|
export const createFile = async (
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
content: string,
|
content: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const fullPath = path.join(outputPath, filePath);
|
const fullPath = path.join(outputPath, filePath);
|
||||||
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
||||||
fs.mkdirSync(fullPath, { recursive: true });
|
fs.mkdirSync(fullPath, { recursive: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const directory = path.dirname(fullPath);
|
const directory = path.dirname(fullPath);
|
||||||
fs.mkdirSync(directory, { recursive: true });
|
fs.mkdirSync(directory, { recursive: true });
|
||||||
fs.writeFileSync(fullPath, content || "");
|
fs.writeFileSync(fullPath, content || "");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const encodeBase64 = (content: string) =>
|
export const encodeBase64 = (content: string) =>
|
||||||
Buffer.from(content, "utf-8").toString("base64");
|
Buffer.from(content, "utf-8").toString("base64");
|
||||||
|
|
||||||
export const getCreateFileCommand = (
|
export const getCreateFileCommand = (
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
content: string,
|
content: string
|
||||||
) => {
|
) => {
|
||||||
const fullPath = path.join(outputPath, filePath);
|
const fullPath = path.join(outputPath, filePath);
|
||||||
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
||||||
return `mkdir -p ${fullPath};`;
|
return `mkdir -p ${fullPath};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const directory = path.dirname(fullPath);
|
const directory = path.dirname(fullPath);
|
||||||
const encodedContent = encodeBase64(content);
|
const encodedContent = encodeBase64(content);
|
||||||
return `
|
return `
|
||||||
mkdir -p ${directory};
|
mkdir -p ${directory};
|
||||||
echo "${encodedContent}" | base64 -d > "${fullPath}";
|
echo "${encodedContent}" | base64 -d > "${fullPath}";
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getServiceContainer = async (appName: string) => {
|
export const getServiceContainer = async (appName: string) => {
|
||||||
try {
|
try {
|
||||||
const filter = {
|
const filter = {
|
||||||
status: ["running"],
|
status: ["running"],
|
||||||
label: [`com.docker.swarm.service.name=${appName}`],
|
label: [`com.docker.swarm.service.name=${appName}`],
|
||||||
};
|
};
|
||||||
|
|
||||||
const containers = await docker.listContainers({
|
const containers = await docker.listContainers({
|
||||||
filters: JSON.stringify(filter),
|
filters: JSON.stringify(filter),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (containers.length === 0 || !containers[0]) {
|
if (containers.length === 0 || !containers[0]) {
|
||||||
throw new Error(`No container found with name: ${appName}`);
|
throw new Error(`No container found with name: ${appName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = containers[0];
|
const container = containers[0];
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRemoteServiceContainer = async (
|
export const getRemoteServiceContainer = async (
|
||||||
serverId: string,
|
serverId: string,
|
||||||
appName: string,
|
appName: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const filter = {
|
const filter = {
|
||||||
status: ["running"],
|
status: ["running"],
|
||||||
label: [`com.docker.swarm.service.name=${appName}`],
|
label: [`com.docker.swarm.service.name=${appName}`],
|
||||||
};
|
};
|
||||||
const remoteDocker = await getRemoteDocker(serverId);
|
const remoteDocker = await getRemoteDocker(serverId);
|
||||||
const containers = await remoteDocker.listContainers({
|
const containers = await remoteDocker.listContainers({
|
||||||
filters: JSON.stringify(filter),
|
filters: JSON.stringify(filter),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (containers.length === 0 || !containers[0]) {
|
if (containers.length === 0 || !containers[0]) {
|
||||||
throw new Error(`No container found with name: ${appName}`);
|
throw new Error(`No container found with name: ${appName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = containers[0];
|
const container = containers[0];
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user